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