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