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, &paramVal, errorp);
1406 	if (paramVal != NULL && *paramVal != NULL) {
1407 		tv.tv_sec = **((int **)paramVal);
1408 		(void) __ns_ldap_freeParam(&paramVal);
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(&current_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(&current_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(&current_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(&current_list->
2221 				    nsServers[i]->updateStatus);
2222 				if (current_list->nsServers[i]->status ==
2223 				    INFO_SERVER_REMOVED) {
2224 					(void) mutex_unlock(&current_list->
2225 					    nsServers[i]->
2226 					    updateStatus);
2227 					continue;
2228 				}
2229 				(void) mutex_unlock(&current_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(&current_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(&current_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(&current_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(&current_list->
2290 				    nsServers[i]->
2291 				    updateStatus);
2292 				if (current_list->nsServers[i]->status  ==
2293 				    INFO_SERVER_UP) {
2294 					(void) mutex_unlock(&current_list->
2295 					    nsServers[i]->
2296 					    updateStatus);
2297 					server = current_list->nsServers[i];
2298 					break;
2299 				}
2300 			} else {
2301 				(void) mutex_lock(&current_list->
2302 				    nsServers[i]->
2303 				    updateStatus);
2304 				if (current_list->nsServers[i]->status ==
2305 				    INFO_SERVER_UP) {
2306 					(void) mutex_unlock(&current_list->
2307 					    nsServers[i]->
2308 					    updateStatus);
2309 					server = current_list->nsServers[i];
2310 					break;
2311 				}
2312 			}
2313 
2314 			(void) mutex_unlock(&current_list->
2315 			    nsServers[i]->
2316 			    updateStatus);
2317 		}
2318 	}
2319 
2320 	if (server == NULL) {
2321 		(void) rw_unlock(&current_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(&current_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(&current_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(&current_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(&current_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(&current_list->listDestroyLock);
2462 
2463 	free(thrPool);
2464 
2465 	return (retCode);
2466 }
2467