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