1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012 Milan Jurik. All rights reserved.
25 */
26
27 #define __STANDALONE_MODULE__
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <stdlib.h>
32 #include <libintl.h>
33 #include <string.h>
34 #include <ctype.h>
35
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <syslog.h>
40 #include <locale.h>
41 #include <errno.h>
42 #include <sys/time.h>
43
44 #include <arpa/inet.h>
45 #include <netdb.h>
46 #include <strings.h>
47
48 #include <thread.h>
49
50 #include <nsswitch.h>
51 #include <nss_dbdefs.h>
52 #include <nss.h>
53
54 #include "ns_cache_door.h"
55 #include "ns_internal.h"
56 #include "ns_connmgmt.h"
57
58 typedef enum {
59 INFO_SERVER_JUST_INITED = -1,
60 INFO_SERVER_UNKNOWN = 0,
61 INFO_SERVER_CONNECTING = 1,
62 INFO_SERVER_UP = 2,
63 INFO_SERVER_ERROR = 3,
64 INFO_SERVER_REMOVED = 4
65 } dir_server_status_t;
66
67 typedef enum {
68 INFO_STATUS_NEW = 2,
69 INFO_STATUS_OLD = 3
70 } dir_server_info_t;
71
72 typedef struct dir_server {
73 char *ip;
74 char **controls;
75 char **saslMech;
76 dir_server_status_t status;
77 mutex_t updateStatus;
78 dir_server_info_t info;
79 } dir_server_t;
80
81 typedef struct dir_server_list {
82 dir_server_t **nsServers;
83
84 rwlock_t listDestroyLock;
85 } dir_server_list_t;
86
87 struct {
88 /* The local list of the directory servers' root DSEs. */
89 dir_server_list_t *list;
90 /* The flag indicating if libsldap is in the 'Standalone' mode. */
91 int standalone;
92 /*
93 * The mutex ensuring that only one thread performs
94 * the initialization of the list.
95 */
96 mutex_t listReplaceLock;
97 /*
98 * A flag indicating that a particular thread is
99 * in the 'ldap_cachemgr' mode. It is stored by thread as
100 * a thread specific data.
101 */
102 const int initFlag;
103 /*
104 * A thread specific key storing
105 * the the 'ldap_cachemgr' mode indicator.
106 */
107 thread_key_t standaloneInitKey;
108 } dir_servers = {NULL, 0, DEFAULTMUTEX, '1'};
109
110 typedef struct switchDatabase {
111 char *conf;
112 uint32_t alloced;
113 } switch_database_t;
114
115 static thread_key_t switchConfigKey;
116
117 #pragma init(createStandaloneKey)
118
119 #define DONT_INCLUDE_ATTR_NAMES 0
120 #define INCLUDE_ATTR_NAMES 1
121 #define IS_PROFILE 1
122 #define NOT_PROFILE 0
123 /* INET6_ADDRSTRLEN + ":" + <5-digit port> + some round-up */
124 #define MAX_HOSTADDR_LEN (INET6_ADDRSTRLEN + 6 + 12)
125
126 static
127 void
128 switch_conf_disposer(void *data)
129 {
130 switch_database_t *localData = (switch_database_t *)data;
131
132 free(localData->conf);
133 free(localData);
134 }
135
136 /*
137 * This function initializes an indication that a thread obtaining a root DSE
138 * will be switched to the 'ldap_cachemgr' mode. Within the thread libsldap
139 * will not invoke the __s_api_requestServer function. Instead, the library
140 * will establish a connection to the server specified by
141 * the __ns_ldap_getRootDSE function.
142 * Since ldap_cachmgr can obtain a DUAProfile and root DSEs at the same time
143 * and we do not want to affect a thread obtaining a DUAProfile,
144 * the 'ldap_cachemgr' mode is thread private.
145 * In addition, this function creates a key holding temporary configuration
146 * for the "hosts" and "ipnodes" databases which is used by the "SKIPDB"
147 * mechanism (__s_api_ip2hostname() & _s_api_hostname2ip()).
148 */
149 static
150 void
151 createStandaloneKey()
152 {
153 if (thr_keycreate(&dir_servers.standaloneInitKey, NULL) != 0) {
154 syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
155 "key needed for sharing ldap connections"));
156 }
157 if (thr_keycreate(&switchConfigKey, switch_conf_disposer) != 0) {
158 syslog(LOG_ERR, gettext("libsldap: unable to create a thread "
159 "key containing current nsswitch configuration"));
160 }
161 }
162
163 /*
164 * This function sets the 'ldap_cachemgr' mode indication.
165 */
166 void
167 __s_api_setInitMode()
168 {
169 (void) thr_setspecific(dir_servers.standaloneInitKey,
170 (void *) &dir_servers.initFlag);
171 }
172
173 /*
174 * This function unset the 'ldap_cachemgr' mode indication.
175 */
176 void
177 __s_api_unsetInitMode()
178 {
179 (void) thr_setspecific(dir_servers.standaloneInitKey, NULL);
180 }
181
182 /*
183 * This function checks if the 'ldap_cachemgr' mode indication is set.
184 */
185 int
186 __s_api_isInitializing() {
187 int *flag = NULL;
188
189 (void) thr_getspecific(dir_servers.standaloneInitKey, (void **) &flag);
190
191 return (flag != NULL && *flag == dir_servers.initFlag);
192 }
193
194 /*
195 * This function checks if the process runs in the 'Standalone' mode.
196 * In this mode libsldap will check the local, process private list of root DSEs
197 * instead of requesting them via a door call to ldap_cachemgr.
198 */
199 int
200 __s_api_isStandalone()
201 {
202 int mode;
203
204 (void) mutex_lock(&dir_servers.listReplaceLock);
205 mode = dir_servers.standalone;
206 (void) mutex_unlock(&dir_servers.listReplaceLock);
207
208 return (mode);
209 }
210
211
212 static
213 int
214 remove_ldap(char *dst, char *src, int dst_buf_len)
215 {
216 int i = 0;
217
218 if (strlen(src) >= dst_buf_len)
219 return (0);
220
221 while (*src != '\0') {
222 /* Copy up to one space from source. */
223 if (isspace(*src)) {
224 dst[i++] = *src;
225 while (isspace(*src))
226 src++;
227 }
228
229 /* If not "ldap", just copy. */
230 if (strncmp(src, "ldap", 4) != 0) {
231 while (!isspace(*src)) {
232 dst[i++] = *src++;
233 /* At the end of string? */
234 if (dst[i-1] == '\0')
235 return (1);
236 }
237 /* Copy up to one space from source. */
238 if (isspace(*src)) {
239 dst[i++] = *src;
240 while (isspace(*src))
241 src++;
242 }
243 /* Copy also the criteria section */
244 if (*src == '[')
245 while (*src != ']') {
246 dst[i++] = *src++;
247 /* Shouln't happen if format is right */
248 if (dst[i-1] == '\0')
249 return (1);
250 }
251 }
252
253 /* If next part is ldap, skip over it ... */
254 if (strncmp(src, "ldap", 4) == 0) {
255 if (isspace(*(src+4)) || *(src+4) == '\0') {
256 src += 4;
257 while (isspace(*src))
258 src++;
259 if (*src == '[') {
260 while (*src++ != ']') {
261 /*
262 * See comment above about
263 * correct format.
264 */
265 if (*src == '\0') {
266 dst[i++] = '\0';
267 return (1);
268 }
269 }
270 }
271 while (isspace(*src))
272 src++;
273 }
274 }
275 if (*src == '\0')
276 dst[i++] = '\0';
277 }
278
279 return (1);
280 }
281
282 static
283 char *
284 get_db(const char *db_name)
285 {
286 char *ptr;
287 switch_database_t *hostService = NULL;
288 FILE *fp = fopen(__NSW_CONFIG_FILE, "rF");
289 char *linep, line[NSS_BUFSIZ];
290
291 if (fp == NULL) {
292 syslog(LOG_WARNING, gettext("libsldap: can not read %s"),
293 __NSW_CONFIG_FILE);
294 return (NULL);
295 }
296
297 while ((linep = fgets(line, NSS_BUFSIZ, fp)) != NULL) {
298 while (isspace(*linep)) {
299 ++linep;
300 }
301 if (*linep == '#') {
302 continue;
303 }
304 if (strncmp(linep, db_name, strlen(db_name)) != 0) {
305 continue;
306 }
307 if ((linep = strchr(linep, ':')) != NULL) {
308 if (linep[strlen(linep) - 1] == '\n') {
309 linep[strlen(linep) - 1] = '\0';
310 }
311
312 while (isspace(*++linep))
313 ;
314
315 if ((ptr = strchr(linep, '#')) != NULL) {
316 while (--ptr >= linep && isspace(*ptr))
317 ;
318 *(ptr + 1) = '\0';
319 }
320
321 if (strlen(linep) == 0) {
322 continue;
323 }
324 break;
325 }
326 }
327
328 (void) fclose(fp);
329
330 if (linep == NULL) {
331 syslog(LOG_WARNING,
332 gettext("libsldap: the %s database "
333 "is missing from %s"),
334 db_name,
335 __NSW_CONFIG_FILE);
336 return (NULL);
337 }
338
339 (void) thr_getspecific(switchConfigKey, (void **) &hostService);
340 if (hostService == NULL) {
341 hostService = calloc(1, sizeof (switch_database_t));
342 if (hostService == NULL) {
343 return (NULL);
344 }
345 (void) thr_setspecific(switchConfigKey, hostService);
346 }
347
348 /*
349 * In a long-living process threads can perform several
350 * getXbyY requests. And the windows between those requests
351 * can be long. The nsswitch configuration can change from time
352 * to time. So instead of allocating/freeing memory every time
353 * the API is called, reallocate memory only when the current
354 * configuration for the database being used is longer than
355 * the previous one.
356 */
357 if (strlen(linep) >= hostService->alloced) {
358 ptr = (char *)realloc((void *)hostService->conf,
359 strlen(linep) + 1);
360 if (ptr == NULL) {
361 free((void *)hostService->conf);
362 hostService->conf = NULL;
363 hostService->alloced = 0;
364 return (NULL);
365 }
366 bzero(ptr, strlen(linep) + 1);
367 hostService->conf = ptr;
368 hostService->alloced = strlen(linep) + 1;
369 }
370
371 if (remove_ldap(hostService->conf, linep, hostService->alloced))
372 return (hostService->conf);
373 else
374 return (NULL);
375 }
376
377 static
378 void
379 _initf_ipnodes(nss_db_params_t *p)
380 {
381 char *services = get_db("ipnodes");
382
383 p->name = NSS_DBNAM_IPNODES;
384 p->flags |= NSS_USE_DEFAULT_CONFIG;
385 p->default_config = services == NULL ? "" : services;
386 }
387
388 static
389 void
390 _initf_hosts(nss_db_params_t *p)
391 {
392 char *services = get_db("hosts");
393
394 p->name = NSS_DBNAM_HOSTS;
395 p->flags |= NSS_USE_DEFAULT_CONFIG;
396 p->default_config = services == NULL ? "" : services;
397 }
398
399 /*
400 * This function is an analog of the standard gethostbyaddr_r()
401 * function with an exception that it removes the 'ldap' back-end
402 * (if any) from the host/ipnodes nsswitch's databases and then
403 * looks up using remaining back-ends.
404 */
405 static
406 struct hostent *
407 _filter_gethostbyaddr_r(const char *addr, int len, int type,
408 struct hostent *result, char *buffer, int buflen,
409 int *h_errnop)
410 {
411 DEFINE_NSS_DB_ROOT(db_root_hosts);
412 DEFINE_NSS_DB_ROOT(db_root_ipnodes);
413 nss_XbyY_args_t arg;
414 nss_status_t res;
415 int (*str2ent)();
416 void (*nss_initf)();
417 nss_db_root_t *nss_db_root;
418 int dbop;
419
420 switch (type) {
421 case AF_INET:
422 str2ent = str2hostent;
423 nss_initf = _initf_hosts;
424 nss_db_root = &db_root_hosts;
425 dbop = NSS_DBOP_HOSTS_BYADDR;
426 break;
427 case AF_INET6:
428 str2ent = str2hostent6;
429 nss_initf = _initf_ipnodes;
430 nss_db_root = &db_root_ipnodes;
431 dbop = NSS_DBOP_IPNODES_BYADDR;
432 default:
433 return (NULL);
434 }
435
436 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2ent);
437
438 arg.key.hostaddr.addr = addr;
439 arg.key.hostaddr.len = len;
440 arg.key.hostaddr.type = type;
441 arg.stayopen = 0;
442 arg.h_errno = NETDB_SUCCESS;
443
444 res = nss_search(nss_db_root, nss_initf, dbop, &arg);
445 arg.status = res;
446 *h_errnop = arg.h_errno;
447 return (struct hostent *)NSS_XbyY_FINI(&arg);
448 }
449
450 /*
451 * This routine is an analog of gethostbyaddr_r().
452 * But in addition __s_api_hostname2ip() performs the "LDAP SKIPDB" activity
453 * prior to querying the name services.
454 * If the buffer is not big enough to accommodate a returning data,
455 * NULL is returned and h_errnop is set to TRY_AGAIN.
456 */
457 struct hostent *
458 __s_api_hostname2ip(const char *name,
459 struct hostent *result, char *buffer, int buflen,
460 int *h_errnop)
461 {
462 DEFINE_NSS_DB_ROOT(db_root_ipnodes);
463 DEFINE_NSS_DB_ROOT(db_root_hosts);
464 nss_XbyY_args_t arg;
465 nss_status_t res;
466 struct in_addr addr;
467 struct in6_addr addr6;
468
469 if (inet_pton(AF_INET, name, &addr) > 0) {
470 if (buflen < strlen(name) + 1 +
471 sizeof (char *) * 2 + /* The h_aliases member */
472 sizeof (struct in_addr) +
473 sizeof (struct in_addr *) * 2) {
474 *h_errnop = TRY_AGAIN;
475 return (NULL);
476 }
477
478 result->h_addrtype = AF_INET;
479 result->h_length = sizeof (struct in_addr);
480 (void) strncpy(buffer, name, buflen);
481
482 result->h_addr_list = (char **)ROUND_UP(
483 buffer + strlen(name) + 1,
484 sizeof (char *));
485 result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
486 sizeof (char *));
487 result->h_aliases[0] = buffer;
488 result->h_aliases[1] = NULL;
489 bcopy(&addr,
490 buffer + buflen - sizeof (struct in_addr),
491 sizeof (struct in_addr));
492 result->h_addr_list[0] = buffer + buflen -
493 sizeof (struct in_addr);
494 result->h_addr_list[1] = NULL;
495 result->h_aliases = result->h_addr_list;
496 result->h_name = buffer;
497
498 *h_errnop = NETDB_SUCCESS;
499 return (result);
500 }
501 if (inet_pton(AF_INET6, name, &addr6) > 0) {
502 if (buflen < strlen(name) + 1 +
503 sizeof (char *) * 2 + /* The h_aliases member */
504 sizeof (struct in6_addr) +
505 sizeof (struct in6_addr *) * 2) {
506 *h_errnop = TRY_AGAIN;
507 return (NULL);
508 }
509
510 result->h_addrtype = AF_INET6;
511 result->h_length = sizeof (struct in6_addr);
512 (void) strncpy(buffer, name, buflen);
513
514 result->h_addr_list = (char **)ROUND_UP(
515 buffer + strlen(name) + 1,
516 sizeof (char *));
517 result->h_aliases = (char **)ROUND_UP(result->h_addr_list,
518 sizeof (char *));
519 result->h_aliases[0] = buffer;
520 result->h_aliases[1] = NULL;
521 bcopy(&addr6,
522 buffer + buflen - sizeof (struct in6_addr),
523 sizeof (struct in6_addr));
524 result->h_addr_list[0] = buffer + buflen -
525 sizeof (struct in6_addr);
526 result->h_addr_list[1] = NULL;
527 result->h_aliases = result->h_addr_list;
528 result->h_name = buffer;
529
530 *h_errnop = NETDB_SUCCESS;
531 return (result);
532 }
533
534 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2hostent);
535
536 arg.key.name = name;
537 arg.stayopen = 0;
538 arg.h_errno = NETDB_SUCCESS;
539
540 res = nss_search(&db_root_ipnodes, _initf_ipnodes,
541 NSS_DBOP_IPNODES_BYNAME, &arg);
542 if (res == NSS_NOTFOUND || res == NSS_UNAVAIL) {
543 arg.h_errno = NETDB_SUCCESS;
544 res = nss_search(&db_root_hosts, _initf_hosts,
545 NSS_DBOP_HOSTS_BYNAME, &arg);
546 }
547 arg.status = res;
548 *h_errnop = arg.h_errno;
549 return ((struct hostent *)NSS_XbyY_FINI(&arg));
550 }
551
552 /*
553 * Convert an IP to a host name.
554 */
555 ns_ldap_return_code
556 __s_api_ip2hostname(char *ipaddr, char **hostname) {
557 struct in_addr in;
558 struct in6_addr in6;
559 struct hostent *hp = NULL, hostEnt;
560 char buffer[NSS_BUFLEN_HOSTS];
561 int buflen = NSS_BUFLEN_HOSTS;
562 char *start = NULL,
563 *end = NULL,
564 delim = '\0';
565 char *port = NULL,
566 *addr = NULL;
567 int errorNum = 0,
568 len = 0;
569
570 if (ipaddr == NULL || hostname == NULL)
571 return (NS_LDAP_INVALID_PARAM);
572 *hostname = NULL;
573 if ((addr = strdup(ipaddr)) == NULL)
574 return (NS_LDAP_MEMORY);
575
576 if (addr[0] == '[') {
577 /*
578 * Assume it's [ipv6]:port
579 * Extract ipv6 IP
580 */
581 start = &addr[1];
582 if ((end = strchr(addr, ']')) != NULL) {
583 *end = '\0';
584 delim = ']';
585 if (*(end + 1) == ':')
586 /* extract port */
587 port = end + 2;
588 } else {
589 free(addr);
590 return (NS_LDAP_INVALID_PARAM);
591 }
592 } else if ((end = strchr(addr, ':')) != NULL) {
593 /* assume it's ipv4:port */
594 *end = '\0';
595 delim = ':';
596 start = addr;
597 port = end + 1;
598 } else
599 /* No port */
600 start = addr;
601
602
603 if (inet_pton(AF_INET, start, &in) == 1) {
604 /* IPv4 */
605 hp = _filter_gethostbyaddr_r((char *)&in,
606 sizeof (in.s_addr),
607 AF_INET,
608 &hostEnt,
609 buffer,
610 buflen,
611 &errorNum);
612 if (hp && hp->h_name) {
613 /* hostname + '\0' */
614 len = strlen(hp->h_name) + 1;
615 if (port)
616 /* ':' + port */
617 len += strlen(port) + 1;
618 if ((*hostname = malloc(len)) == NULL) {
619 free(addr);
620 return (NS_LDAP_MEMORY);
621 }
622
623 if (port)
624 (void) snprintf(*hostname, len, "%s:%s",
625 hp->h_name, port);
626 else
627 (void) strlcpy(*hostname, hp->h_name, len);
628
629 free(addr);
630 return (NS_LDAP_SUCCESS);
631 } else {
632 free(addr);
633 return (NS_LDAP_NOTFOUND);
634 }
635 } else if (inet_pton(AF_INET6, start, &in6) == 1) {
636 /* IPv6 */
637 hp = _filter_gethostbyaddr_r((char *)&in6,
638 sizeof (in6.s6_addr),
639 AF_INET6,
640 &hostEnt,
641 buffer,
642 buflen,
643 &errorNum);
644 if (hp && hp->h_name) {
645 /* hostname + '\0' */
646 len = strlen(hp->h_name) + 1;
647 if (port)
648 /* ':' + port */
649 len += strlen(port) + 1;
650 if ((*hostname = malloc(len)) == NULL) {
651 free(addr);
652 return (NS_LDAP_MEMORY);
653 }
654
655 if (port)
656 (void) snprintf(*hostname, len, "%s:%s",
657 hp->h_name, port);
658 else
659 (void) strlcpy(*hostname, hp->h_name, len);
660
661 free(addr);
662 return (NS_LDAP_SUCCESS);
663 } else {
664 free(addr);
665 return (NS_LDAP_NOTFOUND);
666 }
667 } else {
668 /*
669 * A hostname
670 * Return it as is
671 */
672 if (end)
673 *end = delim;
674 *hostname = addr;
675 return (NS_LDAP_SUCCESS);
676 }
677 }
678
679 /*
680 * This function obtains data returned by an LDAP search request and puts it
681 * in a string in the ldap_cachmgr(1) door call format.
682 *
683 * INPUT:
684 * ld - a pointer to an LDAP structure used for a search operation,
685 * result_msg - a pointer to an LDAPMessage returned by the search,
686 * include_names - if set to INCLUDE_ATTR_NAMES, the output buffer will
687 * contain attribute names.
688 * Otherwise, only values will be return.
689 *
690 * OUTPUT:
691 * a buffer containing server info in the following format:
692 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
693 * Should be free'ed by the caller.
694 */
695 static
696 ns_ldap_return_code
697 convert_to_door_line(LDAP* ld,
698 LDAPMessage *result_msg,
699 int include_names,
700 int is_profile,
701 char **door_line)
702 {
703 uint32_t total_length = 0, attr_len = 0, i;
704 LDAPMessage *e;
705 char *a, **vals;
706 BerElement *ber;
707 int seen_objectclass = 0, rewind = 0;
708
709 if (!door_line) {
710 return (NS_LDAP_INVALID_PARAM);
711 }
712 *door_line = NULL;
713
714 if ((e = ldap_first_entry(ld, result_msg)) == NULL) {
715 return (NS_LDAP_NOTFOUND);
716 }
717
718 /* calculate length of received data */
719 for (a = ldap_first_attribute(ld, e, &ber);
720 a != NULL;
721 a = ldap_next_attribute(ld, e, ber)) {
722
723 if ((vals = ldap_get_values(ld, e, a)) != NULL) {
724 for (i = 0; vals[i] != NULL; i++) {
725 total_length += (include_names ?
726 strlen(a) : 0) +
727 strlen(vals[i]) +
728 strlen(DOORLINESEP) +1;
729 }
730 ldap_value_free(vals);
731 }
732 ldap_memfree(a);
733 }
734 if (ber != NULL) {
735 ber_free(ber, 0);
736 }
737
738 if (total_length == 0) {
739 return (NS_LDAP_NOTFOUND);
740 }
741
742 /* copy the data */
743 /* add 1 for the last '\0' */
744 *door_line = (char *)malloc(total_length + 1);
745 if (*door_line == NULL) {
746 return (NS_LDAP_MEMORY);
747 }
748
749 /* make it an empty string first */
750 **door_line = '\0';
751 a = ldap_first_attribute(ld, e, &ber);
752 while (a != NULL) {
753 if (is_profile) {
754 /*
755 * If we're processing DUAConfigProfile, we need to make
756 * sure we put objectclass attribute first.
757 * __s_api_create_config_door_str depends on that.
758 */
759 if (seen_objectclass) {
760 if (strcasecmp(a, "objectclass") == 0) {
761 /* Skip objectclass now. */
762 a = ldap_next_attribute(ld, e, ber);
763 continue;
764 }
765 } else {
766 if (strcasecmp(a, "objectclass") == 0) {
767 seen_objectclass = 1;
768 rewind = 1;
769 } else {
770 /* Skip all but objectclass first. */
771 a = ldap_next_attribute(ld, e, ber);
772 continue;
773 }
774 }
775 }
776
777 if ((vals = ldap_get_values(ld, e, a)) != NULL) {
778 for (i = 0; vals[i] != NULL; i++) {
779 if (include_names) {
780 attr_len += strlen(a);
781 }
782 attr_len += strlen(vals[i]) +
783 strlen(DOORLINESEP) + 2;
784 if (include_names) {
785 (void) snprintf(*door_line +
786 strlen(*door_line),
787 attr_len,
788 "%s=%s%s",
789 a, vals[i],
790 DOORLINESEP);
791 } else {
792 (void) snprintf(*door_line +
793 strlen(*door_line),
794 attr_len,
795 "%s%s",
796 vals[i],
797 DOORLINESEP);
798 }
799 }
800 ldap_value_free(vals);
801 }
802 ldap_memfree(a);
803
804 /* Rewind */
805 if (rewind) {
806 if (ber != NULL) {
807 ber_free(ber, 0);
808 }
809 a = ldap_first_attribute(ld, e, &ber);
810 rewind = 0;
811 } else {
812 a = ldap_next_attribute(ld, e, ber);
813 }
814 }
815 if (ber != NULL) {
816 ber_free(ber, 0);
817 }
818
819 if (e != result_msg) {
820 (void) ldap_msgfree(e);
821 }
822
823 return (NS_LDAP_SUCCESS);
824 }
825
826 /*
827 * This function looks up the base DN of a directory serving
828 * a specified domain name.
829 *
830 * INPUT:
831 * ld - a pointer to an LDAP structure used for the search operation,
832 * domain_name - the name of a domain.
833 *
834 * OUTPUT:
835 * a buffer containing a directory's base DN found.
836 * Should be free'ed by the caller.
837 */
838 static
839 ns_ldap_return_code
840 getDirBaseDN(LDAP *ld, const char *domain_name, char **dir_base_dn)
841 {
842 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
843 char *attrs[2], *DNlist, *rest, *ptr;
844 char filter[BUFSIZ], *a = NULL;
845 int ldap_rc;
846 LDAPMessage *resultMsg = NULL;
847 ns_ldap_return_code ret_code;
848
849 /* Get the whole list of naming contexts residing on the server */
850 attrs[0] = "namingcontexts";
851 attrs[1] = NULL;
852 ldap_rc = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
853 attrs, 0, NULL, NULL, &tv, 0, &resultMsg);
854 switch (ldap_rc) {
855 /* If successful, the root DSE was found. */
856 case LDAP_SUCCESS:
857 break;
858 /*
859 * If the root DSE was not found, the server does
860 * not comply with the LDAP v3 protocol.
861 */
862 default:
863 if (resultMsg) {
864 (void) ldap_msgfree(resultMsg);
865 resultMsg = NULL;
866 }
867
868 return (NS_LDAP_OP_FAILED);
869 }
870
871 if ((ret_code = convert_to_door_line(ld,
872 resultMsg,
873 DONT_INCLUDE_ATTR_NAMES,
874 NOT_PROFILE,
875 &DNlist)) != NS_LDAP_SUCCESS) {
876 if (resultMsg) {
877 (void) ldap_msgfree(resultMsg);
878 resultMsg = NULL;
879 }
880 return (ret_code);
881 }
882
883 if (resultMsg) {
884 (void) ldap_msgfree(resultMsg);
885 resultMsg = NULL;
886 }
887
888 if (DNlist == NULL ||
889 (ptr = strtok_r(DNlist, DOORLINESEP, &rest)) == NULL) {
890 return (NS_LDAP_NOTFOUND);
891 }
892 attrs[0] = "dn";
893 do {
894 /*
895 * For each context try to find a NIS domain object
896 * which 'nisdomain' attribute's value matches the domain name
897 */
898 (void) snprintf(filter,
899 BUFSIZ,
900 "(&(objectclass=nisDomainObject)"
901 "(nisdomain=%s))",
902 domain_name);
903 ldap_rc = ldap_search_ext_s(ld,
904 ptr,
905 LDAP_SCOPE_SUBTREE,
906 filter,
907 attrs,
908 0,
909 NULL,
910 NULL,
911 &tv,
912 0,
913 &resultMsg);
914 if (ldap_rc != LDAP_SUCCESS) {
915 if (resultMsg) {
916 (void) ldap_msgfree(resultMsg);
917 resultMsg = NULL;
918 }
919 continue;
920 }
921 if ((a = ldap_get_dn(ld, resultMsg)) != NULL) {
922 *dir_base_dn = strdup(a);
923 ldap_memfree(a);
924
925 if (resultMsg) {
926 (void) ldap_msgfree(resultMsg);
927 resultMsg = NULL;
928 }
929
930 if (!*dir_base_dn) {
931 free(DNlist);
932 return (NS_LDAP_MEMORY);
933 }
934 break;
935 }
936
937 if (resultMsg) {
938 (void) ldap_msgfree(resultMsg);
939 resultMsg = NULL;
940 }
941 } while (ptr = strtok_r(NULL, DOORLINESEP, &rest));
942
943 free(DNlist);
944
945 if (!*dir_base_dn) {
946 return (NS_LDAP_NOTFOUND);
947 }
948
949 return (NS_LDAP_SUCCESS);
950 }
951
952 /*
953 * This function parses the results of a search operation
954 * requesting a DUAProfile.
955 *
956 * INPUT:
957 * ld - a pointer to an LDAP structure used for the search operation,
958 * dir_base_dn - the name of a directory's base DN,
959 * profile_name - the name of a DUAProfile to be obtained.
960 *
961 * OUTPUT:
962 * a buffer containing the DUAProfile in the following format:
963 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
964 * Should be free'ed by the caller.
965 */
966 static
967 ns_ldap_return_code
968 getDUAProfile(LDAP *ld,
969 const char *dir_base_dn,
970 const char *profile_name,
971 char **profile)
972 {
973 char searchBaseDN[BUFSIZ], filter[BUFSIZ];
974 LDAPMessage *resultMsg = NULL;
975 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
976 int ldap_rc;
977 ns_ldap_return_code ret_code;
978
979 (void) snprintf(searchBaseDN, BUFSIZ, "ou=profile,%s", dir_base_dn);
980 (void) snprintf(filter,
981 BUFSIZ,
982 _PROFILE_FILTER,
983 _PROFILE1_OBJECTCLASS,
984 _PROFILE2_OBJECTCLASS,
985 profile_name);
986 ldap_rc = ldap_search_ext_s(ld,
987 searchBaseDN,
988 LDAP_SCOPE_SUBTREE,
989 filter,
990 NULL,
991 0,
992 NULL,
993 NULL,
994 &tv,
995 0,
996 &resultMsg);
997
998 switch (ldap_rc) {
999 /* If successful, the DUA profile was found. */
1000 case LDAP_SUCCESS:
1001 break;
1002 /*
1003 * If the root DSE was not found, the server does
1004 * not comply with the LDAP v3 protocol.
1005 */
1006 default:
1007 if (resultMsg) {
1008 (void) ldap_msgfree(resultMsg);
1009 resultMsg = NULL;
1010 }
1011
1012 return (NS_LDAP_OP_FAILED);
1013 }
1014
1015 ret_code = convert_to_door_line(ld,
1016 resultMsg,
1017 INCLUDE_ATTR_NAMES,
1018 IS_PROFILE,
1019 profile);
1020 if (resultMsg) {
1021 (void) ldap_msgfree(resultMsg);
1022 resultMsg = NULL;
1023 }
1024 return (ret_code);
1025 }
1026
1027 /*
1028 * This function derives the directory's base DN from a provided domain name.
1029 *
1030 * INPUT:
1031 * domain_name - the name of a domain to be converted into a base DN,
1032 * buffer - contains the derived base DN,
1033 * buf_len - the length of the buffer.
1034 *
1035 * OUTPUT:
1036 * The function returns the address of the buffer or NULL.
1037 */
1038 static
1039 char *
1040 domainname2baseDN(char *domain_name, char *buffer, uint16_t buf_len)
1041 {
1042 char *nextDC, *chr;
1043 uint16_t i, length;
1044
1045 if (!domain_name || !buffer || buf_len == 0) {
1046 return (NULL);
1047 }
1048
1049 buffer[0] = '\0';
1050 nextDC = chr = domain_name;
1051 length = strlen(domain_name);
1052 for (i = 0; i < length + 1; ++i, ++chr) {
1053 /* Simply replace dots with "dc=" */
1054 if (*chr != '.' && *chr != '\0') {
1055 continue;
1056 }
1057 *chr = '\0';
1058 if (strlcat(buffer, "dc=", buf_len) >= buf_len)
1059 return (NULL);
1060 if (strlcat(buffer, nextDC, buf_len) >= buf_len)
1061 return (NULL);
1062 if (i < length) {
1063 /*
1064 * The end of the domain name
1065 * has not been reached yet
1066 */
1067 if (strlcat(buffer, ",", buf_len) >= buf_len)
1068 return (NULL);
1069 nextDC = chr + 1;
1070 *chr = '.';
1071 }
1072 }
1073
1074 return (buffer);
1075 }
1076
1077 /*
1078 * This function obtains the directory's base DN and a DUAProfile
1079 * from a specified server.
1080 *
1081 * INPUT:
1082 * server - a structure describing a server to connect to and
1083 * a DUAProfile to be obtained from the server,
1084 * cred - credentials to be used during establishing connections to
1085 * the server.
1086 *
1087 * OUTPUT:
1088 * dua_profile - a buffer containing the DUAProfile in the following format:
1089 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
1090 * dir_base_dn - a buffer containing the base DN,
1091 * errorp - an error object describing an error, if any.
1092 *
1093 * All the output data structures should be free'ed by the caller.
1094 */
1095 ns_ldap_return_code
1096 __ns_ldap_getConnectionInfoFromDUA(const ns_dir_server_t *server,
1097 const ns_cred_t *cred,
1098 char **dua_profile,
1099 char **dir_base_dn,
1100 ns_ldap_error_t **errorp)
1101 {
1102 char serverAddr[MAX_HOSTADDR_LEN];
1103 char *dirBaseDN = NULL, *duaProfile = NULL;
1104 ns_cred_t default_cred;
1105 ns_ldap_return_code ret_code;
1106
1107 ns_config_t *config_struct = __s_api_create_config();
1108 ConnectionID sessionId = 0;
1109 Connection *session = NULL;
1110 char errmsg[MAXERROR];
1111 char buffer[NSS_BUFLEN_HOSTS];
1112 ns_conn_user_t *cu = NULL;
1113
1114 if (errorp == NULL) {
1115 __s_api_destroy_config(config_struct);
1116 return (NS_LDAP_INVALID_PARAM);
1117 }
1118
1119 *errorp = NULL;
1120
1121 if (server == NULL) {
1122 __s_api_destroy_config(config_struct);
1123 return (NS_LDAP_INVALID_PARAM);
1124 }
1125
1126 if (config_struct == NULL) {
1127 return (NS_LDAP_MEMORY);
1128 }
1129
1130 /*
1131 * If no credentials are specified, try to establish a connection
1132 * as anonymous.
1133 */
1134 if (!cred) {
1135 default_cred.cred.unix_cred.passwd = NULL;
1136 default_cred.cred.unix_cred.userID = NULL;
1137 default_cred.auth.type = NS_LDAP_AUTH_NONE;
1138 }
1139
1140 /* Now create a default LDAP configuration */
1141
1142 (void) strncpy(buffer, server->server, sizeof (buffer));
1143 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SERVERS_P, buffer,
1144 errorp) != NS_LDAP_SUCCESS) {
1145 __s_api_destroy_config(config_struct);
1146 return (NS_LDAP_CONFIG);
1147 }
1148
1149 /* Put together the address and the port specified by the user app. */
1150 if (server->port > 0) {
1151 (void) snprintf(serverAddr,
1152 sizeof (serverAddr),
1153 "%s:%hu",
1154 buffer,
1155 server->port);
1156 } else {
1157 (void) strncpy(serverAddr, buffer, sizeof (serverAddr));
1158 }
1159
1160 /*
1161 * There is no default value for the 'Default Search Base DN' attribute.
1162 * Derive one from the domain name to make __s_api_crosscheck() happy.
1163 */
1164 if (domainname2baseDN(server->domainName ?
1165 server->domainName : config_struct->domainName,
1166 buffer, NSS_BUFLEN_HOSTS) == NULL) {
1167 (void) snprintf(errmsg,
1168 sizeof (errmsg),
1169 gettext("Can not convert %s into a base DN name"),
1170 server->domainName ?
1171 server->domainName : config_struct->domainName);
1172 MKERROR(LOG_ERR,
1173 *errorp,
1174 NS_LDAP_INTERNAL,
1175 strdup(errmsg),
1176 NS_LDAP_MEMORY);
1177 __s_api_destroy_config(config_struct);
1178 return (NS_LDAP_INTERNAL);
1179 }
1180 if (__ns_ldap_setParamValue(config_struct, NS_LDAP_SEARCH_BASEDN_P,
1181 buffer, errorp) != NS_LDAP_SUCCESS) {
1182 __s_api_destroy_config(config_struct);
1183 return (NS_LDAP_CONFIG);
1184 }
1185
1186 if (__s_api_crosscheck(config_struct, errmsg, B_FALSE) != NS_SUCCESS) {
1187 __s_api_destroy_config(config_struct);
1188 return (NS_LDAP_CONFIG);
1189 }
1190
1191 __s_api_init_config(config_struct);
1192
1193 __s_api_setInitMode();
1194
1195 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE);
1196 if (cu == NULL) {
1197 return (NS_LDAP_INTERNAL);
1198 }
1199
1200 if ((ret_code = __s_api_getConnection(serverAddr,
1201 NS_LDAP_NEW_CONN,
1202 cred ? cred : &default_cred,
1203 &sessionId,
1204 &session,
1205 errorp,
1206 0,
1207 0,
1208 cu)) != NS_LDAP_SUCCESS) {
1209 __s_api_conn_user_free(cu);
1210 __s_api_unsetInitMode();
1211 return (ret_code);
1212 }
1213
1214 __s_api_unsetInitMode();
1215
1216 if ((ret_code = getDirBaseDN(session->ld,
1217 server->domainName ?
1218 server->domainName :
1219 config_struct->domainName,
1220 &dirBaseDN)) != NS_LDAP_SUCCESS) {
1221 (void) snprintf(errmsg,
1222 sizeof (errmsg),
1223 gettext("Can not find the "
1224 "nisDomainObject for domain %s\n"),
1225 server->domainName ?
1226 server->domainName : config_struct->domainName);
1227 MKERROR(LOG_ERR,
1228 *errorp,
1229 ret_code,
1230 strdup(errmsg),
1231 NS_LDAP_MEMORY);
1232 __s_api_conn_user_free(cu);
1233 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1234 return (ret_code);
1235 }
1236
1237 /*
1238 * And here obtain a DUAProfile which will be used
1239 * as a real configuration.
1240 */
1241 if ((ret_code = getDUAProfile(session->ld,
1242 dirBaseDN,
1243 server->profileName ?
1244 server->profileName : "default",
1245 &duaProfile)) != NS_LDAP_SUCCESS) {
1246 (void) snprintf(errmsg,
1247 sizeof (errmsg),
1248 gettext("Can not find the "
1249 "%s DUAProfile\n"),
1250 server->profileName ?
1251 server->profileName : "default");
1252 MKERROR(LOG_ERR,
1253 *errorp,
1254 ret_code,
1255 strdup(errmsg),
1256 NS_LDAP_MEMORY);
1257 __s_api_conn_user_free(cu);
1258 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1259 return (ret_code);
1260 }
1261
1262 if (dir_base_dn) {
1263 *dir_base_dn = dirBaseDN;
1264 } else {
1265 free(dirBaseDN);
1266 }
1267
1268 if (dua_profile) {
1269 *dua_profile = duaProfile;
1270 } else {
1271 free(duaProfile);
1272 }
1273
1274 __s_api_conn_user_free(cu);
1275 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1276
1277 return (NS_LDAP_SUCCESS);
1278 }
1279
1280 /*
1281 * This function obtains the root DSE from a specified server.
1282 *
1283 * INPUT:
1284 * server_addr - an adress of a server to be connected to.
1285 *
1286 * OUTPUT:
1287 * root_dse - a buffer containing the root DSE in the following format:
1288 * [<attribute name>=]value [DOORLINESEP [<attribute name>=]value ]...]
1289 * For example: ( here | used as DOORLINESEP for visual purposes)
1290 * supportedControl=1.1.1.1|supportedSASLmechanisms=EXTERNAL
1291 * Should be free'ed by the caller.
1292 */
1293 ns_ldap_return_code
1294 __ns_ldap_getRootDSE(const char *server_addr,
1295 char **root_dse,
1296 ns_ldap_error_t **errorp,
1297 int anon_fallback)
1298 {
1299 char errmsg[MAXERROR];
1300 ns_ldap_return_code ret_code;
1301
1302 ConnectionID sessionId = 0;
1303 Connection *session = NULL;
1304
1305 struct timeval tv = {NS_DEFAULT_SEARCH_TIMEOUT, 0};
1306 char *attrs[3];
1307 int ldap_rc, ldaperrno = 0;
1308 LDAPMessage *resultMsg = NULL;
1309 void **paramVal = NULL;
1310
1311 ns_cred_t anon;
1312 ns_conn_user_t *cu = NULL;
1313
1314 if (errorp == NULL) {
1315 return (NS_LDAP_INVALID_PARAM);
1316 }
1317
1318 *errorp = NULL;
1319
1320 if (!root_dse) {
1321 return (NS_LDAP_INVALID_PARAM);
1322 }
1323
1324 if (!server_addr) {
1325 return (NS_LDAP_INVALID_PARAM);
1326 }
1327
1328 __s_api_setInitMode();
1329
1330 cu = __s_api_conn_user_init(NS_CONN_USER_SEARCH, NULL, B_FALSE);
1331 if (cu == NULL) {
1332 return (NS_LDAP_INTERNAL);
1333 }
1334
1335 /*
1336 * All the credentials will be taken from the current
1337 * libsldap configuration.
1338 */
1339 if ((ret_code = __s_api_getConnection(server_addr,
1340 NS_LDAP_NEW_CONN,
1341 NULL,
1342 &sessionId,
1343 &session,
1344 errorp,
1345 0,
1346 0,
1347 cu)) != NS_LDAP_SUCCESS) {
1348 /* Fallback to anonymous mode is disabled. Stop. */
1349 if (anon_fallback == 0) {
1350 syslog(LOG_WARNING,
1351 gettext("libsldap: can not get the root DSE from "
1352 " the %s server: %s. "
1353 "Falling back to anonymous disabled.\n"),
1354 server_addr,
1355 errorp && *errorp && (*errorp)->message ?
1356 (*errorp)->message : "");
1357 if (errorp != NULL && *errorp != NULL) {
1358 (void) __ns_ldap_freeError(errorp);
1359 }
1360 __s_api_unsetInitMode();
1361 return (ret_code);
1362 }
1363
1364 /*
1365 * Fallback to anonymous, non-SSL mode for backward
1366 * compatibility reasons. This mode should only be used when
1367 * this function (__ns_ldap_getRootDSE) is called from
1368 * ldap_cachemgr(1M).
1369 */
1370 syslog(LOG_WARNING,
1371 gettext("libsldap: Falling back to anonymous, non-SSL"
1372 " mode for __ns_ldap_getRootDSE. %s\n"),
1373 errorp && *errorp && (*errorp)->message ?
1374 (*errorp)->message : "");
1375
1376 /* Setup the anon credential for anonymous connection. */
1377 (void) memset(&anon, 0, sizeof (ns_cred_t));
1378 anon.auth.type = NS_LDAP_AUTH_NONE;
1379
1380 if (*errorp != NULL) {
1381 (void) __ns_ldap_freeError(errorp);
1382 }
1383 *errorp = NULL;
1384
1385 ret_code = __s_api_getConnection(server_addr,
1386 NS_LDAP_NEW_CONN,
1387 &anon,
1388 &sessionId,
1389 &session,
1390 errorp,
1391 0,
1392 0,
1393 cu);
1394
1395 if (ret_code != NS_LDAP_SUCCESS) {
1396 __s_api_conn_user_free(cu);
1397 __s_api_unsetInitMode();
1398 return (ret_code);
1399 }
1400 }
1401
1402 __s_api_unsetInitMode();
1403
1404 /* get search timeout value */
1405 (void) __ns_ldap_getParam(NS_LDAP_SEARCH_TIME_P, ¶mVal, errorp);
1406 if (paramVal != NULL && *paramVal != NULL) {
1407 tv.tv_sec = **((int **)paramVal);
1408 (void) __ns_ldap_freeParam(¶mVal);
1409 }
1410 if (*errorp != NULL) {
1411 (void) __ns_ldap_freeError(errorp);
1412 }
1413
1414 /* Get root DSE from the server specified by the caller. */
1415 attrs[0] = "supportedControl";
1416 attrs[1] = "supportedsaslmechanisms";
1417 attrs[2] = NULL;
1418 ldap_rc = ldap_search_ext_s(session->ld,
1419 "",
1420 LDAP_SCOPE_BASE,
1421 "(objectclass=*)",
1422 attrs,
1423 0,
1424 NULL,
1425 NULL,
1426 &tv,
1427 0,
1428 &resultMsg);
1429
1430 if (ldap_rc != LDAP_SUCCESS) {
1431 /*
1432 * If the root DSE was not found, the server does
1433 * not comply with the LDAP v3 protocol.
1434 */
1435 (void) ldap_get_option(session->ld,
1436 LDAP_OPT_ERROR_NUMBER,
1437 &ldaperrno);
1438 (void) snprintf(errmsg,
1439 sizeof (errmsg),
1440 gettext(ldap_err2string(ldaperrno)));
1441 MKERROR(LOG_ERR,
1442 *errorp,
1443 NS_LDAP_OP_FAILED,
1444 strdup(errmsg),
1445 NS_LDAP_MEMORY);
1446
1447 if (resultMsg) {
1448 (void) ldap_msgfree(resultMsg);
1449 resultMsg = NULL;
1450 }
1451
1452 __s_api_conn_user_free(cu);
1453 return (NS_LDAP_OP_FAILED);
1454 }
1455 __s_api_conn_user_free(cu);
1456
1457 ret_code = convert_to_door_line(session->ld,
1458 resultMsg,
1459 INCLUDE_ATTR_NAMES,
1460 NOT_PROFILE,
1461 root_dse);
1462 if (ret_code == NS_LDAP_NOTFOUND) {
1463 (void) snprintf(errmsg,
1464 sizeof (errmsg),
1465 gettext("No root DSE data "
1466 "for server %s returned."),
1467 server_addr);
1468 MKERROR(LOG_ERR,
1469 *errorp,
1470 NS_LDAP_NOTFOUND,
1471 strdup(errmsg),
1472 NS_LDAP_MEMORY);
1473 }
1474
1475 if (resultMsg) {
1476 (void) ldap_msgfree(resultMsg);
1477 resultMsg = NULL;
1478 }
1479
1480 DropConnection(sessionId, NS_LDAP_NEW_CONN);
1481
1482 return (ret_code);
1483 }
1484
1485 /*
1486 * This function destroys the local list of root DSEs. The input parameter is
1487 * a pointer to the list to be erased.
1488 * The type of the pointer passed to this function should be
1489 * (dir_server_list_t *).
1490 */
1491 static
1492 void *
1493 disposeOfOldList(void *param)
1494 {
1495 dir_server_list_t *old_list = (dir_server_list_t *)param;
1496 long i = 0, j;
1497
1498 (void) rw_wrlock(&old_list->listDestroyLock);
1499 /* Destroy the old list */
1500 while (old_list->nsServers[i]) {
1501 free(old_list->nsServers[i]->ip);
1502 j = 0;
1503 while (old_list->nsServers[i]->controls &&
1504 old_list->nsServers[i]->controls[j]) {
1505 free(old_list->nsServers[i]->controls[j]);
1506 ++j;
1507 }
1508 free(old_list->nsServers[i]->controls);
1509 j = 0;
1510 while (old_list->nsServers[i]->saslMech &&
1511 old_list->nsServers[i]->saslMech[j]) {
1512 free(old_list->nsServers[i]->saslMech[j]);
1513 ++j;
1514 }
1515 free(old_list->nsServers[i]->saslMech);
1516 ++i;
1517 }
1518 /*
1519 * All the structures pointed by old_list->nsServers were allocated
1520 * in one chunck. The nsServers[0] pointer points to the beginning
1521 * of that chunck.
1522 */
1523 free(old_list->nsServers[0]);
1524 free(old_list->nsServers);
1525 (void) rw_unlock(&old_list->listDestroyLock);
1526 (void) rwlock_destroy(&old_list->listDestroyLock);
1527 free(old_list);
1528
1529 return (NULL);
1530 }
1531
1532 /*
1533 * This function cancels the Standalone mode and destroys the list of root DSEs.
1534 */
1535 void
1536 __ns_ldap_cancelStandalone(void)
1537 {
1538 dir_server_list_t *old_list;
1539
1540 (void) mutex_lock(&dir_servers.listReplaceLock);
1541 dir_servers.standalone = 0;
1542 if (!dir_servers.list) {
1543 (void) mutex_unlock(&dir_servers.listReplaceLock);
1544 return;
1545 }
1546 old_list = dir_servers.list;
1547 dir_servers.list = NULL;
1548 (void) mutex_unlock(&dir_servers.listReplaceLock);
1549
1550 (void) disposeOfOldList(old_list);
1551 }
1552
1553
1554 static
1555 void*
1556 create_ns_servers_entry(void *param)
1557 {
1558 #define CHUNK_SIZE 16
1559
1560 dir_server_t *server = (dir_server_t *)param;
1561 ns_ldap_return_code *retCode = calloc(1,
1562 sizeof (ns_ldap_return_code));
1563 uint32_t sc_counter = 0, sm_counter = 0;
1564 uint32_t sc_mem_blocks = 1, sm_mem_blocks = 1;
1565 char *rootDSE = NULL, *attr, *val, *rest, **ptr;
1566 ns_ldap_error_t *error = NULL;
1567
1568 if (retCode == NULL) {
1569 return (NULL);
1570 }
1571
1572 /*
1573 * We call this function in non anon-fallback mode because we
1574 * want the whole procedure to fail as soon as possible to
1575 * indicate there are problems with connecting to the server.
1576 */
1577 *retCode = __ns_ldap_getRootDSE(server->ip,
1578 &rootDSE,
1579 &error,
1580 SA_ALLOW_FALLBACK);
1581
1582 if (*retCode == NS_LDAP_MEMORY) {
1583 free(retCode);
1584 return (NULL);
1585 }
1586
1587 /*
1588 * If the root DSE can not be obtained, log an error and keep the
1589 * server.
1590 */
1591 if (*retCode != NS_LDAP_SUCCESS) {
1592 server->status = INFO_SERVER_ERROR;
1593 syslog(LOG_WARNING,
1594 gettext("libsldap (\"standalone\" mode): "
1595 "can not obtain the root DSE from %s. %s"),
1596 server->ip,
1597 error && error->message ? error->message : "");
1598 if (error) {
1599 (void) __ns_ldap_freeError(&error);
1600 }
1601 return (retCode);
1602 }
1603
1604 /* Get the first attribute of the root DSE. */
1605 attr = strtok_r(rootDSE, DOORLINESEP, &rest);
1606 if (attr == NULL) {
1607 free(rootDSE);
1608 server->status = INFO_SERVER_ERROR;
1609 syslog(LOG_WARNING,
1610 gettext("libsldap (\"standalone\" mode): "
1611 "the root DSE from %s is empty or corrupted."),
1612 server->ip);
1613 *retCode = NS_LDAP_INTERNAL;
1614 return (retCode);
1615 }
1616
1617 server->controls = (char **)calloc(CHUNK_SIZE, sizeof (char *));
1618 server->saslMech = (char **)calloc(CHUNK_SIZE, sizeof (char *));
1619 if (server->controls == NULL || server->saslMech == NULL) {
1620 free(rootDSE);
1621 free(retCode);
1622 return (NULL);
1623 }
1624
1625 do {
1626 if ((val = strchr(attr, '=')) == NULL) {
1627 continue;
1628 }
1629 ++val;
1630
1631 if (strncasecmp(attr,
1632 _SASLMECHANISM,
1633 _SASLMECHANISM_LEN) == 0) {
1634 if (sm_counter == CHUNK_SIZE * sm_mem_blocks - 1) {
1635 ptr = (char **)realloc(server->saslMech,
1636 CHUNK_SIZE *
1637 ++sm_mem_blocks *
1638 sizeof (char *));
1639 if (ptr == NULL) {
1640 *retCode = NS_LDAP_MEMORY;
1641 break;
1642 }
1643 bzero((char *)ptr +
1644 (sm_counter + 1) *
1645 sizeof (char *),
1646 CHUNK_SIZE *
1647 sm_mem_blocks *
1648 sizeof (char *) -
1649 (sm_counter + 1) *
1650 sizeof (char *));
1651 server->saslMech = ptr;
1652 }
1653 server->saslMech[sm_counter] = strdup(val);
1654 if (server->saslMech[sm_counter] == NULL) {
1655 *retCode = NS_LDAP_MEMORY;
1656 break;
1657 }
1658 ++sm_counter;
1659 continue;
1660 }
1661 if (strncasecmp(attr,
1662 _SUPPORTEDCONTROL,
1663 _SUPPORTEDCONTROL_LEN) == 0) {
1664 if (sc_counter == CHUNK_SIZE * sc_mem_blocks - 1) {
1665 ptr = (char **)realloc(server->controls,
1666 CHUNK_SIZE *
1667 ++sc_mem_blocks *
1668 sizeof (char *));
1669 if (ptr == NULL) {
1670 *retCode = NS_LDAP_MEMORY;
1671 break;
1672 }
1673 bzero((char *)ptr +
1674 (sc_counter + 1) *
1675 sizeof (char *),
1676 CHUNK_SIZE *
1677 sc_mem_blocks *
1678 sizeof (char *) -
1679 (sc_counter + 1) *
1680 sizeof (char *));
1681 server->controls = ptr;
1682 }
1683
1684 server->controls[sc_counter] = strdup(val);
1685 if (server->controls[sc_counter] == NULL) {
1686 *retCode = NS_LDAP_MEMORY;
1687 break;
1688 }
1689 ++sc_counter;
1690 continue;
1691 }
1692
1693 } while (attr = strtok_r(NULL, DOORLINESEP, &rest));
1694
1695 free(rootDSE);
1696
1697 if (*retCode == NS_LDAP_MEMORY) {
1698 free(retCode);
1699 return (NULL);
1700 }
1701
1702 server->controls[sc_counter] = NULL;
1703 server->saslMech[sm_counter] = NULL;
1704
1705 server->status = INFO_SERVER_UP;
1706
1707 return (retCode);
1708 #undef CHUNK_SIZE
1709 }
1710
1711
1712 /*
1713 * This function creates a new local list of root DSEs from all the servers
1714 * mentioned in the DUAProfile (or local NS BEC) and returns
1715 * a pointer to the list.
1716 */
1717 static
1718 ns_ldap_return_code
1719 createDirServerList(dir_server_list_t **new_list,
1720 ns_ldap_error_t **errorp)
1721 {
1722 char **serverList;
1723 ns_ldap_return_code retCode = NS_LDAP_SUCCESS;
1724 dir_server_t *tmpSrvArray;
1725 long srvListLength, i;
1726 thread_t *thrPool, thrID;
1727 void *status = NULL;
1728
1729 if (errorp == NULL) {
1730 return (NS_LDAP_INVALID_PARAM);
1731 }
1732
1733 *errorp = NULL;
1734
1735 if (new_list == NULL) {
1736 return (NS_LDAP_INVALID_PARAM);
1737 }
1738
1739 retCode = __s_api_getServers(&serverList, errorp);
1740 if (retCode != NS_LDAP_SUCCESS || serverList == NULL) {
1741 return (retCode);
1742 }
1743
1744 for (i = 0; serverList[i]; ++i) {
1745 ;
1746 }
1747 srvListLength = i;
1748
1749 thrPool = calloc(srvListLength, sizeof (thread_t));
1750 if (thrPool == NULL) {
1751 __s_api_free2dArray(serverList);
1752 return (NS_LDAP_MEMORY);
1753 }
1754
1755 *new_list = (dir_server_list_t *)calloc(1,
1756 sizeof (dir_server_list_t));
1757 if (*new_list == NULL) {
1758 __s_api_free2dArray(serverList);
1759 free(thrPool);
1760 return (NS_LDAP_MEMORY);
1761 }
1762 (void) rwlock_init(&(*new_list)->listDestroyLock, USYNC_THREAD, NULL);
1763
1764 (*new_list)->nsServers = (dir_server_t **)calloc(srvListLength + 1,
1765 sizeof (dir_server_t *));
1766 if ((*new_list)->nsServers == NULL) {
1767 free(*new_list);
1768 *new_list = NULL;
1769 __s_api_free2dArray(serverList);
1770 free(thrPool);
1771 return (NS_LDAP_MEMORY);
1772 }
1773
1774 /*
1775 * Allocate a set of dir_server_t structures as an array,
1776 * with one alloc call and then initialize the nsServers pointers
1777 * with the addresses of the array's members.
1778 */
1779 tmpSrvArray = (dir_server_t *)calloc(srvListLength,
1780 sizeof (dir_server_t));
1781 for (i = 0; i < srvListLength; ++i) {
1782 (*new_list)->nsServers[i] = &tmpSrvArray[i];
1783
1784 (*new_list)->nsServers[i]->info = INFO_STATUS_NEW;
1785 (void) mutex_init(&(*new_list)->nsServers[i]->updateStatus,
1786 USYNC_THREAD,
1787 NULL);
1788
1789 (*new_list)->nsServers[i]->ip = strdup(serverList[i]);
1790 if ((*new_list)->nsServers[i]->ip == NULL) {
1791 retCode = NS_LDAP_MEMORY;
1792 break;
1793 }
1794
1795 (*new_list)->nsServers[i]->status = INFO_SERVER_CONNECTING;
1796
1797 switch (thr_create(NULL,
1798 0,
1799 create_ns_servers_entry,
1800 (*new_list)->nsServers[i],
1801 0,
1802 &thrID)) {
1803 case EAGAIN:
1804 (*new_list)->nsServers[i]->status =
1805 INFO_SERVER_ERROR;
1806 continue;
1807 case ENOMEM:
1808 (*new_list)->nsServers[i]->status =
1809 INFO_SERVER_ERROR;
1810 continue;
1811 default:
1812 thrPool[i] = thrID;
1813 continue;
1814 }
1815 }
1816
1817 for (i = 0; i < srvListLength; ++i) {
1818 if (thrPool[i] != 0 &&
1819 thr_join(thrPool[i], NULL, &status) == 0) {
1820 if (status == NULL) {
1821 /*
1822 * Some memory allocation problems occured. Just
1823 * ignore the server and hope there will be some
1824 * other good ones.
1825 */
1826 (*new_list)->nsServers[i]->status =
1827 INFO_SERVER_ERROR;
1828 }
1829 free(status);
1830 }
1831 }
1832
1833 __s_api_free2dArray(serverList);
1834 free(thrPool);
1835
1836 if (retCode == NS_LDAP_MEMORY) {
1837 (void) disposeOfOldList(*new_list);
1838 return (NS_LDAP_MEMORY);
1839 }
1840
1841 return (NS_LDAP_SUCCESS);
1842 }
1843
1844 /*
1845 * This functions replaces the local list of root DSEs with a new one and starts
1846 * a thread destroying the old list. There is no need for other threads to wait
1847 * until the old list will be destroyed.
1848 * Since it is possible that more than one thread can start creating the list,
1849 * this function should be protected by mutexes to be sure that only one thread
1850 * performs the initialization.
1851 */
1852 static
1853 ns_ldap_return_code
1854 initGlobalList(ns_ldap_error_t **error)
1855 {
1856 dir_server_list_t *new_list, *old_list;
1857 ns_ldap_return_code ret_code;
1858 thread_t tid;
1859
1860 ret_code = createDirServerList(&new_list, error);
1861 if (ret_code != NS_LDAP_SUCCESS) {
1862 return (ret_code);
1863 }
1864
1865 old_list = dir_servers.list;
1866 dir_servers.list = new_list;
1867
1868 if (old_list) {
1869 (void) thr_create(NULL,
1870 0,
1871 disposeOfOldList,
1872 old_list,
1873 THR_DETACHED,
1874 &tid);
1875 }
1876
1877 return (NS_LDAP_SUCCESS);
1878 }
1879
1880 static
1881 struct {
1882 char *authMech;
1883 ns_auth_t auth;
1884 } authArray[] = {{"none", {NS_LDAP_AUTH_NONE,
1885 NS_LDAP_TLS_NONE,
1886 NS_LDAP_SASL_NONE,
1887 NS_LDAP_SASLOPT_NONE}},
1888 {"simple", {NS_LDAP_AUTH_SIMPLE,
1889 NS_LDAP_TLS_NONE,
1890 NS_LDAP_SASL_NONE,
1891 NS_LDAP_SASLOPT_NONE}},
1892 {"tls:simple", {NS_LDAP_AUTH_TLS,
1893 NS_LDAP_TLS_SIMPLE,
1894 NS_LDAP_SASL_NONE,
1895 NS_LDAP_SASLOPT_NONE}},
1896 {"tls:sasl/CRAM-MD5", {NS_LDAP_AUTH_TLS,
1897 NS_LDAP_TLS_SASL,
1898 NS_LDAP_SASL_CRAM_MD5,
1899 NS_LDAP_SASLOPT_NONE}},
1900 {"tls:sasl/DIGEST-MD5", {NS_LDAP_AUTH_TLS,
1901 NS_LDAP_TLS_SASL,
1902 NS_LDAP_SASL_DIGEST_MD5,
1903 NS_LDAP_SASLOPT_NONE}},
1904 {"sasl/CRAM-MD5", {NS_LDAP_AUTH_SASL,
1905 NS_LDAP_TLS_SASL,
1906 NS_LDAP_SASL_CRAM_MD5,
1907 NS_LDAP_SASLOPT_NONE}},
1908 {"sasl/DIGEST-MD5", {NS_LDAP_AUTH_SASL,
1909 NS_LDAP_TLS_SASL,
1910 NS_LDAP_SASL_DIGEST_MD5,
1911 NS_LDAP_SASLOPT_NONE}},
1912 {"sasl/GSSAPI", {NS_LDAP_AUTH_SASL,
1913 NS_LDAP_TLS_SASL,
1914 NS_LDAP_SASL_GSSAPI,
1915 NS_LDAP_SASLOPT_PRIV | NS_LDAP_SASLOPT_INT}},
1916 {NULL, {NS_LDAP_AUTH_NONE,
1917 NS_LDAP_TLS_NONE,
1918 NS_LDAP_SASL_NONE,
1919 NS_LDAP_SASLOPT_NONE}}};
1920
1921 ns_ldap_return_code
1922 __ns_ldap_initAuth(const char *auth_mech,
1923 ns_auth_t *auth,
1924 ns_ldap_error_t **errorp)
1925 {
1926 uint32_t i;
1927 char errmsg[MAXERROR];
1928
1929 if (auth_mech == NULL) {
1930 (void) snprintf(errmsg,
1931 sizeof (errmsg),
1932 gettext("Invalid authentication method specified\n"));
1933 MKERROR(LOG_WARNING,
1934 *errorp,
1935 NS_LDAP_INTERNAL,
1936 strdup(errmsg),
1937 NS_LDAP_MEMORY);
1938 return (NS_LDAP_INTERNAL);
1939 }
1940
1941 for (i = 0; authArray[i].authMech != NULL; ++i) {
1942 if (strcasecmp(auth_mech, authArray[i].authMech) == 0) {
1943 *auth = authArray[i].auth;
1944 return (NS_LDAP_SUCCESS);
1945 }
1946 }
1947
1948 (void) snprintf(errmsg,
1949 sizeof (errmsg),
1950 gettext("Invalid authentication method specified\n"));
1951 MKERROR(LOG_WARNING,
1952 *errorp,
1953 NS_LDAP_INTERNAL,
1954 strdup(errmsg),
1955 NS_LDAP_MEMORY);
1956 return (NS_LDAP_INTERNAL);
1957 }
1958
1959 /*
1960 * This function "informs" libsldap that a client application has specified
1961 * a directory to use. The function obtains a DUAProfile, credentials,
1962 * and naming context. During all further operations on behalf
1963 * of the application requested a standalone schema libsldap will use
1964 * the information obtained by __ns_ldap_initStandalone() instead of
1965 * door_call(3C)ing ldap_cachemgr(1M).
1966 *
1967 * INPUT:
1968 * sa_conf - a structure describing where and in which way to obtain all
1969 * the configuration describing how to communicate to
1970 * a choosen LDAP directory,
1971 * errorp - an error object describing an error occured.
1972 */
1973 ns_ldap_return_code
1974 __ns_ldap_initStandalone(const ns_standalone_conf_t *sa_conf,
1975 ns_ldap_error_t **errorp) {
1976
1977 ns_cred_t user_cred = {{NS_LDAP_AUTH_NONE,
1978 NS_LDAP_TLS_NONE,
1979 NS_LDAP_SASL_NONE,
1980 NS_LDAP_SASLOPT_NONE},
1981 NULL,
1982 {NULL, NULL}};
1983 char *dua_profile = NULL;
1984 char errmsg[MAXERROR];
1985 ns_config_t *cfg;
1986 int ret_code;
1987
1988 if (sa_conf->SA_BIND_DN == NULL && sa_conf->SA_BIND_PWD != NULL ||
1989 sa_conf->SA_BIND_DN != NULL && sa_conf->SA_BIND_PWD == NULL) {
1990 (void) snprintf(errmsg,
1991 sizeof (errmsg),
1992 gettext("Bind DN and bind password"
1993 " must both be provided\n"));
1994 MKERROR(LOG_ERR,
1995 *errorp,
1996 NS_CONFIG_NOTLOADED,
1997 strdup(errmsg),
1998 NS_LDAP_MEMORY);
1999 return (NS_LDAP_INTERNAL);
2000 }
2001
2002 switch (sa_conf->type) {
2003 case NS_LDAP_SERVER:
2004 if (sa_conf->SA_BIND_DN != NULL) {
2005 user_cred.cred.unix_cred.userID = sa_conf->SA_BIND_DN;
2006 user_cred.auth.type = NS_LDAP_AUTH_SIMPLE;
2007 }
2008
2009 if (sa_conf->SA_BIND_PWD != NULL) {
2010 user_cred.cred.unix_cred.passwd = sa_conf->SA_BIND_PWD;
2011 }
2012
2013 if (sa_conf->SA_AUTH != NULL) {
2014 user_cred.auth.type = sa_conf->SA_AUTH->type;
2015 user_cred.auth.tlstype = sa_conf->SA_AUTH->tlstype;
2016 user_cred.auth.saslmech = sa_conf->SA_AUTH->saslmech;
2017 user_cred.auth.saslopt = sa_conf->SA_AUTH->saslopt;
2018 }
2019
2020 if (sa_conf->SA_CERT_PATH != NULL) {
2021 user_cred.hostcertpath = sa_conf->SA_CERT_PATH;
2022 }
2023
2024 ret_code = __ns_ldap_getConnectionInfoFromDUA(
2025 &sa_conf->ds_profile.server,
2026 &user_cred,
2027 &dua_profile,
2028 NULL,
2029 errorp);
2030 if (ret_code != NS_LDAP_SUCCESS) {
2031 return (ret_code);
2032 }
2033
2034 cfg = __s_api_create_config_door_str(dua_profile, errorp);
2035 if (cfg == NULL) {
2036 free(dua_profile);
2037 return (NS_LDAP_CONFIG);
2038 }
2039
2040 if (sa_conf->SA_CERT_PATH != NULL) {
2041 char *certPathAttr;
2042 ParamIndexType type;
2043
2044 switch (cfg->version) {
2045 case NS_LDAP_V1:
2046 certPathAttr = "NS_LDAP_CERT_PATH";
2047 break;
2048 default: /* Version 2 */
2049 certPathAttr = "NS_LDAP_HOST_CERTPATH";
2050 break;
2051 }
2052
2053 if (__s_api_get_versiontype(cfg,
2054 certPathAttr,
2055 &type) == 0 &&
2056 (ret_code = __ns_ldap_setParamValue(cfg,
2057 type,
2058 sa_conf->SA_CERT_PATH,
2059 errorp)) != NS_LDAP_SUCCESS) {
2060 __s_api_destroy_config(cfg);
2061 return (ret_code);
2062 }
2063 }
2064
2065 if (sa_conf->SA_BIND_DN != NULL &&
2066 sa_conf->SA_BIND_PWD != NULL) {
2067 char *authMethods;
2068
2069 authMethods = __s_api_strValue(cfg, NS_LDAP_AUTH_P,
2070 NS_FILE_FMT);
2071 if (authMethods != NULL &&
2072 strstr(authMethods, "sasl/GSSAPI") != NULL) {
2073 /*
2074 * The received DUAProfile specifies
2075 * sasl/GSSAPI as an auth. mechanism.
2076 * The bind DN and password will be
2077 * ignored.
2078 */
2079 syslog(LOG_INFO, gettext("sasl/GSSAPI will be "
2080 "used as an authentication method. "
2081 "The bind DN and password will "
2082 "be ignored.\n"));
2083 free(authMethods);
2084 break;
2085 }
2086
2087 if (authMethods != NULL)
2088 free(authMethods);
2089
2090 if (__ns_ldap_setParamValue(cfg,
2091 NS_LDAP_BINDDN_P,
2092 sa_conf->SA_BIND_DN,
2093 errorp) != NS_LDAP_SUCCESS) {
2094 __s_api_destroy_config(cfg);
2095 return (NS_LDAP_CONFIG);
2096 }
2097
2098 if (__ns_ldap_setParamValue(cfg,
2099 NS_LDAP_BINDPASSWD_P,
2100 sa_conf->SA_BIND_PWD,
2101 errorp) != NS_LDAP_SUCCESS) {
2102 __s_api_destroy_config(cfg);
2103 return (NS_LDAP_CONFIG);
2104 }
2105 }
2106
2107 break;
2108 default: /* NS_CACHEMGR */
2109 return (NS_LDAP_SUCCESS);
2110 }
2111
2112 __s_api_init_config(cfg);
2113 /* Connection management should use the new config now. */
2114 __s_api_reinit_conn_mgmt_new_config(cfg);
2115 __ns_ldap_setServer(TRUE);
2116
2117 (void) mutex_lock(&dir_servers.listReplaceLock);
2118 if ((ret_code = initGlobalList(errorp)) != NS_SUCCESS) {
2119 (void) mutex_unlock(&dir_servers.listReplaceLock);
2120 return (ret_code);
2121 }
2122 dir_servers.standalone = 1;
2123 (void) mutex_unlock(&dir_servers.listReplaceLock);
2124
2125 return (NS_LDAP_SUCCESS);
2126 }
2127
2128 /*
2129 * INPUT:
2130 * serverAddr is the address of a server and
2131 * request is one of the following:
2132 * NS_CACHE_NEW: get a new server address, addr is ignored.
2133 * NS_CACHE_NORESP: get the next one, remove addr from list.
2134 * NS_CACHE_NEXT: get the next one, keep addr on list.
2135 * NS_CACHE_WRITE: get a non-replica server, if possible, if not, same
2136 * as NS_CACHE_NEXT.
2137 * addrType:
2138 * NS_CACHE_ADDR_IP: return server address as is, this is default.
2139 * NS_CACHE_ADDR_HOSTNAME: return server addess as FQDN format, only
2140 * self credential case requires such format.
2141 * OUTPUT:
2142 * ret
2143 *
2144 * a structure of type ns_server_info_t containing the server address
2145 * or name, server controls and supported SASL mechanisms.
2146 * NOTE: Caller should allocate space for the structure and free
2147 * all the space allocated by the function for the information contained
2148 * in the structure.
2149 *
2150 * error - an error object describing an error, if any.
2151 */
2152 ns_ldap_return_code
2153 __s_api_findRootDSE(const char *request,
2154 const char *serverAddr,
2155 const char *addrType,
2156 ns_server_info_t *ret,
2157 ns_ldap_error_t **error)
2158 {
2159 dir_server_list_t *current_list = NULL;
2160 ns_ldap_return_code ret_code;
2161 long i = 0;
2162 int matched = FALSE;
2163 dir_server_t *server = NULL;
2164 char errmsg[MAXERROR];
2165
2166 (void) mutex_lock(&dir_servers.listReplaceLock);
2167 if (dir_servers.list == NULL) {
2168 (void) mutex_unlock(&dir_servers.listReplaceLock);
2169 (void) snprintf(errmsg,
2170 sizeof (errmsg),
2171 gettext("The list of root DSEs is empty: "
2172 "the Standalone mode was not properly initialized"));
2173 MKERROR(LOG_ERR,
2174 *error,
2175 NS_CONFIG_NOTLOADED,
2176 strdup(errmsg),
2177 NS_LDAP_MEMORY);
2178 return (NS_LDAP_INTERNAL);
2179 }
2180
2181 current_list = dir_servers.list;
2182 (void) rw_rdlock(¤t_list->listDestroyLock);
2183 (void) mutex_unlock(&dir_servers.listReplaceLock);
2184
2185 /*
2186 * The code below is mostly the clone of the
2187 * ldap_cachemgr::cachemgr_getldap.c::getldap_get_serverInfo() function.
2188 * Currently we have two different server lists: one is maintained
2189 * by libsldap ('standalone' mode), the other is in ldap_cachemgr
2190 * (a part of its standard functionality).
2191 */
2192
2193 /*
2194 * If NS_CACHE_NEW, or the server info is new,
2195 * starts from the beginning of the list.
2196 */
2197 (void) mutex_lock(¤t_list->nsServers[0]->updateStatus);
2198 if (strcmp(request, NS_CACHE_NEW) == 0 ||
2199 current_list->nsServers[0]->info == INFO_STATUS_NEW) {
2200 matched = TRUE;
2201 }
2202 (void) mutex_unlock(¤t_list->nsServers[i]->updateStatus);
2203
2204 for (i = 0; current_list->nsServers[i]; ++i) {
2205 /*
2206 * Lock the updateStatus mutex to
2207 * make sure the server status stays the same
2208 * while the data is being processed.
2209 */
2210 if (matched == FALSE &&
2211 strcmp(current_list->nsServers[i]->ip,
2212 serverAddr) == 0) {
2213 matched = TRUE;
2214 if (strcmp(request, NS_CACHE_NORESP) == 0) {
2215
2216 /*
2217 * if the server has already been removed,
2218 * don't bother.
2219 */
2220 (void) mutex_lock(¤t_list->
2221 nsServers[i]->updateStatus);
2222 if (current_list->nsServers[i]->status ==
2223 INFO_SERVER_REMOVED) {
2224 (void) mutex_unlock(¤t_list->
2225 nsServers[i]->
2226 updateStatus);
2227 continue;
2228 }
2229 (void) mutex_unlock(¤t_list->
2230 nsServers[i]->
2231 updateStatus);
2232
2233 /*
2234 * if the information is new,
2235 * give this server one more chance.
2236 */
2237 (void) mutex_lock(¤t_list->
2238 nsServers[i]->
2239 updateStatus);
2240 if (current_list->nsServers[i]->info ==
2241 INFO_STATUS_NEW &&
2242 current_list->nsServers[i]->status ==
2243 INFO_SERVER_UP) {
2244 server = current_list->nsServers[i];
2245 (void) mutex_unlock(¤t_list->
2246 nsServers[i]->
2247 updateStatus);
2248 break;
2249 } else {
2250 /*
2251 * it is recommended that
2252 * before removing the
2253 * server from the list,
2254 * the server should be
2255 * contacted one more time
2256 * to make sure that it is
2257 * really unavailable.
2258 * For now, just trust the client
2259 * (i.e., the sldap library)
2260 * that it knows what it is
2261 * doing and would not try
2262 * to mess up the server
2263 * list.
2264 */
2265 current_list->nsServers[i]->status =
2266 INFO_SERVER_REMOVED;
2267 (void) mutex_unlock(¤t_list->
2268 nsServers[i]->
2269 updateStatus);
2270 continue;
2271 }
2272 } else {
2273 /*
2274 * req == NS_CACHE_NEXT or NS_CACHE_WRITE
2275 */
2276 continue;
2277 }
2278 }
2279
2280 if (matched) {
2281 if (strcmp(request, NS_CACHE_WRITE) == 0) {
2282 /*
2283 * ldap_cachemgr checks here if the server
2284 * is not a non-replica server (a server
2285 * of type INFO_RW_WRITEABLE). But currently
2286 * it considers all the servers in its list
2287 * as those.
2288 */
2289 (void) mutex_lock(¤t_list->
2290 nsServers[i]->
2291 updateStatus);
2292 if (current_list->nsServers[i]->status ==
2293 INFO_SERVER_UP) {
2294 (void) mutex_unlock(¤t_list->
2295 nsServers[i]->
2296 updateStatus);
2297 server = current_list->nsServers[i];
2298 break;
2299 }
2300 } else {
2301 (void) mutex_lock(¤t_list->
2302 nsServers[i]->
2303 updateStatus);
2304 if (current_list->nsServers[i]->status ==
2305 INFO_SERVER_UP) {
2306 (void) mutex_unlock(¤t_list->
2307 nsServers[i]->
2308 updateStatus);
2309 server = current_list->nsServers[i];
2310 break;
2311 }
2312 }
2313
2314 (void) mutex_unlock(¤t_list->
2315 nsServers[i]->
2316 updateStatus);
2317 }
2318 }
2319
2320 if (server == NULL) {
2321 (void) rw_unlock(¤t_list->listDestroyLock);
2322 (void) snprintf(errmsg,
2323 sizeof (errmsg),
2324 gettext("No servers are available"));
2325 MKERROR(LOG_ERR,
2326 *error,
2327 NS_CONFIG_NOTLOADED,
2328 strdup(errmsg),
2329 NS_LDAP_MEMORY);
2330 return (NS_LDAP_NOTFOUND);
2331 }
2332
2333 (void) mutex_lock(&server->updateStatus);
2334 server->info = INFO_STATUS_OLD;
2335 (void) mutex_unlock(&server->updateStatus);
2336
2337 if (ret == NULL) {
2338 (void) rw_unlock(¤t_list->listDestroyLock);
2339 return (NS_LDAP_SUCCESS);
2340 }
2341
2342 if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
2343 ret_code = __s_api_ip2hostname(server->ip, &ret->serverFQDN);
2344 if (ret_code != NS_LDAP_SUCCESS) {
2345 (void) snprintf(errmsg,
2346 sizeof (errmsg),
2347 gettext("The %s address "
2348 "can not be resolved into "
2349 "a host name. Returning "
2350 "the address as it is."),
2351 server->ip);
2352 MKERROR(LOG_ERR,
2353 *error,
2354 NS_CONFIG_NOTLOADED,
2355 strdup(errmsg),
2356 NS_LDAP_MEMORY);
2357 return (NS_LDAP_INTERNAL);
2358 }
2359 }
2360
2361 ret->server = strdup(server->ip);
2362
2363 ret->controls = __s_api_cp2dArray(server->controls);
2364 ret->saslMechanisms = __s_api_cp2dArray(server->saslMech);
2365
2366 (void) rw_unlock(¤t_list->listDestroyLock);
2367
2368 return (NS_LDAP_SUCCESS);
2369 }
2370
2371 /*
2372 * This function iterates through the list of the configured LDAP servers
2373 * and "pings" those which are marked as removed or if any error occurred
2374 * during the previous receiving of the server's root DSE. If the
2375 * function is able to reach such a server and get its root DSE, it
2376 * marks the server as on-line. Otherwise, the server's status is set
2377 * to "Error".
2378 * For each server the function tries to connect to, it fires up
2379 * a separate thread and then waits until all the treads finish.
2380 * The function returns NS_LDAP_INTERNAL if the Standalone mode was not
2381 * initialized or was canceled prior to an invocation of
2382 * __ns_ldap_pingOfflineServers().
2383 */
2384 ns_ldap_return_code
2385 __ns_ldap_pingOfflineServers(void)
2386 {
2387 dir_server_list_t *current_list = NULL;
2388 ns_ldap_return_code retCode = NS_LDAP_SUCCESS;
2389 long srvListLength, i = 0;
2390 thread_t *thrPool, thrID;
2391 void *status = NULL;
2392
2393 (void) mutex_lock(&dir_servers.listReplaceLock);
2394 if (dir_servers.list == NULL) {
2395 (void) mutex_unlock(&dir_servers.listReplaceLock);
2396 return (NS_LDAP_INTERNAL);
2397 }
2398
2399 current_list = dir_servers.list;
2400 (void) rw_wrlock(¤t_list->listDestroyLock);
2401 (void) mutex_unlock(&dir_servers.listReplaceLock);
2402
2403 while (current_list->nsServers[i] != NULL) {
2404 ++i;
2405 }
2406 srvListLength = i;
2407
2408 thrPool = calloc(srvListLength, sizeof (thread_t));
2409 if (thrPool == NULL) {
2410 (void) rw_unlock(¤t_list->listDestroyLock);
2411 return (NS_LDAP_MEMORY);
2412 }
2413
2414 for (i = 0; i < srvListLength; ++i) {
2415 if (current_list->nsServers[i]->status != INFO_SERVER_REMOVED &&
2416 current_list->nsServers[i]->status != INFO_SERVER_ERROR) {
2417 continue;
2418 }
2419 current_list->nsServers[i]->status = INFO_SERVER_CONNECTING;
2420 current_list->nsServers[i]->info = INFO_STATUS_NEW;
2421
2422 __s_api_free2dArray(current_list->nsServers[i]->controls);
2423 current_list->nsServers[i]->controls = NULL;
2424 __s_api_free2dArray(current_list->nsServers[i]->saslMech);
2425 current_list->nsServers[i]->saslMech = NULL;
2426
2427 switch (thr_create(NULL,
2428 0,
2429 create_ns_servers_entry,
2430 current_list->nsServers[i],
2431 0,
2432 &thrID)) {
2433 case EAGAIN:
2434 current_list->nsServers[i]->status = INFO_SERVER_ERROR;
2435 continue;
2436 case ENOMEM:
2437 current_list->nsServers[i]->status = INFO_SERVER_ERROR;
2438 retCode = NS_LDAP_MEMORY;
2439 break;
2440 default:
2441 thrPool[i] = thrID;
2442 continue;
2443 }
2444 /* A memory allocation error has occured */
2445 break;
2446
2447 }
2448
2449 for (i = 0; i < srvListLength; ++i) {
2450 if (thrPool[i] != 0 &&
2451 thr_join(thrPool[i], NULL, &status) == 0) {
2452 if (status == NULL) {
2453 current_list->nsServers[i]->status =
2454 INFO_SERVER_ERROR;
2455 retCode = NS_LDAP_MEMORY;
2456 }
2457 free(status);
2458 }
2459 }
2460
2461 (void) rw_unlock(¤t_list->listDestroyLock);
2462
2463 free(thrPool);
2464
2465 return (retCode);
2466 }