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