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