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