xref: /illumos-gate/usr/src/lib/libsldap/common/ns_connect.c (revision 29680e551a97367a1d58ec0c94a967aa81fac54a)
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 #include <stdlib.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <synch.h>
33 #include <time.h>
34 #include <libintl.h>
35 #include <thread.h>
36 #include <syslog.h>
37 #include <sys/mman.h>
38 #include <nsswitch.h>
39 #include <nss_dbdefs.h>
40 #include "solaris-priv.h"
41 #include "solaris-int.h"
42 #include "ns_sldap.h"
43 #include "ns_internal.h"
44 #include "ns_cache_door.h"
45 #include "ldappr.h"
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <procfs.h>
49 #include <unistd.h>
50 
51 extern unsigned int _sleep(unsigned int);
52 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
53 		LDAPControl **, LDAPControl **);
54 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
55 
56 static int openConnection(LDAP **, const char *, const ns_cred_t *,
57 		int, ns_ldap_error_t **, int, int);
58 static void
59 _DropConnection(ConnectionID cID, int flag, int fini);
60 /*
61  * sessionLock, wait4session, sessionTid
62  * are variables to synchronize the creation/retrieval of a connection.
63  * MTperCon is a flag to enable/disable multiple threads sharing the same
64  * connection.
65  * sessionPoolLock is a mutex lock for the connection pool.
66  * sharedConnNumber is the number of sharable connections in the pool.
67  * sharedConnNumberLock is a mutex for sharedConnNumber.
68  */
69 static mutex_t	sessionLock = DEFAULTMUTEX;
70 static int	wait4session = 0;
71 static thread_t sessionTid = 0;
72 int	MTperConn = 1;
73 static rwlock_t sessionPoolLock = DEFAULTRWLOCK;
74 
75 static Connection **sessionPool = NULL;
76 static int sessionPoolSize = 0;
77 static int sharedConnNumber = 0;
78 static mutex_t sharedConnNumberLock = DEFAULTMUTEX;
79 
80 
81 static mutex_t	nscdLock = DEFAULTMUTEX;
82 static int	nscdChecked = 0;
83 static pid_t	checkedPid = -1;
84 static int	isNscd = 0;
85 /*
86  * SSF values are for SASL integrity & privacy.
87  * JES DS5.2 does not support this feature but DS6 does.
88  * The values between 0 and 65535 can work with both server versions.
89  */
90 #define	MAX_SASL_SSF	65535
91 #define	MIN_SASL_SSF	0
92 
93 /* Number of hostnames to allocate memory for */
94 #define	NUMTOMALLOC	32
95 /*
96  * ns_mtckey is for sharing a ldap connection among multiple
97  * threads; created by ns_ldap_init() in ns_init.c
98  */
99 extern thread_key_t ns_mtckey;
100 
101 /* Per thread LDAP error resides in thread-specific data. */
102 struct ldap_error {
103 	int	le_errno;
104 	char	*le_matched;
105 	char	*le_errmsg;
106 };
107 
108 static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
109 
110 /* destructor */
111 void
112 ns_tsd_cleanup(void *key) {
113 	struct ldap_error *le = (struct ldap_error *)key;
114 
115 	if (le == NULL)
116 		return;
117 	if (le->le_matched != NULL) {
118 		ldap_memfree(le->le_matched);
119 	}
120 	if (le->le_errmsg != NULL) {
121 		ldap_memfree(le->le_errmsg);
122 	}
123 	free(le);
124 }
125 
126 /* Callback function for allocating a mutex */
127 static void *
128 ns_mutex_alloc(void)
129 {
130 	mutex_t *mutexp = NULL;
131 
132 	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
133 		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
134 			free(mutexp);
135 			mutexp = NULL;
136 		}
137 	}
138 	return (mutexp);
139 }
140 
141 /* Callback function for freeing a mutex */
142 static void
143 ns_mutex_free(void *mutexp)
144 {
145 	(void) mutex_destroy((mutex_t *)mutexp);
146 	free(mutexp);
147 }
148 
149 /*
150  * Function for setting up thread-specific data
151  * where per thread LDAP error is stored
152  */
153 static int
154 tsd_setup()
155 {
156 	void	*tsd;
157 	int	rc;
158 
159 	/* return success if TSD already set */
160 	rc = thr_getspecific(ns_mtckey, &tsd);
161 	if (rc == 0 && tsd != NULL)
162 		return (0);
163 
164 	/* allocate and set TSD */
165 	tsd = (void *) calloc(1, sizeof (struct ldap_error));
166 	if (tsd == NULL)
167 		return (-1);
168 	rc = thr_setspecific(ns_mtckey, tsd);
169 	if (rc != 0) { /* must be ENOMEM */
170 		free(tsd);
171 		return (-1);
172 	}
173 	return (0);
174 
175 
176 }
177 
178 /* Callback function for setting the per thread LDAP error */
179 /*ARGSUSED*/
180 static void
181 set_ld_error(int err, char *matched, char *errmsg, void *dummy)
182 {
183 	struct ldap_error	*le;
184 
185 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
186 		syslog(LOG_ERR, "set_ld_error: thr_getspecific failed. errno"
187 		    " %d", errno);
188 		return;
189 	}
190 
191 	/* play safe, do nothing if TSD pointer is NULL */
192 	if (le == NULL)
193 		return;
194 
195 	le->le_errno = err;
196 	if (le->le_matched != NULL) {
197 		ldap_memfree(le->le_matched);
198 	}
199 	le->le_matched = matched;
200 	if (le->le_errmsg != NULL) {
201 		ldap_memfree(le->le_errmsg);
202 	}
203 	le->le_errmsg = errmsg;
204 }
205 
206 int
207 /* check and allocate the thread-specific data for using a shared connection */
208 __s_api_check_MTC_tsd()
209 {
210 	if (tsd_setup() != 0)
211 		return (NS_LDAP_MEMORY);
212 
213 	return (NS_LDAP_SUCCESS);
214 }
215 
216 /* Callback function for getting the per thread LDAP error */
217 /*ARGSUSED*/
218 static int
219 get_ld_error(char **matched, char **errmsg, void *dummy)
220 {
221 	struct ldap_error	*le;
222 
223 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
224 		syslog(LOG_ERR, "get_ld_error: thr_getspecific failed. errno"
225 		    " %d", errno);
226 		return (errno);
227 	}
228 
229 	/* play safe, return NULL error data, if TSD pointer is NULL */
230 	if (le == NULL)
231 		le = &ldap_error_NULL;
232 
233 	if (matched != NULL) {
234 		*matched = le->le_matched;
235 	}
236 	if (errmsg != NULL) {
237 		*errmsg = le->le_errmsg;
238 	}
239 	return (le->le_errno);
240 }
241 
242 /* Callback function for setting per thread errno */
243 static void
244 set_errno(int err)
245 {
246 	errno = err;
247 }
248 
249 /* Callback function for getting per thread errno */
250 static int
251 get_errno(void)
252 {
253 	return (errno);
254 }
255 
256 /*
257  * set up to allow multiple threads to use the same ldap connection
258  */
259 static int
260 setup_mt_conn(LDAP *ld)
261 {
262 
263 	struct ldap_thread_fns		tfns;
264 	struct ldap_extra_thread_fns	extrafns;
265 	int				rc;
266 
267 	/*
268 	 * Set the function pointers for dealing with mutexes
269 	 * and error information
270 	 */
271 	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
272 	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
273 	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
274 	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
275 	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
276 	tfns.ltf_get_errno = get_errno;
277 	tfns.ltf_set_errno = set_errno;
278 	tfns.ltf_get_lderrno = get_ld_error;
279 	tfns.ltf_set_lderrno = set_ld_error;
280 	tfns.ltf_lderrno_arg = NULL;
281 
282 	/*
283 	 * Set up this session to use those function pointers
284 	 */
285 	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
286 	    (void *) &tfns);
287 	if (rc < 0) {
288 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
289 		"(LDAP_OPT_THREAD_FN_PTRS)");
290 		return (-1);
291 	}
292 
293 	/*
294 	 * Set the function pointers for working with semaphores
295 	 */
296 	(void) memset(&extrafns, '\0',
297 	    sizeof (struct ldap_extra_thread_fns));
298 	extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
299 	extrafns.ltf_mutex_trylock = NULL;
300 	extrafns.ltf_sema_alloc = NULL;
301 	extrafns.ltf_sema_free = NULL;
302 	extrafns.ltf_sema_wait = NULL;
303 	extrafns.ltf_sema_post = NULL;
304 
305 	/* Set up this session to use those function pointers */
306 	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
307 	    (void *) &extrafns);
308 	if (rc < 0) {
309 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
310 		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)");
311 		return (-1);
312 	}
313 
314 	return (0);
315 }
316 
317 static void
318 ns_setup_mt_conn_and_tsd(LDAP *ld) {
319 	thread_t t = thr_self();
320 	void *tsd;
321 	/* set up to share this connection among threads */
322 	if (MTperConn == 1) {
323 		if (tsd_setup() == -1) {
324 			syslog(LOG_ERR, "tid= %d: unable "
325 				"to set up TSD\n", t);
326 		} else {
327 			if (setup_mt_conn(ld) == -1) {
328 			/* multiple threads per connection not supported */
329 				syslog(LOG_ERR, "tid= %d: multiple "
330 					"threads per connection not "
331 					"supported\n", t);
332 				(void) thr_getspecific(ns_mtckey, &tsd);
333 				ns_tsd_cleanup(tsd);
334 				(void) thr_setspecific(ns_mtckey, NULL);
335 				MTperConn = 0;
336 			}
337 		}
338 	}
339 }
340 
341 /*
342  * Check /proc/PID/psinfo to see if this process is nscd
343  * If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce
344  * constant reconnects for many operations.
345  * A more complete solution is to develop true connection pooling.
346  * However, this is much better than a new connection for every request.
347  */
348 int
349 __s_api_nscd_proc(void)
350 {
351 	pid_t		my_pid;
352 	psinfo_t	pinfo;
353 	char		fname[BUFSIZ];
354 	int		ret;
355 	int		fd;
356 
357 	/* Don't bother checking if this process isn't root. */
358 	/* It can't be nscd */
359 	if (getuid() != 0)
360 		return (0);
361 
362 	my_pid = getpid();
363 	if (nscdChecked && (my_pid == checkedPid)) {
364 		return (isNscd);
365 	}
366 	(void) mutex_lock(&nscdLock);
367 	if (nscdChecked && (my_pid == checkedPid)) {
368 		(void) mutex_unlock(&nscdLock);
369 		return (isNscd);
370 	}
371 	nscdChecked = 1;
372 	checkedPid = my_pid;
373 	isNscd = 0;
374 	if (snprintf(fname, BUFSIZ, "/proc/%d/psinfo", my_pid) != 0) {
375 		if ((fd = open(fname,  O_RDONLY)) > 0) {
376 			ret = read(fd, &pinfo, sizeof (psinfo_t));
377 			(void) close(fd);
378 			if (ret == sizeof (psinfo_t) &&
379 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
380 				/* process runs as root and is named nscd */
381 				/* that's good enough for now */
382 				isNscd = 1;
383 			}
384 		}
385 	}
386 	(void) mutex_unlock(&nscdLock);
387 	return (isNscd);
388 }
389 
390 /*
391  * This function requests a server from the cache manager through
392  * the door functionality
393  */
394 
395 static int
396 __s_api_requestServer(const char *request, const char *server,
397 	ns_server_info_t *ret, ns_ldap_error_t **error,  const char *addrType)
398 {
399 	union {
400 		ldap_data_t	s_d;
401 		char		s_b[DOORBUFFERSIZE];
402 	} space;
403 	ldap_data_t	*sptr;
404 	int		ndata;
405 	int		adata;
406 	char		errstr[MAXERROR];
407 	const char	*ireq;
408 	char		*rbuf, *ptr, *rest;
409 	char		*dptr;
410 	char		**mptr, **mptr1, **cptr, **cptr1;
411 	int		mcnt, ccnt;
412 	char		**servers;
413 	int		rc, len;
414 
415 	if (ret == NULL || error == NULL) {
416 		return (NS_LDAP_OP_FAILED);
417 	}
418 	(void) memset(ret, 0, sizeof (ns_server_info_t));
419 	*error = NULL;
420 
421 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
422 
423 	if (request == NULL)
424 		ireq = NS_CACHE_NEW;
425 	else
426 		ireq = request;
427 
428 	adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1);
429 	if (server != NULL) {
430 		adata += strlen(DOORLINESEP) + 1;
431 		adata += strlen(server) + 1;
432 	}
433 	ndata = sizeof (space);
434 	len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
435 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
436 	if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
437 		return (NS_LDAP_MEMORY);
438 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >=
439 	    len)
440 		return (NS_LDAP_MEMORY);
441 	if (server != NULL) {
442 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
443 		    DOORLINESEP, len) >= len)
444 			return (NS_LDAP_MEMORY);
445 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server,
446 		    len) >= len)
447 			return (NS_LDAP_MEMORY);
448 	}
449 	sptr = &space.s_d;
450 
451 	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
452 	case SUCCESS:
453 		break;
454 	/* this case is for when the $mgr is not running, but ldapclient */
455 	/* is trying to initialize things */
456 	case NOSERVER:
457 		/* get first server from config list unavailable otherwise */
458 		servers = NULL;
459 		rc = __s_api_getServers(&servers, error);
460 		if (rc != NS_LDAP_SUCCESS) {
461 			if (servers != NULL) {
462 				__s_api_free2dArray(servers);
463 				servers = NULL;
464 			}
465 			return (rc);
466 		}
467 		if (servers == NULL || servers[0] == NULL) {
468 			__s_api_free2dArray(servers);
469 			servers = NULL;
470 			(void) sprintf(errstr,
471 			    gettext("No server found in configuration"));
472 			MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
473 			    strdup(errstr), NULL);
474 			return (NS_LDAP_CONFIG);
475 		}
476 		ret->server = strdup(servers[0]);
477 		if (ret->server == NULL) {
478 			__s_api_free2dArray(servers);
479 			return (NS_LDAP_MEMORY);
480 		}
481 		ret->saslMechanisms = NULL;
482 		ret->controls = NULL;
483 		__s_api_free2dArray(servers);
484 		servers = NULL;
485 		return (NS_LDAP_SUCCESS);
486 	case NOTFOUND:
487 	default:
488 		return (NS_LDAP_OP_FAILED);
489 	}
490 
491 	/* copy info from door call return structure here */
492 	rbuf =  space.s_d.ldap_ret.ldap_u.config;
493 
494 	/* Get the host */
495 	ptr = strtok_r(rbuf, DOORLINESEP, &rest);
496 	if (ptr == NULL) {
497 		(void) sprintf(errstr, gettext("No server returned from "
498 		    "ldap_cachemgr"));
499 		MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
500 		    strdup(errstr), NULL);
501 		return (NS_LDAP_OP_FAILED);
502 	}
503 	ret->server = strdup(ptr);
504 	if (ret->server == NULL) {
505 		return (NS_LDAP_MEMORY);
506 	}
507 	/* Get the host FQDN format */
508 	if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
509 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
510 		if (ptr == NULL) {
511 			(void) sprintf(errstr, gettext("No server FQDN format "
512 			    "returned from ldap_cachemgr"));
513 			MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
514 			    strdup(errstr), NULL);
515 			free(ret->server);
516 			ret->server = NULL;
517 			return (NS_LDAP_OP_FAILED);
518 		}
519 		ret->serverFQDN = strdup(ptr);
520 		if (ret->serverFQDN == NULL) {
521 			free(ret->server);
522 			ret->server = NULL;
523 			return (NS_LDAP_MEMORY);
524 		}
525 	}
526 
527 	/* get the Supported Controls/SASL mechs */
528 	mptr = NULL;
529 	mcnt = 0;
530 	cptr = NULL;
531 	ccnt = 0;
532 	for (; ; ) {
533 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
534 		if (ptr == NULL)
535 			break;
536 		if (strncasecmp(ptr, _SASLMECHANISM,
537 		    _SASLMECHANISM_LEN) == 0) {
538 			dptr = strchr(ptr, '=');
539 			if (dptr == NULL)
540 				continue;
541 			dptr++;
542 			mptr1 = (char **)realloc((void *)mptr,
543 			    sizeof (char *) * (mcnt+2));
544 			if (mptr1 == NULL) {
545 				__s_api_free2dArray(mptr);
546 				if (sptr != &space.s_d) {
547 					(void) munmap((char *)sptr, ndata);
548 				}
549 				__s_api_free2dArray(cptr);
550 				__s_api_free_server_info(ret);
551 				return (NS_LDAP_MEMORY);
552 			}
553 			mptr = mptr1;
554 			mptr[mcnt] = strdup(dptr);
555 			if (mptr[mcnt] == NULL) {
556 				if (sptr != &space.s_d) {
557 					(void) munmap((char *)sptr, ndata);
558 				}
559 				__s_api_free2dArray(cptr);
560 				cptr = NULL;
561 				__s_api_free2dArray(mptr);
562 				mptr = NULL;
563 				__s_api_free_server_info(ret);
564 				return (NS_LDAP_MEMORY);
565 			}
566 			mcnt++;
567 			mptr[mcnt] = NULL;
568 		}
569 		if (strncasecmp(ptr, _SUPPORTEDCONTROL,
570 		    _SUPPORTEDCONTROL_LEN) == 0) {
571 			dptr = strchr(ptr, '=');
572 			if (dptr == NULL)
573 				continue;
574 			dptr++;
575 			cptr1 = (char **)realloc((void *)cptr,
576 			    sizeof (char *) * (ccnt+2));
577 			if (cptr1 == NULL) {
578 				if (sptr != &space.s_d) {
579 					(void) munmap((char *)sptr, ndata);
580 				}
581 				__s_api_free2dArray(cptr);
582 				__s_api_free2dArray(mptr);
583 				mptr = NULL;
584 				__s_api_free_server_info(ret);
585 				return (NS_LDAP_MEMORY);
586 			}
587 			cptr = cptr1;
588 			cptr[ccnt] = strdup(dptr);
589 			if (cptr[ccnt] == NULL) {
590 				if (sptr != &space.s_d) {
591 					(void) munmap((char *)sptr, ndata);
592 				}
593 				__s_api_free2dArray(cptr);
594 				cptr = NULL;
595 				__s_api_free2dArray(mptr);
596 				mptr = NULL;
597 				__s_api_free_server_info(ret);
598 				return (NS_LDAP_MEMORY);
599 			}
600 			ccnt++;
601 			cptr[ccnt] = NULL;
602 		}
603 	}
604 	if (mptr != NULL) {
605 		ret->saslMechanisms = mptr;
606 	}
607 	if (cptr != NULL) {
608 		ret->controls = cptr;
609 	}
610 
611 
612 	/* clean up door call */
613 	if (sptr != &space.s_d) {
614 		(void) munmap((char *)sptr, ndata);
615 	}
616 	*error = NULL;
617 
618 	return (NS_LDAP_SUCCESS);
619 }
620 
621 
622 /*
623  * printCred(): prints the credential structure
624  */
625 static void
626 printCred(int pri, const ns_cred_t *cred)
627 {
628 	thread_t	t = thr_self();
629 
630 	if (cred == NULL) {
631 		syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t);
632 		return;
633 	}
634 
635 	syslog(pri, "tid= %d: AuthType=%d", t, cred->auth.type);
636 	syslog(pri, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
637 	syslog(pri, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
638 	syslog(pri, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
639 	if (cred->hostcertpath)
640 		syslog(pri, "tid= %d: hostCertPath=%s\n",
641 		    t, cred->hostcertpath);
642 	if (cred->cred.unix_cred.userID)
643 		syslog(pri, "tid= %d: userID=%s\n",
644 		    t, cred->cred.unix_cred.userID);
645 #ifdef DEBUG
646 	if (cred->cred.unix_cred.passwd)
647 		syslog(pri, "tid= %d: passwd=%s\n",
648 		    t, cred->cred.unix_cred.passwd);
649 #endif
650 }
651 
652 /*
653  * printConnection(): prints the connection structure
654  */
655 static void
656 printConnection(int pri, Connection *con)
657 {
658 	thread_t	t = thr_self();
659 
660 	if (con == NULL)
661 		return;
662 
663 	syslog(pri, "tid= %d: connectionID=%d\n", t, con->connectionId);
664 	syslog(pri, "tid= %d: shared=%d\n", t, con->shared);
665 	syslog(pri, "tid= %d: usedBit=%d\n", t, con->usedBit);
666 	syslog(pri, "tid= %d: threadID=%d\n", t, con->threadID);
667 	if (con->serverAddr) {
668 		syslog(pri, "tid= %d: serverAddr=%s\n",
669 		    t, con->serverAddr);
670 	}
671 	printCred(pri, con->auth);
672 }
673 
674 
675 
676 /*
677  * addConnection(): set up a connection so that it can be shared
678  * among multiple threads and then insert the connection in the
679  * connection list.
680  * Returns: -1 = failure, new Connection ID = success
681  *
682  * This function could exit with sessionLock locked. It will be
683  * be unlocked in __s_api_getConnection() when it exits without getting a
684  * connection.
685  */
686 static int
687 addConnection(Connection *con)
688 {
689 	int i, noMTperC = 0;
690 	thread_t t = thr_self();
691 	struct ldap_thread_fns tfns;
692 	void *tsd;
693 
694 	if (!con)
695 		return (-1);
696 
697 	syslog(LOG_DEBUG, "tid= %d: Adding connection (serverAddr=%s)",
698 	    t, con->serverAddr);
699 
700 	if (MTperConn == 1) {
701 		/*
702 		 * Make sure ld has proper thread functions and tsd
703 		 * is set up.
704 		 */
705 		(void) memset(&tfns, 0, sizeof (struct ldap_thread_fns));
706 		/*
707 		 * ldap_init sets ltf_get_lderrno and ltf_set_lderrno to NULLs.
708 		 * It's supposed to be overwritten by ns_setup_mt_conn_and_tsd.
709 		 */
710 		if (ldap_get_option(con->ld, LDAP_OPT_THREAD_FN_PTRS,
711 		    (void *)&tfns) != 0 ||
712 		    tfns.ltf_get_lderrno != get_ld_error ||
713 		    tfns.ltf_set_lderrno != set_ld_error) {
714 			MTperConn = 0;
715 			noMTperC = 1;
716 		} else {
717 			if (thr_getspecific(ns_mtckey, &tsd) != 0 ||
718 			    tsd == NULL)
719 				noMTperC = 1;
720 		}
721 
722 	} else {
723 		noMTperC = 1;
724 	}
725 
726 	(void) rw_wrlock(&sessionPoolLock);
727 	if (sessionPool == NULL) {
728 		sessionPoolSize = SESSION_CACHE_INC;
729 		sessionPool = calloc(sessionPoolSize,
730 		    sizeof (struct connection **));
731 		if (!sessionPool) {
732 			(void) rw_unlock(&sessionPoolLock);
733 			return (-1);
734 		}
735 
736 		syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t);
737 	}
738 	for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
739 		;
740 	if (i == sessionPoolSize) {
741 		/* run out of array, need to increase sessionPool */
742 		Connection **cl;
743 		cl = (Connection **) realloc(sessionPool,
744 		    (sessionPoolSize + SESSION_CACHE_INC) *
745 		    sizeof (Connection *));
746 		if (!cl) {
747 			(void) rw_unlock(&sessionPoolLock);
748 			return (-1);
749 		}
750 		(void) memset(cl + sessionPoolSize, 0,
751 		    SESSION_CACHE_INC * sizeof (struct connection *));
752 		sessionPool = cl;
753 		sessionPoolSize += SESSION_CACHE_INC;
754 		syslog(LOG_DEBUG, "tid: %d: Increased "
755 		    "sessionPoolSize to: %d\n",
756 		    t, sessionPoolSize);
757 	}
758 	sessionPool[i] = con;
759 	if (noMTperC == 0) {
760 		con->shared++;
761 		con->pid = getpid();
762 		(void) mutex_lock(&sharedConnNumberLock);
763 		sharedConnNumber++;
764 		(void) mutex_unlock(&sharedConnNumberLock);
765 	} else
766 		con->usedBit = B_TRUE;
767 
768 	(void) rw_unlock(&sessionPoolLock);
769 
770 	con->connectionId = i + CONID_OFFSET;
771 
772 	syslog(LOG_DEBUG, "tid= %d: Connection added [%d]\n",
773 	    t, i);
774 	printConnection(LOG_DEBUG, con);
775 
776 	/*
777 	 * A connection can be shared now, unlock
778 	 * the session mutex and let other
779 	 * threads try to use this connection or
780 	 * get their own.
781 	 */
782 	if (wait4session != 0 && sessionTid == thr_self()) {
783 		wait4session = 0;
784 		sessionTid = 0;
785 		syslog(LOG_DEBUG, "tid= %d: unlocking sessionLock\n", t);
786 		(void) mutex_unlock(&sessionLock);
787 	}
788 
789 	return (i + CONID_OFFSET);
790 }
791 
792 /*
793  * See if the specified session matches a currently available
794  */
795 
796 static int
797 findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
798 	Connection **conp)
799 {
800 	Connection *cp;
801 	int id;
802 
803 	if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
804 		return (-1);
805 
806 	/*
807 	 * If a new connection is requested, no need to continue.
808 	 * If the process is not nscd and is not requesting keep connections
809 	 * alive, no need to continue.
810 	 */
811 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
812 	    !(flags & NS_LDAP_KEEP_CONN)))
813 		return (-1);
814 
815 	*conp = NULL;
816 	if (sessionPool == NULL)
817 		return (-1);
818 	id = cID - CONID_OFFSET;
819 	if (id < 0 || id >= sessionPoolSize)
820 		return (-1);
821 
822 	(void) rw_rdlock(&sessionPoolLock);
823 	if (sessionPool[id] == NULL) {
824 		(void) rw_unlock(&sessionPoolLock);
825 		return (-1);
826 	}
827 	cp = sessionPool[id];
828 
829 	/*
830 	 * Make sure the connection has the same type of authentication method
831 	 */
832 	if ((cp->usedBit) ||
833 	    (cp->notAvail) ||
834 	    (cp->auth->auth.type != auth->auth.type) ||
835 	    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
836 	    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
837 	    (cp->auth->auth.saslopt != auth->auth.saslopt)) {
838 		(void) rw_unlock(&sessionPoolLock);
839 		return (-1);
840 	}
841 	if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
842 	    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
843 	    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
844 	    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
845 	    ((cp->auth->cred.unix_cred.userID == NULL) ||
846 	    (strcasecmp(cp->auth->cred.unix_cred.userID,
847 	    auth->cred.unix_cred.userID) != 0))) {
848 		(void) rw_unlock(&sessionPoolLock);
849 		return (-1);
850 	}
851 
852 	/* An existing connection is found but it needs to be reset */
853 	if (flags & NS_LDAP_NEW_CONN) {
854 		(void) rw_unlock(&sessionPoolLock);
855 		DropConnection(cID, 0);
856 		return (-1);
857 	}
858 	/* found an available connection */
859 	cp->usedBit = B_TRUE;
860 	(void) rw_unlock(&sessionPoolLock);
861 	cp->threadID = thr_self();
862 	*conp = cp;
863 	return (cID);
864 }
865 
866 /*
867  * findConnection(): find an available connection from the list
868  * that matches the criteria specified in Connection structure.
869  * If serverAddr is NULL, then find a connection to any server
870  * as long as it matches the rest of the parameters.
871  * Returns: -1 = failure, the Connection ID found = success.
872  *
873  * This function could exit with sessionLock locked. It will be
874  * be unlocked in addConnection() when this thread adds the connection
875  * to the pool or in __s_api_getConnection() when it exits without getting a
876  * connection.
877  */
878 #define	TRY_TIMES	10
879 static int
880 findConnection(int flags, const char *serverAddr,
881 	const ns_cred_t *auth, Connection **conp)
882 {
883 	Connection *cp;
884 	int i, j, conn_server_index, up_server_index, drop_conn;
885 	int rc;
886 	int try;
887 	ns_server_info_t sinfo;
888 	ns_ldap_error_t *errorp = NULL;
889 	char **servers;
890 	void **paramVal = NULL;
891 #ifdef DEBUG
892 	thread_t t = thr_self();
893 #endif /* DEBUG */
894 
895 	if (auth == NULL || conp == NULL)
896 		return (-1);
897 	*conp = NULL;
898 
899 	/*
900 	 * If a new connection is requested, no need to continue.
901 	 * If the process is not nscd and is not requesting keep connections
902 	 * alive, no need to continue.
903 	 */
904 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
905 	    !(flags & NS_LDAP_KEEP_CONN)))
906 		return (-1);
907 
908 #ifdef DEBUG
909 	(void) fprintf(stderr, "tid= %d: Find connection\n", t);
910 	(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
911 	if (serverAddr && *serverAddr)
912 		(void) fprintf(stderr, "tid= %d: serverAddr=%s\n",
913 		    t, serverAddr);
914 	else
915 		(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
916 	printCred(LOG_DEBUG, auth);
917 	fflush(stderr);
918 #endif /* DEBUG */
919 
920 	/*
921 	 * If multiple threads per connection not supported,
922 	 * no sessionPool means no connection
923 	 */
924 	(void) rw_rdlock(&sessionPoolLock);
925 	if (MTperConn == 0 && sessionPool == NULL) {
926 		(void) rw_unlock(&sessionPoolLock);
927 		return (-1);
928 	}
929 
930 	/*
931 	 * If no sharable connections in cache, then serialize the opening
932 	 * of connections. Make sure only one is being opened
933 	 * at a time. Otherwise, we may end up with more
934 	 * connections than we want (if multiple threads get
935 	 * here at the same time)
936 	 */
937 	(void) mutex_lock(&sharedConnNumberLock);
938 	if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) {
939 		(void) mutex_unlock(&sharedConnNumberLock);
940 		(void) rw_unlock(&sessionPoolLock);
941 		(void) mutex_lock(&sessionLock);
942 		(void) mutex_lock(&sharedConnNumberLock);
943 		if (sessionPool == NULL || (sharedConnNumber == 0 &&
944 		    MTperConn == 1)) {
945 			(void) mutex_unlock(&sharedConnNumberLock);
946 			wait4session = 1;
947 			sessionTid = thr_self();
948 #ifdef DEBUG
949 			(void) fprintf(stderr, "tid= %d: get "
950 			    "connection ... \n", t);
951 			fflush(stderr);
952 #endif /* DEBUG */
953 			/*
954 			 * Exit with sessionLock locked. It will be
955 			 * be unlocked in addConnection() when this
956 			 * thread adds the connection to the pool or
957 			 * in __s_api_getConnection() when it exits
958 			 * without getting a connection.
959 			 */
960 			return (-1);
961 		}
962 
963 #ifdef DEBUG
964 		(void) fprintf(stderr, "tid= %d: shareable connections "
965 		    "exist\n", t);
966 		fflush(stderr);
967 #endif /* DEBUG */
968 		(void) mutex_unlock(&sharedConnNumberLock);
969 		/*
970 		 * There are sharable connections, check to see if
971 		 * one can be shared.
972 		 */
973 		(void) mutex_unlock(&sessionLock);
974 		(void) rw_rdlock(&sessionPoolLock);
975 	} else
976 		(void) mutex_unlock(&sharedConnNumberLock);
977 
978 	try = 0;
979 	check_again:
980 
981 	for (i = 0; i < sessionPoolSize; ++i) {
982 		if (sessionPool[i] == NULL)
983 			continue;
984 		cp = sessionPool[i];
985 #ifdef DEBUG
986 		(void) fprintf(stderr, "tid= %d: checking connection "
987 		    "[%d] ....\n", t, i);
988 		printConnection(LOG_DEBUG, cp);
989 #endif /* DEBUG */
990 		if ((cp->usedBit) || (cp->notAvail) ||
991 		    (cp->auth->auth.type != auth->auth.type) ||
992 		    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
993 		    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
994 		    (cp->auth->auth.saslopt != auth->auth.saslopt) ||
995 		    (serverAddr && *serverAddr &&
996 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
997 			continue;
998 		if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
999 		    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
1000 		    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
1001 		    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
1002 		    ((cp->auth->cred.unix_cred.userID == NULL) ||
1003 		    (cp->auth->cred.unix_cred.passwd == NULL) ||
1004 		    ((strcasecmp(cp->auth->cred.unix_cred.userID,
1005 		    auth->cred.unix_cred.userID) != 0)) ||
1006 		    ((strcmp(cp->auth->cred.unix_cred.passwd,
1007 		    auth->cred.unix_cred.passwd) != 0))))
1008 				continue;
1009 		if (!(serverAddr && *serverAddr)) {
1010 			/*
1011 			 * Get preferred server list.
1012 			 * When preferred servers are merged with default
1013 			 * servers (S_LDAP_SERVER_P) by __s_api_getServer,
1014 			 * preferred servers are copied sequencially.
1015 			 * The order should be the same as the order retrieved
1016 			 * by __ns_ldap_getParam.
1017 			 */
1018 			if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
1019 			    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
1020 				(void) __ns_ldap_freeError(&errorp);
1021 				(void) __ns_ldap_freeParam(&paramVal);
1022 				(void) rw_unlock(&sessionPoolLock);
1023 				return (-1);
1024 			}
1025 			servers = (char **)paramVal;
1026 			/*
1027 			 * Do fallback only if preferred servers are defined.
1028 			 */
1029 			if (servers != NULL) {
1030 				/*
1031 				 * Find the 1st available server
1032 				 */
1033 				rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
1034 				    &sinfo, &errorp, NS_CACHE_ADDR_IP);
1035 				if (rc != NS_LDAP_SUCCESS) {
1036 					/*
1037 					 * Drop the connection.
1038 					 * Pass 1 to fini so it won't be locked
1039 					 * inside _DropConnection
1040 					 */
1041 					_DropConnection(
1042 					    cp->connectionId,
1043 					    NS_LDAP_NEW_CONN, 1);
1044 					(void) rw_unlock(
1045 					    &sessionPoolLock);
1046 					(void) __ns_ldap_freeError(&errorp);
1047 					(void) __ns_ldap_freeParam(
1048 					    (void ***)&servers);
1049 					return (-1);
1050 				}
1051 
1052 				if (sinfo.server) {
1053 					/*
1054 					 * Test if cp->serverAddr is a
1055 					 * preferred server.
1056 					 */
1057 					conn_server_index = -1;
1058 					for (j = 0; servers[j] != NULL; j++) {
1059 						if (strcasecmp(servers[j],
1060 						    cp->serverAddr) == 0) {
1061 							conn_server_index = j;
1062 							break;
1063 						}
1064 					}
1065 					/*
1066 					 * Test if sinfo.server is a preferred
1067 					 * server.
1068 					 */
1069 					up_server_index = -1;
1070 					for (j = 0; servers[j] != NULL; j++) {
1071 						if (strcasecmp(sinfo.server,
1072 						    servers[j]) == 0) {
1073 							up_server_index = j;
1074 							break;
1075 						}
1076 					}
1077 
1078 					/*
1079 					 * The following code is to fall back
1080 					 * to preferred servers if servers
1081 					 * are previously down but are up now.
1082 					 * If cp->serverAddr is a preferred
1083 					 * server, it falls back to the servers
1084 					 * ahead of it. If cp->serverAddr is
1085 					 * not a preferred server, it falls
1086 					 * back to any of preferred servers
1087 					 * returned by ldap_cachemgr.
1088 					 */
1089 					if (conn_server_index >= 0 &&
1090 					    up_server_index >= 0) {
1091 						/*
1092 						 * cp->serverAddr and
1093 						 * sinfo.server are preferred
1094 						 * servers.
1095 						 */
1096 						if (up_server_index ==
1097 						    conn_server_index)
1098 							/*
1099 							 * sinfo.server is the
1100 							 * same as
1101 							 * cp->serverAddr.
1102 							 * Keep the connection.
1103 							 */
1104 							drop_conn = 0;
1105 						else
1106 							/*
1107 							 * 1.
1108 							 * up_server_index <
1109 							 * conn_server_index
1110 							 *
1111 							 * sinfo is ahead of
1112 							 * cp->serverAddr in
1113 							 * Need to fall back.
1114 							 * 2.
1115 							 * up_server_index >
1116 							 * conn_server_index
1117 							 * cp->serverAddr is
1118 							 * down. Drop it.
1119 							 */
1120 							drop_conn = 1;
1121 					} else if (conn_server_index >= 0 &&
1122 					    up_server_index == -1) {
1123 						/*
1124 						 * cp->serverAddr is a preferred
1125 						 * server but sinfo.server is
1126 						 * not. Preferred servers are
1127 						 * ahead of default servers.
1128 						 * This means cp->serverAddr is
1129 						 * down. Drop it.
1130 						 */
1131 						drop_conn = 1;
1132 					} else if (conn_server_index == -1 &&
1133 					    up_server_index >= 0) {
1134 						/*
1135 						 * cp->serverAddr is not a
1136 						 * preferred server but
1137 						 * sinfo.server is.
1138 						 * Fall back.
1139 						 */
1140 						drop_conn = 1;
1141 					} else {
1142 						/*
1143 						 * Both index are -1
1144 						 * cp->serverAddr and
1145 						 * sinfo.server are not
1146 						 * preferred servers.
1147 						 * No fallback.
1148 						 */
1149 						drop_conn = 0;
1150 					}
1151 					if (drop_conn) {
1152 						/*
1153 						 * Drop the connection so the
1154 						 * new conection can fall back
1155 						 * to a new server later.
1156 						 * Pass 1 to fini so it won't
1157 						 * be locked inside
1158 						 * _DropConnection
1159 						 */
1160 						_DropConnection(
1161 						    cp->connectionId,
1162 						    NS_LDAP_NEW_CONN, 1);
1163 						(void) rw_unlock(
1164 						    &sessionPoolLock);
1165 						(void) __ns_ldap_freeParam(
1166 						    (void ***)&servers);
1167 						__s_api_free_server_info(
1168 						    &sinfo);
1169 						return (-1);
1170 					} else {
1171 						/*
1172 						 * Keep the connection
1173 						 */
1174 						(void) __ns_ldap_freeParam(
1175 						    (void ***)&servers);
1176 						__s_api_free_server_info(
1177 						    &sinfo);
1178 					}
1179 				} else {
1180 					(void) rw_unlock(&sessionPoolLock);
1181 					syslog(LOG_WARNING, "libsldap: Null "
1182 					    "sinfo.server from "
1183 					    "__s_api_requestServer");
1184 					(void) __ns_ldap_freeParam(
1185 					    (void ***)&servers);
1186 					return (-1);
1187 				}
1188 			}
1189 		}
1190 
1191 		/* found an available connection */
1192 		if (MTperConn == 0)
1193 			cp->usedBit = B_TRUE;
1194 		else {
1195 			/*
1196 			 * if connection was established in a different
1197 			 * process, drop it and get a new one
1198 			 */
1199 			if (cp->pid != getpid()) {
1200 				(void) rw_unlock(&sessionPoolLock);
1201 				DropConnection(cp->connectionId,
1202 				    NS_LDAP_NEW_CONN);
1203 
1204 				goto get_conn;
1205 			}
1206 			/* allocate TSD for per thread ldap error */
1207 			rc = tsd_setup();
1208 
1209 			/* if we got TSD, this connection is shared */
1210 			if (rc != -1)
1211 				cp->shared++;
1212 			else if (cp->shared == 0) {
1213 				cp->usedBit = B_TRUE;
1214 				cp->threadID = thr_self();
1215 				(void) rw_unlock(&sessionPoolLock);
1216 				return (-1);
1217 			}
1218 		}
1219 		(void) rw_unlock(&sessionPoolLock);
1220 
1221 		*conp = cp;
1222 #ifdef DEBUG
1223 		(void) fprintf(stderr, "tid= %d: Connection found "
1224 		    "cID=%d, shared =%d\n", t, i, cp->shared);
1225 		fflush(stderr);
1226 #endif /* DEBUG */
1227 		return (i + CONID_OFFSET);
1228 	}
1229 
1230 	get_conn:
1231 
1232 	(void) rw_unlock(&sessionPoolLock);
1233 
1234 	/*
1235 	 * If multiple threads per connection not supported,
1236 	 * we are done, just return -1 to tell the caller to
1237 	 * proceed with opening a connection
1238 	 */
1239 	if (MTperConn == 0)
1240 		return (-1);
1241 
1242 	/*
1243 	 * No connection can be shared, test to see if
1244 	 * one is being opened. If trylock returns
1245 	 * EBUSY then it is, so wait until the opening
1246 	 * is done and try to see if the new connection
1247 	 * can be shared.
1248 	 */
1249 	rc = mutex_trylock(&sessionLock);
1250 	if (rc == EBUSY) {
1251 		(void) mutex_lock(&sessionLock);
1252 		(void) mutex_unlock(&sessionLock);
1253 		(void) rw_rdlock(&sessionPoolLock);
1254 #ifdef DEBUG
1255 		(void) fprintf(stderr, "tid= %d: check session "
1256 		    "pool again\n", t);
1257 		fflush(stderr);
1258 #endif /* DEBUG */
1259 		if (try < TRY_TIMES) {
1260 			try++;
1261 			goto check_again;
1262 		} else {
1263 			syslog(LOG_WARNING, "libsldap: mutex_trylock "
1264 			    "%d times. Stop.", TRY_TIMES);
1265 			(void) rw_unlock(&sessionPoolLock);
1266 			return (-1);
1267 		}
1268 	} else if (rc == 0) {
1269 		/*
1270 		 * No connection can be shared, none being opened,
1271 		 * exit with sessionLock locked to open one. The
1272 		 * mutex will be unlocked in addConnection() when
1273 		 * this thread adds the new connection to the pool
1274 		 * or in __s_api_getConnection() when it exits
1275 		 * without getting a connection.
1276 		 */
1277 		wait4session = 1;
1278 		sessionTid = thr_self();
1279 #ifdef DEBUG
1280 		(void) fprintf(stderr, "tid= %d: no connection found, "
1281 		    "none being opened, get connection ...\n", t);
1282 		fflush(stderr);
1283 #endif /* DEBUG */
1284 		return (-1);
1285 	} else {
1286 		syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
1287 		    "error %d", rc);
1288 		return (-1);
1289 	}
1290 }
1291 
1292 /*
1293  * Free a Connection structure
1294  */
1295 static void
1296 freeConnection(Connection *con)
1297 {
1298 	if (con == NULL)
1299 		return;
1300 	if (con->serverAddr)
1301 		free(con->serverAddr);
1302 	if (con->auth)
1303 		(void) __ns_ldap_freeCred(&(con->auth));
1304 	if (con->saslMechanisms) {
1305 		__s_api_free2dArray(con->saslMechanisms);
1306 	}
1307 	if (con->controls) {
1308 		__s_api_free2dArray(con->controls);
1309 	}
1310 	free(con);
1311 }
1312 
1313 /*
1314  * Find a connection matching the passed in criteria.  If an open
1315  * connection with that criteria exists use it, otherwise open a
1316  * new connection.
1317  * Success: returns the pointer to the Connection structure
1318  * Failure: returns NULL, error code and message should be in errorp
1319  */
1320 
1321 static int
1322 makeConnection(Connection **conp, const char *serverAddr,
1323 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
1324 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
1325 	int nopasswd_acct_mgmt, int flags, char ***badsrvrs)
1326 {
1327 	Connection *con = NULL;
1328 	ConnectionID id;
1329 	char errmsg[MAXERROR];
1330 	int rc, exit_rc = NS_LDAP_SUCCESS;
1331 	ns_server_info_t sinfo;
1332 	char *hReq, *host = NULL;
1333 	LDAP *ld = NULL;
1334 	int passwd_mgmt = 0;
1335 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
1336 	short	memerr = 0; /* Variable for tracking memory allocation */
1337 	char *serverAddrType = NULL, **bindHost = NULL;
1338 
1339 
1340 	if (conp == NULL || errorp == NULL || auth == NULL)
1341 		return (NS_LDAP_INVALID_PARAM);
1342 	*errorp = NULL;
1343 	*conp = NULL;
1344 	(void) memset(&sinfo, 0, sizeof (sinfo));
1345 
1346 	if ((wait4session == 0 || sessionTid != thr_self()) &&
1347 	    (id = findConnection(flags, serverAddr, auth, &con)) != -1) {
1348 		/* connection found in cache */
1349 #ifdef DEBUG
1350 		(void) fprintf(stderr, "tid= %d: connection found in "
1351 		    "cache %d\n", thr_self(), id);
1352 		fflush(stderr);
1353 #endif /* DEBUG */
1354 		*cID = id;
1355 		*conp = con;
1356 		return (NS_LDAP_SUCCESS);
1357 	}
1358 
1359 	if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
1360 		serverAddrType = NS_CACHE_ADDR_HOSTNAME;
1361 		bindHost = &sinfo.serverFQDN;
1362 	} else {
1363 		serverAddrType = NS_CACHE_ADDR_IP;
1364 		bindHost = &sinfo.server;
1365 	}
1366 
1367 	if (serverAddr) {
1368 		/*
1369 		 * We're given the server address, just use it.
1370 		 * In case of sasl/GSSAPI, serverAddr would need to be a FQDN.
1371 		 * We assume this is the case for now.
1372 		 *
1373 		 * Only the server address fields of sinfo structure are filled
1374 		 * in since these are the only relevant data that we have. Other
1375 		 * fields of this structure (controls, saslMechanisms) are
1376 		 * kept to NULL.
1377 		 */
1378 		sinfo.server = strdup(serverAddr);
1379 		if (sinfo.server == NULL)  {
1380 			return (NS_LDAP_MEMORY);
1381 		}
1382 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
1383 			sinfo.serverFQDN = strdup(serverAddr);
1384 			if (sinfo.serverFQDN == NULL) {
1385 				free(sinfo.server);
1386 				return (NS_LDAP_MEMORY);
1387 			}
1388 		}
1389 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
1390 		    fail_if_new_pwd_reqd, passwd_mgmt);
1391 		if (rc == NS_LDAP_SUCCESS || rc ==
1392 		    NS_LDAP_SUCCESS_WITH_INFO) {
1393 			exit_rc = rc;
1394 			goto create_con;
1395 		} else {
1396 			if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
1397 				(void) snprintf(errmsg, sizeof (errmsg),
1398 				    "%s %s", gettext("makeConnection: "
1399 				    "failed to open connection using "
1400 				    "sasl/GSSAPI to"), *bindHost);
1401 			} else {
1402 				(void) snprintf(errmsg, sizeof (errmsg),
1403 				    "%s %s", gettext("makeConnection: "
1404 				    "failed to open connection to"),
1405 				    *bindHost);
1406 			}
1407 			syslog(LOG_ERR, "libsldap: %s", errmsg);
1408 			__s_api_free_server_info(&sinfo);
1409 			return (rc);
1410 		}
1411 	}
1412 
1413 	/* No cached connection, create one */
1414 	for (; ; ) {
1415 		if (host == NULL)
1416 			hReq = NS_CACHE_NEW;
1417 		else
1418 			hReq = NS_CACHE_NEXT;
1419 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
1420 		    serverAddrType);
1421 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
1422 		    (host && (strcasecmp(host, sinfo.server) == 0))) {
1423 			/* Log the error */
1424 			if (*errorp) {
1425 				(void) snprintf(errmsg, sizeof (errmsg),
1426 				"%s: (%s)", gettext("makeConnection: "
1427 				"unable to make LDAP connection, "
1428 				"request for a server failed"),
1429 				    (*errorp)->message);
1430 				syslog(LOG_ERR, "libsldap: %s", errmsg);
1431 			}
1432 
1433 			__s_api_free_server_info(&sinfo);
1434 			if (host)
1435 				free(host);
1436 			return (NS_LDAP_OP_FAILED);
1437 		}
1438 		if (host)
1439 			free(host);
1440 		host = strdup(sinfo.server);
1441 		if (host == NULL) {
1442 			__s_api_free_server_info(&sinfo);
1443 			return (NS_LDAP_MEMORY);
1444 		}
1445 
1446 		/* check if server supports password management */
1447 		passwd_mgmt = __s_api_contain_passwd_control_oid(
1448 		    sinfo.controls);
1449 		/* check if server supports password less account mgmt */
1450 		if (nopasswd_acct_mgmt &&
1451 		    !__s_api_contain_account_usable_control_oid(
1452 		    sinfo.controls)) {
1453 			syslog(LOG_WARNING, "libsldap: server %s does not "
1454 			    "provide account information without password",
1455 			    host);
1456 			free(host);
1457 			__s_api_free_server_info(&sinfo);
1458 			return (NS_LDAP_OP_FAILED);
1459 		}
1460 		/* make the connection */
1461 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
1462 		    fail_if_new_pwd_reqd, passwd_mgmt);
1463 		/* if success, go to create connection structure */
1464 		if (rc == NS_LDAP_SUCCESS ||
1465 		    rc == NS_LDAP_SUCCESS_WITH_INFO) {
1466 			exit_rc = rc;
1467 			break;
1468 		}
1469 
1470 		/*
1471 		 * If not able to reach the server, inform the ldap
1472 		 * cache manager that the server should be removed
1473 		 * from its server list. Thus, the manager will not
1474 		 * return this server on the next get-server request
1475 		 * and will also reduce the server list refresh TTL,
1476 		 * so that it will find out sooner when the server
1477 		 * is up again.
1478 		 */
1479 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
1480 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
1481 			    (*errorp)->status == LDAP_SERVER_DOWN) {
1482 				/* Reset memory allocation error */
1483 				memerr = 0;
1484 				/*
1485 				 * We contacted a server that we could
1486 				 * not either authenticate to or contact.
1487 				 * If it is due to authentication, then
1488 				 * we need to try the server again. So,
1489 				 * do not remove the server yet, but
1490 				 * add it to the bad server list.
1491 				 * The caller routine will remove
1492 				 * the servers if:
1493 				 *	a). A good server is found or
1494 				 *	b). All the possible methods
1495 				 *	    are tried without finding
1496 				 *	    a good server
1497 				 */
1498 				if (*badsrvrs == NULL) {
1499 					if (!(*badsrvrs = (char **)malloc
1500 					    (sizeof (char *) * NUMTOMALLOC))) {
1501 						memerr = 1;
1502 					}
1503 				/* Allocate memory in chunks of NUMTOMALLOC */
1504 				} else if ((totalbad % NUMTOMALLOC) ==
1505 				    NUMTOMALLOC - 1) {
1506 					char **tmpptr;
1507 					if (!(tmpptr = (char **)realloc(
1508 					    *badsrvrs,
1509 					    (sizeof (char *) * NUMTOMALLOC *
1510 					    ((totalbad/NUMTOMALLOC) + 2))))) {
1511 						memerr = 1;
1512 					} else {
1513 						*badsrvrs = tmpptr;
1514 					}
1515 				}
1516 				/*
1517 				 * Store host only if there were no unsuccessful
1518 				 * memory allocations above
1519 				 */
1520 				if (!memerr &&
1521 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
1522 					memerr = 1;
1523 					totalbad--;
1524 				}
1525 				(*badsrvrs)[totalbad] = NULL;
1526 			}
1527 		}
1528 
1529 		/* else, cleanup and go for the next server */
1530 		__s_api_free_server_info(&sinfo);
1531 
1532 		/* Return if we had memory allocation errors */
1533 		if (memerr)
1534 			return (NS_LDAP_MEMORY);
1535 		if (*errorp) {
1536 			/*
1537 			 * If openConnection() failed due to
1538 			 * password policy, or invalid credential,
1539 			 * keep *errorp and exit
1540 			 */
1541 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
1542 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
1543 				free(host);
1544 				return (rc);
1545 			} else {
1546 				(void) __ns_ldap_freeError(errorp);
1547 				*errorp = NULL;
1548 			}
1549 		}
1550 	}
1551 
1552 create_con:
1553 	/* we have created ld, setup con structure */
1554 	if (host)
1555 		free(host);
1556 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
1557 		__s_api_free_server_info(&sinfo);
1558 		/*
1559 		 * If password control attached in **errorp,
1560 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1561 		 * free the error structure
1562 		 */
1563 		if (*errorp) {
1564 			(void) __ns_ldap_freeError(errorp);
1565 			*errorp = NULL;
1566 		}
1567 		(void) ldap_unbind(ld);
1568 		return (NS_LDAP_MEMORY);
1569 	}
1570 
1571 	con->serverAddr = sinfo.server; /* Store original format */
1572 	if (sinfo.serverFQDN != NULL) {
1573 		free(sinfo.serverFQDN);
1574 		sinfo.serverFQDN = NULL;
1575 	}
1576 	con->saslMechanisms = sinfo.saslMechanisms;
1577 	con->controls = sinfo.controls;
1578 
1579 	con->auth = __ns_ldap_dupAuth(auth);
1580 	if (con->auth == NULL) {
1581 		(void) ldap_unbind(ld);
1582 		freeConnection(con);
1583 		/*
1584 		 * If password control attached in **errorp,
1585 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1586 		 * free the error structure
1587 		 */
1588 		if (*errorp) {
1589 			(void) __ns_ldap_freeError(errorp);
1590 			*errorp = NULL;
1591 		}
1592 		return (NS_LDAP_MEMORY);
1593 	}
1594 
1595 	con->threadID = thr_self();
1596 	con->pid = getpid();
1597 
1598 	con->ld = ld;
1599 	if ((id = addConnection(con)) == -1) {
1600 		(void) ldap_unbind(ld);
1601 		freeConnection(con);
1602 		/*
1603 		 * If password control attached in **errorp,
1604 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1605 		 * free the error structure
1606 		 */
1607 		if (*errorp) {
1608 			(void) __ns_ldap_freeError(errorp);
1609 			*errorp = NULL;
1610 		}
1611 		return (NS_LDAP_MEMORY);
1612 	}
1613 #ifdef DEBUG
1614 	(void) fprintf(stderr, "tid= %d: connection added into "
1615 	    "cache %d\n", thr_self(), id);
1616 	fflush(stderr);
1617 #endif /* DEBUG */
1618 	*cID = id;
1619 	*conp = con;
1620 	return (exit_rc);
1621 }
1622 
1623 /*
1624  * Return the specified connection to the pool.  If necessary
1625  * delete the connection.
1626  */
1627 
1628 static void
1629 _DropConnection(ConnectionID cID, int flag, int fini)
1630 {
1631 	Connection *cp;
1632 	int id;
1633 	int use_lock = !fini;
1634 #ifdef DEBUG
1635 	thread_t t = thr_self();
1636 #endif /* DEBUG */
1637 
1638 	id = cID - CONID_OFFSET;
1639 	if (id < 0 || id >= sessionPoolSize)
1640 		return;
1641 #ifdef DEBUG
1642 	(void) fprintf(stderr, "tid= %d: "
1643 	    "Dropping connection cID=%d flag=0x%x, fini = %d\n",
1644 	    t, cID, flag, fini);
1645 	fflush(stderr);
1646 #endif /* DEBUG */
1647 	if (use_lock)
1648 		(void) rw_wrlock(&sessionPoolLock);
1649 
1650 	cp = sessionPool[id];
1651 	/* sanity check before removing */
1652 	if (!cp || (!fini && !cp->shared && !cp->usedBit)) {
1653 #ifdef DEBUG
1654 		if (cp == NULL)
1655 			(void) fprintf(stderr, "tid= %d: no "
1656 			    "need to remove (fini = %d, cp = %p)\n", t,
1657 			    fini, cp);
1658 		else
1659 			(void) fprintf(stderr, "tid= %d: no "
1660 			    "need to remove (fini = %d, cp = %p, shared = %d)"
1661 			    "\n", t, fini, cp, cp->shared);
1662 		fflush(stderr);
1663 #endif /* DEBUG */
1664 		if (use_lock)
1665 			(void) rw_unlock(&sessionPoolLock);
1666 		return;
1667 	}
1668 
1669 	if (!fini &&
1670 	    ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail &&
1671 	    ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc())) {
1672 #ifdef DEBUG
1673 		(void) fprintf(stderr, "tid= %d: keep alive (fini = %d "
1674 		    "shared = %d)\n", t, fini, cp->shared);
1675 #endif /* DEBUG */
1676 		/* release Connection (keep alive) */
1677 		if (cp->shared)
1678 			cp->shared--;
1679 		cp->usedBit = B_FALSE;
1680 		cp->threadID = 0;	/* unmark the threadID */
1681 		if (use_lock)
1682 			(void) rw_unlock(&sessionPoolLock);
1683 	} else {
1684 		/* delete Connection (disconnect) */
1685 		if (cp->shared > 0) {
1686 #ifdef DEBUG
1687 		(void) fprintf(stderr, "tid= %d: Connection no "
1688 		    "longer available (fini = %d, shared = %d)\n",
1689 		    t, fini, cp->shared);
1690 		fflush(stderr);
1691 #endif /* DEBUG */
1692 			cp->shared--;
1693 			/*
1694 			 * Mark this connection not available and decrement
1695 			 * sharedConnNumber. There could be multiple threads
1696 			 * sharing this connection so decrement
1697 			 * sharedConnNumber only once per connection.
1698 			 */
1699 			if (cp->notAvail == 0) {
1700 				cp->notAvail = 1;
1701 				(void) mutex_lock(&sharedConnNumberLock);
1702 				sharedConnNumber--;
1703 				(void) mutex_unlock(&sharedConnNumberLock);
1704 			}
1705 		}
1706 
1707 		if (cp->shared <= 0) {
1708 #ifdef DEBUG
1709 			(void) fprintf(stderr, "tid= %d: unbind "
1710 			    "(fini = %d, shared = %d)\n",
1711 			    t, fini, cp->shared);
1712 			fflush(stderr);
1713 #endif /* DEBUG */
1714 			sessionPool[id] = NULL;
1715 			(void) ldap_unbind(cp->ld);
1716 			freeConnection(cp);
1717 		}
1718 
1719 		if (use_lock)
1720 			(void) rw_unlock(&sessionPoolLock);
1721 	}
1722 }
1723 
1724 void
1725 DropConnection(ConnectionID cID, int flag)
1726 {
1727 	_DropConnection(cID, flag, 0);
1728 }
1729 
1730 /*
1731  * This routine is called after a bind operation is
1732  * done in openConnection() to process the password
1733  * management information, if any.
1734  *
1735  * Input:
1736  *   bind_type: "simple" or "sasl/DIGEST-MD5"
1737  *   ldaprc   : ldap rc from the ldap bind operation
1738  *   controls : controls returned by the server
1739  *   errmsg   : error message from the server
1740  *   fail_if_new_pwd_reqd:
1741  *              flag indicating if connection should be open
1742  *              when password needs to change immediately
1743  *   passwd_mgmt:
1744  *              flag indicating if server supports password
1745  *              policy/management
1746  *
1747  * Output     : ns_ldap_error structure, which may contain
1748  *              password status and number of seconds until
1749  *              expired
1750  *
1751  * return rc:
1752  * NS_LDAP_EXTERNAL: error, connection should not open
1753  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
1754  * NS_LDAP_SUCCESS: OK to open connection
1755  *
1756  */
1757 
1758 static int
1759 process_pwd_mgmt(char *bind_type, int ldaprc,
1760 		LDAPControl **controls,
1761 		char *errmsg, ns_ldap_error_t **errorp,
1762 		int fail_if_new_pwd_reqd,
1763 		int passwd_mgmt)
1764 {
1765 	char		errstr[MAXERROR];
1766 	LDAPControl	**ctrl = NULL;
1767 	int		exit_rc;
1768 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
1769 	int		sec_until_exp = 0;
1770 
1771 	/*
1772 	 * errmsg may be an empty string,
1773 	 * even if ldaprc is LDAP_SUCCESS,
1774 	 * free the empty string if that's the case
1775 	 */
1776 	if (errmsg &&
1777 	    (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
1778 		ldap_memfree(errmsg);
1779 		errmsg = NULL;
1780 	}
1781 
1782 	if (ldaprc != LDAP_SUCCESS) {
1783 		/*
1784 		 * try to map ldap rc and error message to
1785 		 * a password status
1786 		 */
1787 		if (errmsg) {
1788 			if (passwd_mgmt)
1789 				pwd_status =
1790 				    __s_api_set_passwd_status(
1791 				    ldaprc, errmsg);
1792 			ldap_memfree(errmsg);
1793 		}
1794 
1795 		(void) snprintf(errstr, sizeof (errstr),
1796 		    gettext("openConnection: "
1797 		    "%s bind failed "
1798 		    "- %s"), bind_type, ldap_err2string(ldaprc));
1799 
1800 		if (pwd_status != NS_PASSWD_GOOD) {
1801 			MKERROR_PWD_MGMT(*errorp,
1802 			    ldaprc, strdup(errstr),
1803 			    pwd_status, 0, NULL);
1804 		} else {
1805 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
1806 			    NULL);
1807 		}
1808 		if (controls)
1809 			ldap_controls_free(controls);
1810 
1811 		return (NS_LDAP_INTERNAL);
1812 	}
1813 
1814 	/*
1815 	 * ldaprc is LDAP_SUCCESS,
1816 	 * process the password management controls, if any
1817 	 */
1818 	exit_rc = NS_LDAP_SUCCESS;
1819 	if (controls && passwd_mgmt) {
1820 		/*
1821 		 * The control with the OID
1822 		 * 2.16.840.1.113730.3.4.4 (or
1823 		 * LDAP_CONTROL_PWEXPIRED, as defined
1824 		 * in the ldap.h header file) is the
1825 		 * expired password control.
1826 		 *
1827 		 * This control is used if the server
1828 		 * is configured to require users to
1829 		 * change their passwords when first
1830 		 * logging in and whenever the
1831 		 * passwords are reset.
1832 		 *
1833 		 * If the user is logging in for the
1834 		 * first time or if the user's
1835 		 * password has been reset, the
1836 		 * server sends this control to
1837 		 * indicate that the client needs to
1838 		 * change the password immediately.
1839 		 *
1840 		 * At this point, the only operation
1841 		 * that the client can perform is to
1842 		 * change the user's password. If the
1843 		 * client requests any other LDAP
1844 		 * operation, the server sends back
1845 		 * an LDAP_UNWILLING_TO_PERFORM
1846 		 * result code with an expired
1847 		 * password control.
1848 		 *
1849 		 * The control with the OID
1850 		 * 2.16.840.1.113730.3.4.5 (or
1851 		 * LDAP_CONTROL_PWEXPIRING, as
1852 		 * defined in the ldap.h header file)
1853 		 * is the password expiration warning
1854 		 * control.
1855 		 *
1856 		 * This control is used if the server
1857 		 * is configured to expire user
1858 		 * passwords after a certain amount
1859 		 * of time.
1860 		 *
1861 		 * The server sends this control back
1862 		 * to the client if the client binds
1863 		 * using a password that will soon
1864 		 * expire.  The ldctl_value field of
1865 		 * the LDAPControl structure
1866 		 * specifies the number of seconds
1867 		 * before the password will expire.
1868 		 */
1869 		for (ctrl = controls; *ctrl; ctrl++) {
1870 
1871 			if (strcmp((*ctrl)->ldctl_oid,
1872 			    LDAP_CONTROL_PWEXPIRED) == 0) {
1873 				/*
1874 				 * if the caller wants this bind
1875 				 * to fail, set up the error info.
1876 				 * If call to this function is
1877 				 * for searching the LDAP directory,
1878 				 * e.g., __ns_ldap_list(),
1879 				 * there's really no sense to
1880 				 * let a connection open and
1881 				 * then fail immediately afterward
1882 				 * on the LDAP search operation with
1883 				 * the LDAP_UNWILLING_TO_PERFORM rc
1884 				 */
1885 				pwd_status =
1886 				    NS_PASSWD_CHANGE_NEEDED;
1887 				if (fail_if_new_pwd_reqd) {
1888 					(void) snprintf(errstr,
1889 					    sizeof (errstr),
1890 					    gettext(
1891 					    "openConnection: "
1892 					    "%s bind "
1893 					    "failed "
1894 					    "- password "
1895 					    "expired. It "
1896 					    " needs to change "
1897 					    "immediately!"),
1898 					    bind_type);
1899 					MKERROR_PWD_MGMT(*errorp,
1900 					    LDAP_SUCCESS,
1901 					    strdup(errstr),
1902 					    pwd_status,
1903 					    0,
1904 					    NULL);
1905 					exit_rc = NS_LDAP_INTERNAL;
1906 				} else {
1907 					MKERROR_PWD_MGMT(*errorp,
1908 					    LDAP_SUCCESS,
1909 					    NULL,
1910 					    pwd_status,
1911 					    0,
1912 					    NULL);
1913 					exit_rc =
1914 					    NS_LDAP_SUCCESS_WITH_INFO;
1915 				}
1916 				break;
1917 			} else if (strcmp((*ctrl)->ldctl_oid,
1918 			    LDAP_CONTROL_PWEXPIRING) == 0) {
1919 				pwd_status =
1920 				    NS_PASSWD_ABOUT_TO_EXPIRE;
1921 				if ((*ctrl)->
1922 				    ldctl_value.bv_len > 0 &&
1923 				    (*ctrl)->
1924 				    ldctl_value.bv_val)
1925 					sec_until_exp =
1926 					    atoi((*ctrl)->
1927 					    ldctl_value.bv_val);
1928 				MKERROR_PWD_MGMT(*errorp,
1929 				    LDAP_SUCCESS,
1930 				    NULL,
1931 				    pwd_status,
1932 				    sec_until_exp,
1933 				    NULL);
1934 				exit_rc =
1935 				    NS_LDAP_SUCCESS_WITH_INFO;
1936 				break;
1937 			}
1938 		}
1939 	}
1940 
1941 	if (controls)
1942 		ldap_controls_free(controls);
1943 
1944 	return (exit_rc);
1945 }
1946 
1947 static int
1948 ldap_in_hosts_switch()
1949 {
1950 	enum __nsw_parse_err		pserr;
1951 	struct __nsw_switchconfig	*conf;
1952 	struct __nsw_lookup		*lkp;
1953 	const char			*name;
1954 	int				found = 0;
1955 
1956 	conf = __nsw_getconfig("hosts", &pserr);
1957 	if (conf == NULL) {
1958 		return (-1);
1959 	}
1960 
1961 	/* check for skip and count other backends */
1962 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
1963 		name = lkp->service_name;
1964 		if (strcmp(name, "ldap") == 0) {
1965 			found = 1;
1966 			break;
1967 		}
1968 	}
1969 	__nsw_freeconfig(conf);
1970 	return (found);
1971 }
1972 
1973 static int
1974 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
1975 	int timeoutSec, ns_ldap_error_t **errorp,
1976 	int fail_if_new_pwd_reqd, int passwd_mgmt)
1977 {
1978 	LDAP		*ld = NULL;
1979 	char		*binddn, *passwd;
1980 	char		*digest_md5_name;
1981 	const char	*s;
1982 	int		ldapVersion = LDAP_VERSION3;
1983 	int		derefOption = LDAP_DEREF_ALWAYS;
1984 	int		zero = 0;
1985 	int		rc;
1986 	char		errstr[MAXERROR];
1987 	int		errnum = 0;
1988 	LDAPMessage	*resultMsg;
1989 	int		msgId;
1990 	int		useSSL = 0, port = 0;
1991 	struct timeval	tv;
1992 	AuthType_t	bindType;
1993 	int		timeoutMilliSec = timeoutSec * 1000;
1994 	struct berval	cred;
1995 	char		*sslServerAddr;
1996 	char		*s1;
1997 	char		*errmsg, *end = NULL;
1998 	LDAPControl	**controls;
1999 	int		pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
2000 	ns_sasl_cb_param_t	sasl_param;
2001 
2002 	*errorp = NULL;
2003 	*ldp = NULL;
2004 
2005 	switch (auth->auth.type) {
2006 		case NS_LDAP_AUTH_NONE:
2007 		case NS_LDAP_AUTH_SIMPLE:
2008 		case NS_LDAP_AUTH_SASL:
2009 			bindType = auth->auth.type;
2010 			break;
2011 		case NS_LDAP_AUTH_TLS:
2012 			useSSL = 1;
2013 			switch (auth->auth.tlstype) {
2014 				case NS_LDAP_TLS_NONE:
2015 					bindType = NS_LDAP_AUTH_NONE;
2016 					break;
2017 				case NS_LDAP_TLS_SIMPLE:
2018 					bindType = NS_LDAP_AUTH_SIMPLE;
2019 					break;
2020 				case NS_LDAP_TLS_SASL:
2021 					bindType = NS_LDAP_AUTH_SASL;
2022 					break;
2023 				default:
2024 					(void) sprintf(errstr,
2025 					gettext("openConnection: unsupported "
2026 					    "TLS authentication method "
2027 					    "(%d)"), auth->auth.tlstype);
2028 					MKERROR(LOG_WARNING, *errorp,
2029 					    LDAP_AUTH_METHOD_NOT_SUPPORTED,
2030 					    strdup(errstr), NULL);
2031 					return (NS_LDAP_INTERNAL);
2032 			}
2033 			break;
2034 		default:
2035 			(void) sprintf(errstr,
2036 			    gettext("openConnection: unsupported "
2037 			    "authentication method (%d)"), auth->auth.type);
2038 			MKERROR(LOG_WARNING, *errorp,
2039 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2040 			    NULL);
2041 			return (NS_LDAP_INTERNAL);
2042 	}
2043 
2044 	if (useSSL) {
2045 		const char	*hostcertpath;
2046 		char		*alloc_hcp = NULL;
2047 #ifdef DEBUG
2048 		(void) fprintf(stderr, "tid= %d: +++TLS transport\n",
2049 		    thr_self());
2050 #endif /* DEBUG */
2051 
2052 		if (prldap_set_session_option(NULL, NULL,
2053 		    PRLDAP_OPT_IO_MAX_TIMEOUT,
2054 		    timeoutMilliSec) != LDAP_SUCCESS) {
2055 			(void) snprintf(errstr, sizeof (errstr),
2056 			    gettext("openConnection: failed to initialize "
2057 			    "TLS security"));
2058 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2059 			    strdup(errstr), NULL);
2060 			return (NS_LDAP_INTERNAL);
2061 		}
2062 
2063 		hostcertpath = auth->hostcertpath;
2064 		if (hostcertpath == NULL) {
2065 			alloc_hcp = __s_get_hostcertpath();
2066 			hostcertpath = alloc_hcp;
2067 		}
2068 
2069 		if (hostcertpath == NULL)
2070 			return (NS_LDAP_MEMORY);
2071 
2072 		if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
2073 			if (alloc_hcp)
2074 				free(alloc_hcp);
2075 			(void) snprintf(errstr, sizeof (errstr),
2076 			    gettext("openConnection: failed to initialize "
2077 			    "TLS security (%s)"),
2078 			    ldapssl_err2string(rc));
2079 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2080 			    strdup(errstr), NULL);
2081 			return (NS_LDAP_INTERNAL);
2082 		}
2083 		if (alloc_hcp)
2084 			free(alloc_hcp);
2085 
2086 		/* determine if the host name contains a port number */
2087 		s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
2088 		if (s == NULL)
2089 			s = serverAddr;
2090 		s = strchr(s, ':');
2091 		if (s != NULL) {
2092 			/*
2093 			 * If we do get a port number, we will try stripping
2094 			 * it. At present, referrals will always have a
2095 			 * port number.
2096 			 */
2097 			sslServerAddr = strdup(serverAddr);
2098 			if (sslServerAddr == NULL)
2099 				return (NS_LDAP_MEMORY);
2100 			s1 = strrchr(sslServerAddr, ':');
2101 			if (s1 != NULL)
2102 				*s1 = '\0';
2103 			(void) snprintf(errstr, sizeof (errstr),
2104 			    gettext("openConnection: cannot use tls with %s. "
2105 			    "Trying %s"),
2106 			    serverAddr, sslServerAddr);
2107 			syslog(LOG_ERR, "libsldap: %s", errstr);
2108 		} else
2109 			sslServerAddr = (char *)serverAddr;
2110 
2111 		ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
2112 
2113 		if (sslServerAddr != serverAddr)
2114 			free(sslServerAddr);
2115 
2116 		if (ld == NULL ||
2117 		    ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
2118 			(void) snprintf(errstr, sizeof (errstr),
2119 			    gettext("openConnection: failed to connect "
2120 			    "using TLS (%s)"), strerror(errno));
2121 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2122 			    strdup(errstr), NULL);
2123 			return (NS_LDAP_INTERNAL);
2124 		}
2125 	} else {
2126 #ifdef DEBUG
2127 		(void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
2128 		    thr_self());
2129 #endif /* DEBUG */
2130 		port = LDAP_PORT;
2131 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI &&
2132 		    (end = strchr(serverAddr, ':')) != NULL) {
2133 			/*
2134 			 * The IP is converted to hostname so it's a
2135 			 * hostname:port up to this point.
2136 			 *
2137 			 * libldap passes hostname:port to the sasl layer.
2138 			 * The ldap service principal is constructed as
2139 			 * ldap/hostname:port@REALM. Kerberos authentication
2140 			 * will fail. So it needs to be parsed to construct
2141 			 * a valid principal ldap/hostname@REALM.
2142 			 *
2143 			 * For useSSL case above, it already parses port so
2144 			 * no need to parse serverAddr
2145 			 */
2146 			*end = '\0';
2147 			port = atoi(end + 1);
2148 		}
2149 
2150 		/* Warning message IF cannot connect to host(s) */
2151 		if ((ld = ldap_init((char *)serverAddr, port)) == NULL) {
2152 			char *p = strerror(errno);
2153 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2154 			    strdup(p), NULL);
2155 			if (end)
2156 				*end = ':';
2157 			return (NS_LDAP_INTERNAL);
2158 		} else {
2159 			if (end)
2160 				*end = ':';
2161 			/* check and avoid gethostname recursion */
2162 			if (ldap_in_hosts_switch() > 0 &&
2163 			    ! __s_api_isipv4((char *)serverAddr) &&
2164 			    ! __s_api_isipv6((char *)serverAddr)) {
2165 				/* host: ldap - found, attempt to recover */
2166 				if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
2167 				    "ldap") != 0) {
2168 					(void) snprintf(errstr, sizeof (errstr),
2169 					    gettext("openConnection: "
2170 					    "unrecoverable gethostname "
2171 					    "recursion detected "
2172 					    "in /etc/nsswitch.conf"));
2173 					MKERROR(LOG_WARNING, *errorp,
2174 					    LDAP_CONNECT_ERROR,
2175 					    strdup(errstr), NULL);
2176 					(void) ldap_unbind(ld);
2177 					return (NS_LDAP_INTERNAL);
2178 				}
2179 			}
2180 		}
2181 	}
2182 
2183 	ns_setup_mt_conn_and_tsd(ld);
2184 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
2185 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
2186 	/*
2187 	 * set LDAP_OPT_REFERRALS to OFF.
2188 	 * This library will handle the referral itself
2189 	 * based on API flags or configuration file
2190 	 * specification. If this option is not set
2191 	 * to OFF, libldap will never pass the
2192 	 * referral info up to this library
2193 	 */
2194 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
2195 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
2196 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
2197 	/* setup TCP/IP connect timeout */
2198 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
2199 	    &timeoutMilliSec);
2200 	/* retry if LDAP I/O was interrupted */
2201 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
2202 
2203 	switch (bindType) {
2204 	case NS_LDAP_AUTH_NONE:
2205 #ifdef DEBUG
2206 		(void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
2207 		    thr_self());
2208 #endif /* DEBUG */
2209 		break;
2210 	case NS_LDAP_AUTH_SIMPLE:
2211 		binddn = auth->cred.unix_cred.userID;
2212 		passwd = auth->cred.unix_cred.passwd;
2213 		if (passwd == NULL || *passwd == '\0' ||
2214 		    binddn == NULL || *binddn == '\0') {
2215 			(void) sprintf(errstr, gettext("openConnection: "
2216 			    "missing credentials for Simple bind"));
2217 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
2218 			    strdup(errstr), NULL);
2219 			(void) ldap_unbind(ld);
2220 			return (NS_LDAP_INTERNAL);
2221 		}
2222 
2223 #ifdef DEBUG
2224 		(void) fprintf(stderr, "tid= %d: +++Simple bind\n",
2225 		    thr_self());
2226 #endif /* DEBUG */
2227 		msgId = ldap_simple_bind(ld, binddn, passwd);
2228 
2229 		if (msgId == -1) {
2230 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
2231 			    (void *)&errnum);
2232 			(void) snprintf(errstr, sizeof (errstr),
2233 			    gettext("openConnection: simple bind failed "
2234 			    "- %s"), ldap_err2string(errnum));
2235 			(void) ldap_unbind(ld);
2236 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
2237 			    NULL);
2238 			return (NS_LDAP_INTERNAL);
2239 		}
2240 
2241 		tv.tv_sec = timeoutSec;
2242 		tv.tv_usec = 0;
2243 		rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
2244 
2245 		if ((rc == -1) || (rc == 0)) {
2246 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
2247 			    (void *)&errnum);
2248 			(void) snprintf(errstr, sizeof (errstr),
2249 			    gettext("openConnection: simple bind failed "
2250 			    "- %s"), ldap_err2string(errnum));
2251 			(void) ldap_msgfree(resultMsg);
2252 			(void) ldap_unbind(ld);
2253 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
2254 			    NULL);
2255 			return (NS_LDAP_INTERNAL);
2256 		}
2257 
2258 		/*
2259 		 * get ldaprc, controls, and error msg
2260 		 */
2261 		rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2262 		    &errmsg, NULL, &controls, 1);
2263 
2264 		if (rc != LDAP_SUCCESS) {
2265 			(void) snprintf(errstr, sizeof (errstr),
2266 			    gettext("openConnection: simple bind failed "
2267 			    "- unable to parse result"));
2268 			(void) ldap_unbind(ld);
2269 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2270 			    strdup(errstr), NULL);
2271 			return (NS_LDAP_INTERNAL);
2272 		}
2273 
2274 		/* process the password management info, if any */
2275 		pwd_rc = process_pwd_mgmt("simple",
2276 		    errnum, controls, errmsg,
2277 		    errorp,
2278 		    fail_if_new_pwd_reqd,
2279 		    passwd_mgmt);
2280 
2281 		if (pwd_rc == NS_LDAP_INTERNAL) {
2282 			(void) ldap_unbind(ld);
2283 			return (pwd_rc);
2284 		}
2285 
2286 		if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
2287 			*ldp = ld;
2288 			return (pwd_rc);
2289 		}
2290 
2291 		break;
2292 	case NS_LDAP_AUTH_SASL:
2293 		if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
2294 		    auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2295 			(void) sprintf(errstr,
2296 			    gettext("openConnection: SASL options are "
2297 			    "not supported (%d) for non-GSSAPI sasl bind"),
2298 			    auth->auth.saslopt);
2299 			MKERROR(LOG_WARNING, *errorp,
2300 			    LDAP_AUTH_METHOD_NOT_SUPPORTED,
2301 			    strdup(errstr), NULL);
2302 			(void) ldap_unbind(ld);
2303 			return (NS_LDAP_INTERNAL);
2304 		}
2305 		if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2306 			binddn = auth->cred.unix_cred.userID;
2307 			passwd = auth->cred.unix_cred.passwd;
2308 			if (passwd == NULL || *passwd == '\0' ||
2309 			    binddn == NULL || *binddn == '\0') {
2310 				(void) sprintf(errstr,
2311 				    gettext("openConnection: missing "
2312 				    "credentials for SASL bind"));
2313 				MKERROR(LOG_WARNING, *errorp,
2314 				    LDAP_INVALID_CREDENTIALS,
2315 				    strdup(errstr), NULL);
2316 				(void) ldap_unbind(ld);
2317 				return (NS_LDAP_INTERNAL);
2318 			}
2319 			cred.bv_val = passwd;
2320 			cred.bv_len = strlen(passwd);
2321 		}
2322 
2323 		switch (auth->auth.saslmech) {
2324 		case NS_LDAP_SASL_CRAM_MD5:
2325 			/*
2326 			 * NOTE: if iDS changes to support cram_md5,
2327 			 * please add password management code here.
2328 			 * Since ldap_sasl_cram_md5_bind_s does not
2329 			 * return anything that could be used to
2330 			 * extract the ldap rc/errmsg/control to
2331 			 * determine if bind failed due to password
2332 			 * policy, a new cram_md5_bind API will need
2333 			 * to be introduced. See
2334 			 * ldap_x_sasl_digest_md5_bind() and case
2335 			 * NS_LDAP_SASL_DIGEST_MD5 below for details.
2336 			 */
2337 			if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
2338 			    &cred, NULL, NULL)) != LDAP_SUCCESS) {
2339 				(void) ldap_get_option(ld,
2340 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2341 				(void) snprintf(errstr, sizeof (errstr),
2342 				    gettext("openConnection: "
2343 				    "sasl/CRAM-MD5 bind failed - %s"),
2344 				    ldap_err2string(errnum));
2345 				MKERROR(LOG_WARNING, *errorp, errnum,
2346 				    strdup(errstr), NULL);
2347 				(void) ldap_unbind(ld);
2348 				return (NS_LDAP_INTERNAL);
2349 			}
2350 			break;
2351 		case NS_LDAP_SASL_DIGEST_MD5:
2352 			digest_md5_name = malloc(strlen(binddn) + 5);
2353 			/* 5 = strlen("dn: ") + 1 */
2354 			if (digest_md5_name == NULL) {
2355 				(void) ldap_unbind(ld);
2356 				return (NS_LDAP_MEMORY);
2357 			}
2358 			(void) strcpy(digest_md5_name, "dn: ");
2359 			(void) strcat(digest_md5_name, binddn);
2360 
2361 			tv.tv_sec = timeoutSec;
2362 			tv.tv_usec = 0;
2363 			rc = ldap_x_sasl_digest_md5_bind(ld,
2364 			    digest_md5_name, &cred, NULL, NULL,
2365 			    &tv, &resultMsg);
2366 
2367 			if (resultMsg == NULL) {
2368 				free(digest_md5_name);
2369 				(void) ldap_get_option(ld,
2370 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2371 				(void) snprintf(errstr, sizeof (errstr),
2372 				    gettext("openConnection: "
2373 				    "DIGEST-MD5 bind failed - %s"),
2374 				    ldap_err2string(errnum));
2375 				(void) ldap_unbind(ld);
2376 				MKERROR(LOG_WARNING, *errorp, errnum,
2377 				    strdup(errstr), NULL);
2378 				return (NS_LDAP_INTERNAL);
2379 			}
2380 
2381 			/*
2382 			 * get ldaprc, controls, and error msg
2383 			 */
2384 			rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2385 			    &errmsg, NULL, &controls, 1);
2386 
2387 			if (rc != LDAP_SUCCESS) {
2388 				free(digest_md5_name);
2389 				(void) snprintf(errstr, sizeof (errstr),
2390 				    gettext("openConnection: "
2391 				    "DIGEST-MD5 bind failed "
2392 				    "- unable to parse result"));
2393 				(void) ldap_unbind(ld);
2394 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2395 				    strdup(errstr), NULL);
2396 				return (NS_LDAP_INTERNAL);
2397 			}
2398 
2399 			/* process the password management info, if any */
2400 			pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
2401 			    errnum, controls, errmsg,
2402 			    errorp,
2403 			    fail_if_new_pwd_reqd,
2404 			    passwd_mgmt);
2405 
2406 			if (pwd_rc == NS_LDAP_INTERNAL) {
2407 				free(digest_md5_name);
2408 				(void) ldap_unbind(ld);
2409 				return (pwd_rc);
2410 			}
2411 
2412 			if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
2413 				*ldp = ld;
2414 				return (pwd_rc);
2415 			}
2416 
2417 			free(digest_md5_name);
2418 			break;
2419 		case NS_LDAP_SASL_GSSAPI:
2420 			if (sasl_gssapi_inited == 0) {
2421 				rc = __s_api_sasl_gssapi_init();
2422 				if (rc != NS_LDAP_SUCCESS) {
2423 					(void) snprintf(errstr, sizeof (errstr),
2424 					    gettext("openConnection: "
2425 					    "GSSAPI initialization "
2426 					    "failed"));
2427 					(void) ldap_unbind(ld);
2428 					MKERROR(LOG_WARNING, *errorp, rc,
2429 					    strdup(errstr), NULL);
2430 					return (rc);
2431 				}
2432 			}
2433 			(void) memset(&sasl_param, 0,
2434 			    sizeof (ns_sasl_cb_param_t));
2435 			sasl_param.authid = NULL;
2436 			sasl_param.authzid = "";
2437 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
2438 			    (void *)&min_ssf);
2439 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
2440 			    (void *)&max_ssf);
2441 
2442 			rc = ldap_sasl_interactive_bind_s(
2443 			    ld, NULL, "GSSAPI",
2444 			    NULL, NULL, LDAP_SASL_INTERACTIVE,
2445 			    __s_api_sasl_bind_callback,
2446 			    &sasl_param);
2447 
2448 			if (rc != LDAP_SUCCESS) {
2449 				(void) snprintf(errstr, sizeof (errstr),
2450 				    gettext("openConnection: "
2451 				    "GSSAPI bind failed "
2452 				    "- %d %s"), rc, ldap_err2string(rc));
2453 				(void) ldap_unbind(ld);
2454 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2455 				    strdup(errstr), NULL);
2456 				return (NS_LDAP_INTERNAL);
2457 			}
2458 
2459 			break;
2460 		default:
2461 			(void) ldap_unbind(ld);
2462 			(void) sprintf(errstr,
2463 			    gettext("openConnection: unsupported SASL "
2464 			    "mechanism (%d)"), auth->auth.saslmech);
2465 			MKERROR(LOG_WARNING, *errorp,
2466 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2467 			    NULL);
2468 			return (NS_LDAP_INTERNAL);
2469 		}
2470 	}
2471 
2472 	*ldp = ld;
2473 	return (NS_LDAP_SUCCESS);
2474 }
2475 
2476 /*
2477  * FUNCTION:	__s_api_getDefaultAuth
2478  *
2479  *	Constructs a credential for authentication using the config module.
2480  *
2481  * RETURN VALUES:
2482  *
2483  * NS_LDAP_SUCCESS	If successful
2484  * NS_LDAP_CONFIG	If there are any config errors.
2485  * NS_LDAP_MEMORY	Memory errors.
2486  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
2487  *			not build a new authp.
2488  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
2489  *			necessary fields of a cred for a given auth method
2490  *			are not provided.
2491  * INPUT:
2492  *
2493  * cLevel	Currently requested credential level to be tried
2494  *
2495  * aMethod	Currently requested authentication method to be tried
2496  *
2497  * OUTPUT:
2498  *
2499  * authp		authentication method to use.
2500  */
2501 static int
2502 __s_api_getDefaultAuth(
2503 	int	*cLevel,
2504 	ns_auth_t *aMethod,
2505 	ns_cred_t **authp)
2506 {
2507 	void		**paramVal = NULL;
2508 	char		*modparamVal = NULL;
2509 	int		getUid = 0;
2510 	int		getPasswd = 0;
2511 	int		getCertpath = 0;
2512 	int		rc = 0;
2513 	ns_ldap_error_t	*errorp = NULL;
2514 
2515 #ifdef DEBUG
2516 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
2517 #endif
2518 
2519 	if (aMethod == NULL) {
2520 		/* Require an Auth */
2521 		return (NS_LDAP_INVALID_PARAM);
2522 
2523 	}
2524 	/*
2525 	 * credential level "self" can work with auth method sasl/GSSAPI only
2526 	 */
2527 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
2528 	    aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
2529 		return (NS_LDAP_INVALID_PARAM);
2530 
2531 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
2532 	if ((*authp) == NULL)
2533 		return (NS_LDAP_MEMORY);
2534 
2535 	(*authp)->auth = *aMethod;
2536 
2537 	switch (aMethod->type) {
2538 		case NS_LDAP_AUTH_NONE:
2539 			return (NS_LDAP_SUCCESS);
2540 		case NS_LDAP_AUTH_SIMPLE:
2541 			getUid++;
2542 			getPasswd++;
2543 			break;
2544 		case NS_LDAP_AUTH_SASL:
2545 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
2546 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
2547 				getUid++;
2548 				getPasswd++;
2549 			} else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
2550 				(void) __ns_ldap_freeCred(authp);
2551 				*authp = NULL;
2552 				return (NS_LDAP_INVALID_PARAM);
2553 			}
2554 			break;
2555 		case NS_LDAP_AUTH_TLS:
2556 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
2557 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
2558 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
2559 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
2560 				getUid++;
2561 				getPasswd++;
2562 				getCertpath++;
2563 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
2564 				getCertpath++;
2565 			} else {
2566 				(void) __ns_ldap_freeCred(authp);
2567 				*authp = NULL;
2568 				return (NS_LDAP_INVALID_PARAM);
2569 			}
2570 			break;
2571 	}
2572 
2573 	if (getUid) {
2574 		paramVal = NULL;
2575 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
2576 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2577 			(void) __ns_ldap_freeCred(authp);
2578 			(void) __ns_ldap_freeError(&errorp);
2579 			*authp = NULL;
2580 			return (rc);
2581 		}
2582 
2583 		if (paramVal == NULL || *paramVal == NULL) {
2584 			(void) __ns_ldap_freeCred(authp);
2585 			*authp = NULL;
2586 			return (NS_LDAP_INVALID_PARAM);
2587 		}
2588 
2589 		(*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
2590 		(void) __ns_ldap_freeParam(&paramVal);
2591 		if ((*authp)->cred.unix_cred.userID == NULL) {
2592 			(void) __ns_ldap_freeCred(authp);
2593 			*authp = NULL;
2594 			return (NS_LDAP_MEMORY);
2595 		}
2596 	}
2597 	if (getPasswd) {
2598 		paramVal = NULL;
2599 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
2600 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2601 			(void) __ns_ldap_freeCred(authp);
2602 			(void) __ns_ldap_freeError(&errorp);
2603 			*authp = NULL;
2604 			return (rc);
2605 		}
2606 
2607 		if (paramVal == NULL || *paramVal == NULL) {
2608 			(void) __ns_ldap_freeCred(authp);
2609 			*authp = NULL;
2610 			return (NS_LDAP_INVALID_PARAM);
2611 		}
2612 
2613 		modparamVal = dvalue((char *)*paramVal);
2614 		(void) __ns_ldap_freeParam(&paramVal);
2615 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
2616 			(void) __ns_ldap_freeCred(authp);
2617 			if (modparamVal != NULL)
2618 				free(modparamVal);
2619 			*authp = NULL;
2620 			return (NS_LDAP_INVALID_PARAM);
2621 		}
2622 
2623 		(*authp)->cred.unix_cred.passwd = modparamVal;
2624 	}
2625 	if (getCertpath) {
2626 		paramVal = NULL;
2627 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
2628 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2629 			(void) __ns_ldap_freeCred(authp);
2630 			(void) __ns_ldap_freeError(&errorp);
2631 			*authp = NULL;
2632 			return (rc);
2633 		}
2634 
2635 		if (paramVal == NULL || *paramVal == NULL) {
2636 			(void) __ns_ldap_freeCred(authp);
2637 			*authp = NULL;
2638 			return (NS_LDAP_INVALID_PARAM);
2639 		}
2640 
2641 		(*authp)->hostcertpath = strdup((char *)*paramVal);
2642 		(void) __ns_ldap_freeParam(&paramVal);
2643 		if ((*authp)->hostcertpath == NULL) {
2644 			(void) __ns_ldap_freeCred(authp);
2645 			*authp = NULL;
2646 			return (NS_LDAP_MEMORY);
2647 		}
2648 	}
2649 	return (NS_LDAP_SUCCESS);
2650 }
2651 
2652 /*
2653  * FUNCTION:	__s_api_getConnection
2654  *
2655  *	Bind to the specified server or one from the server
2656  *	list and return the pointer.
2657  *
2658  *	This function can rebind or not (NS_LDAP_HARD), it can require a
2659  *	credential or bind anonymously
2660  *
2661  *	This function follows the DUA configuration schema algorithm
2662  *
2663  * RETURN VALUES:
2664  *
2665  * NS_LDAP_SUCCESS	A connection was made successfully.
2666  * NS_LDAP_SUCCESS_WITH_INFO
2667  * 			A connection was made successfully, but with
2668  *			password management info in *errorp
2669  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
2670  * NS_LDAP_CONFIG	If there are any config errors.
2671  * NS_LDAP_MEMORY	Memory errors.
2672  * NS_LDAP_INTERNAL	If there was a ldap error.
2673  *
2674  * INPUT:
2675  *
2676  * server	Bind to this LDAP server only
2677  * flags	If NS_LDAP_HARD is set function will not return until it has
2678  *		a connection unless there is a authentication problem.
2679  *		If NS_LDAP_NEW_CONN is set the function must force a new
2680  *              connection to be created
2681  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
2682  * auth		Credentials for bind. This could be NULL in which case
2683  *		a default cred built from the config module is used.
2684  * sessionId	cookie that points to a previous session
2685  * fail_if_new_pwd_reqd
2686  *		a flag indicating this function should fail if the passwd
2687  *		in auth needs to change immediately
2688  * nopasswd_acct_mgmt
2689  *		a flag indicating that makeConnection should check before
2690  *		binding if server supports LDAP V3 password less
2691  *		account management
2692  *
2693  * OUTPUT:
2694  *
2695  * session	pointer to a session with connection information
2696  * errorp	Set if there are any INTERNAL, or CONFIG error.
2697  */
2698 int
2699 __s_api_getConnection(
2700 	const char *server,
2701 	const int flags,
2702 	const ns_cred_t *cred,		/* credentials for bind */
2703 	ConnectionID *sessionId,
2704 	Connection **session,
2705 	ns_ldap_error_t **errorp,
2706 	int fail_if_new_pwd_reqd,
2707 	int nopasswd_acct_mgmt)
2708 {
2709 	char		errmsg[MAXERROR];
2710 	ns_auth_t	**aMethod = NULL;
2711 	ns_auth_t	**aNext = NULL;
2712 	int		**cLevel = NULL;
2713 	int		**cNext = NULL;
2714 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
2715 	int		rc;
2716 	Connection	*con = NULL;
2717 	int		sec = 1;
2718 	ns_cred_t 	*authp = NULL;
2719 	ns_cred_t	anon;
2720 	int		version = NS_LDAP_V2, self_gssapi_only = 0;
2721 	void		**paramVal = NULL;
2722 	char		**badSrvrs = NULL; /* List of problem hostnames */
2723 
2724 	if ((session == NULL) || (sessionId == NULL)) {
2725 		return (NS_LDAP_INVALID_PARAM);
2726 	}
2727 	*session = NULL;
2728 
2729 	/* if we already have a session id try to reuse connection */
2730 	if (*sessionId > 0) {
2731 		rc = findConnectionById(flags, cred, *sessionId, &con);
2732 		if (rc == *sessionId && con) {
2733 			*session = con;
2734 			return (NS_LDAP_SUCCESS);
2735 		}
2736 		*sessionId = 0;
2737 	}
2738 
2739 	/* get profile version number */
2740 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
2741 	    &paramVal, errorp)) != NS_LDAP_SUCCESS)
2742 		return (rc);
2743 	if (paramVal == NULL) {
2744 		(void) sprintf(errmsg, gettext("getConnection: no file "
2745 		    "version"));
2746 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
2747 		    NS_LDAP_CONFIG);
2748 		return (NS_LDAP_CONFIG);
2749 	}
2750 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
2751 		version = NS_LDAP_V1;
2752 	(void) __ns_ldap_freeParam((void ***)&paramVal);
2753 
2754 	/* Get the bind timeout value */
2755 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
2756 	if (paramVal != NULL && *paramVal != NULL) {
2757 		timeoutSec = **((int **)paramVal);
2758 		(void) __ns_ldap_freeParam(&paramVal);
2759 	}
2760 	if (*errorp)
2761 		(void) __ns_ldap_freeError(errorp);
2762 
2763 	if (cred == NULL) {
2764 		/* Get the authentication method list */
2765 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
2766 		    (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
2767 			return (rc);
2768 		if (aMethod == NULL) {
2769 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
2770 			if (aMethod == NULL)
2771 				return (NS_LDAP_MEMORY);
2772 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
2773 			if (aMethod[0] == NULL) {
2774 				free(aMethod);
2775 				return (NS_LDAP_MEMORY);
2776 			}
2777 			if (version == NS_LDAP_V1)
2778 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
2779 			else {
2780 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
2781 				(aMethod[0])->saslmech =
2782 				    NS_LDAP_SASL_DIGEST_MD5;
2783 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
2784 			}
2785 		}
2786 
2787 		/* Get the credential level list */
2788 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
2789 		    (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
2790 			(void) __ns_ldap_freeParam((void ***)&aMethod);
2791 			return (rc);
2792 		}
2793 		if (cLevel == NULL) {
2794 			cLevel = (int **)calloc(2, sizeof (int *));
2795 			if (cLevel == NULL)
2796 				return (NS_LDAP_MEMORY);
2797 			cLevel[0] = (int *)calloc(1, sizeof (int));
2798 			if (cLevel[0] == NULL)
2799 				return (NS_LDAP_MEMORY);
2800 			if (version == NS_LDAP_V1)
2801 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
2802 			else
2803 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
2804 		}
2805 	}
2806 
2807 	/* setup the anon credential for anonymous connection */
2808 	(void) memset(&anon, 0, sizeof (ns_cred_t));
2809 	anon.auth.type = NS_LDAP_AUTH_NONE;
2810 
2811 	for (; ; ) {
2812 		if (cred != NULL) {
2813 			/* using specified auth method */
2814 			rc = makeConnection(&con, server, cred,
2815 			    sessionId, timeoutSec, errorp,
2816 			    fail_if_new_pwd_reqd,
2817 			    nopasswd_acct_mgmt, flags, &badSrvrs);
2818 			/* not using bad server if credentials were supplied */
2819 			if (badSrvrs && *badSrvrs) {
2820 				__s_api_free2dArray(badSrvrs);
2821 				badSrvrs = NULL;
2822 			}
2823 			if (rc == NS_LDAP_SUCCESS ||
2824 			    rc == NS_LDAP_SUCCESS_WITH_INFO) {
2825 				*session = con;
2826 				break;
2827 			}
2828 		} else {
2829 			self_gssapi_only = __s_api_self_gssapi_only_get();
2830 			/* for every cred level */
2831 			for (cNext = cLevel; *cNext != NULL; cNext++) {
2832 				if (self_gssapi_only &&
2833 				    **cNext != NS_LDAP_CRED_SELF)
2834 					continue;
2835 				if (**cNext == NS_LDAP_CRED_ANON) {
2836 					/*
2837 					 * make connection anonymously
2838 					 * Free the down server list before
2839 					 * looping through
2840 					 */
2841 					if (badSrvrs && *badSrvrs) {
2842 						__s_api_free2dArray(badSrvrs);
2843 						badSrvrs = NULL;
2844 					}
2845 					rc = makeConnection(&con, server, &anon,
2846 					    sessionId, timeoutSec, errorp,
2847 					    fail_if_new_pwd_reqd,
2848 					    nopasswd_acct_mgmt, flags,
2849 					    &badSrvrs);
2850 					if (rc == NS_LDAP_SUCCESS ||
2851 					    rc ==
2852 					    NS_LDAP_SUCCESS_WITH_INFO) {
2853 						*session = con;
2854 						goto done;
2855 					}
2856 					continue;
2857 				}
2858 				/* for each cred level */
2859 				for (aNext = aMethod; *aNext != NULL; aNext++) {
2860 					if (self_gssapi_only &&
2861 					    (*aNext)->saslmech !=
2862 					    NS_LDAP_SASL_GSSAPI)
2863 						continue;
2864 					/*
2865 					 * self coexists with sasl/GSSAPI only
2866 					 * and non-self coexists with non-gssapi
2867 					 * only
2868 					 */
2869 					if ((**cNext == NS_LDAP_CRED_SELF &&
2870 					    (*aNext)->saslmech !=
2871 					    NS_LDAP_SASL_GSSAPI) ||
2872 					    (**cNext != NS_LDAP_CRED_SELF &&
2873 					    (*aNext)->saslmech ==
2874 					    NS_LDAP_SASL_GSSAPI))
2875 						continue;
2876 					/* make connection and authenticate */
2877 					/* with default credentials */
2878 					authp = NULL;
2879 					rc = __s_api_getDefaultAuth(*cNext,
2880 					    *aNext, &authp);
2881 					if (rc != NS_LDAP_SUCCESS) {
2882 						continue;
2883 					}
2884 					/*
2885 					 * Free the down server list before
2886 					 * looping through
2887 					 */
2888 					if (badSrvrs && *badSrvrs) {
2889 						__s_api_free2dArray(badSrvrs);
2890 						badSrvrs = NULL;
2891 					}
2892 					rc = makeConnection(&con, server, authp,
2893 					    sessionId, timeoutSec, errorp,
2894 					    fail_if_new_pwd_reqd,
2895 					    nopasswd_acct_mgmt, flags,
2896 					    &badSrvrs);
2897 					(void) __ns_ldap_freeCred(&authp);
2898 					if (rc == NS_LDAP_SUCCESS ||
2899 					    rc ==
2900 					    NS_LDAP_SUCCESS_WITH_INFO) {
2901 						*session = con;
2902 						goto done;
2903 					}
2904 				}
2905 			}
2906 		}
2907 		if (flags & NS_LDAP_HARD) {
2908 			if (sec < LDAPMAXHARDLOOKUPTIME)
2909 				sec *= 2;
2910 			_sleep(sec);
2911 		} else {
2912 			break;
2913 		}
2914 	}
2915 
2916 done:
2917 	/*
2918 	 * If unable to get a connection, and this is
2919 	 * the thread opening the shared connection,
2920 	 * unlock the session mutex and let other
2921 	 * threads try to get their own connection.
2922 	 */
2923 	if (wait4session != 0 && sessionTid == thr_self()) {
2924 		wait4session = 0;
2925 		sessionTid = 0;
2926 #ifdef DEBUG
2927 		(void) fprintf(stderr, "tid= %d: __s_api_getConnection: "
2928 		    "unlocking sessionLock \n", thr_self());
2929 		fflush(stderr);
2930 #endif /* DEBUG */
2931 		(void) mutex_unlock(&sessionLock);
2932 	}
2933 	if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
2934 		/*
2935 		 * self_gssapi_only is true but no self/sasl/gssapi is
2936 		 * configured
2937 		 */
2938 		rc = NS_LDAP_CONFIG;
2939 	}
2940 
2941 	(void) __ns_ldap_freeParam((void ***)&aMethod);
2942 	(void) __ns_ldap_freeParam((void ***)&cLevel);
2943 
2944 	if (badSrvrs && *badSrvrs) {
2945 		/*
2946 		 * At this point, either we have a successful
2947 		 * connection or exhausted all the possible auths.
2948 		 * and creds. Mark the problem servers as down
2949 		 * so that the problem servers are not contacted
2950 		 * again until the refresh_ttl expires.
2951 		 */
2952 		(void) __s_api_removeBadServers(badSrvrs);
2953 		__s_api_free2dArray(badSrvrs);
2954 	}
2955 	return (rc);
2956 }
2957 
2958 #pragma fini(_free_sessionPool)
2959 static void
2960 _free_sessionPool()
2961 {
2962 	int id;
2963 
2964 	(void) rw_wrlock(&sessionPoolLock);
2965 	if (sessionPool != NULL) {
2966 		for (id = 0; id < sessionPoolSize; id++)
2967 			_DropConnection(id + CONID_OFFSET, 0, 1);
2968 		free(sessionPool);
2969 		sessionPool = NULL;
2970 		sessionPoolSize = 0;
2971 	}
2972 	(void) rw_unlock(&sessionPoolLock);
2973 }
2974