xref: /titanic_51/usr/src/lib/libsldap/common/ns_connect.c (revision 14ea4bb737263733ad80a36b4f73f681c30a6b45)
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 	/* if a new connection is requested, no need to continue */
842 	if (flags & NS_LDAP_NEW_CONN)
843 		return (-1);
844 
845 #ifdef DEBUG
846 	(void) fprintf(stderr, "tid= %d: Find connection\n", t);
847 	(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
848 	if (serverAddr && *serverAddr)
849 		(void) fprintf(stderr, "tid= %d: serverAddr=%s\n",
850 			t, serverAddr);
851 	else
852 		(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
853 	printCred(stderr, auth);
854 	fflush(stderr);
855 #endif /* DEBUG */
856 
857 	/*
858 	 * If multiple threads per connection not supported,
859 	 * no sessionPool means no connection
860 	 */
861 	(void) rw_rdlock(&sessionPoolLock);
862 	if (MTperConn == 0 && sessionPool == NULL) {
863 		(void) rw_unlock(&sessionPoolLock);
864 		return (-1);
865 	}
866 
867 	/*
868 	 * If no connection in cache, then serialize the opening
869 	 * of connections. Make sure only one is being opened
870 	 * at a time. Otherwise, we may end up with more
871 	 * connections than we want (if multiple threads get
872 	 * here at the same time)
873 	 */
874 	if (sessionPool == NULL) {
875 		(void) rw_unlock(&sessionPoolLock);
876 		(void) mutex_lock(&sessionLock);
877 		if (sessionPool == NULL) {
878 			wait4session = 1;
879 			sessionTid = thr_self();
880 #ifdef DEBUG
881 			(void) fprintf(stderr, "tid= %d: get "
882 				"connection ... \n", t);
883 			fflush(stderr);
884 #endif /* DEBUG */
885 			/*
886 			 * Exit with sessionLock locked. It will be
887 			 * be unlocked in addConnection() when this
888 			 * thread adds the connection to the pool or
889 			 * in __s_api_getConnection() when it exits
890 			 * without getting a connection.
891 			 */
892 			return (-1);
893 		}
894 
895 #ifdef DEBUG
896 		(void) fprintf(stderr, "tid= %d: session pool not empty\n", t);
897 		fflush(stderr);
898 #endif /* DEBUG */
899 		/*
900 		 * connection pool is not empty, check to see if
901 		 * one can be shared.
902 		 */
903 		(void) mutex_unlock(&sessionLock);
904 		(void) rw_rdlock(&sessionPoolLock);
905 	}
906 	try = 0;
907 	check_again:
908 
909 	for (i = 0; i < sessionPoolSize; ++i) {
910 		if (sessionPool[i] == NULL)
911 			continue;
912 		cp = sessionPool[i];
913 #ifdef DEBUG
914 		(void) fprintf(stderr, "tid= %d: checking connection "
915 			"[%d] ....\n", t, i);
916 		printConnection(stderr, cp);
917 #endif /* DEBUG */
918 		if ((cp->usedBit) || (cp->notAvail) ||
919 		    (cp->auth->auth.type != auth->auth.type) ||
920 		    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
921 		    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
922 		    (cp->auth->auth.saslopt != auth->auth.saslopt) ||
923 		    (serverAddr && *serverAddr &&
924 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
925 			continue;
926 		if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
927 		    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
928 		    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
929 		    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
930 		    ((cp->auth->cred.unix_cred.userID == NULL) ||
931 		    (cp->auth->cred.unix_cred.passwd == NULL) ||
932 		    ((strcasecmp(cp->auth->cred.unix_cred.userID,
933 			    auth->cred.unix_cred.userID) != 0)) ||
934 		    ((strcmp(cp->auth->cred.unix_cred.passwd,
935 			    auth->cred.unix_cred.passwd) != 0))))
936 				continue;
937 		/* found an available connection */
938 		if (MTperConn == 0)
939 			cp->usedBit = B_TRUE;
940 		else {
941 			/* allocate TSD for per thread ldap error */
942 			rc = tsd_setup();
943 
944 			/* if we got TSD, this connection is shared */
945 			if (rc != -1)
946 				cp->shared++;
947 			else if (cp->shared == 0) {
948 				cp->usedBit = B_TRUE;
949 				cp->threadID = thr_self();
950 				(void) rw_unlock(&sessionPoolLock);
951 				return (-1);
952 			}
953 		}
954 		(void) rw_unlock(&sessionPoolLock);
955 
956 		*conp = cp;
957 #ifdef DEBUG
958 		(void) fprintf(stderr, "tid= %d: Connection found "
959 			"cID=%d, shared =%d\n", t, i, cp->shared);
960 		fflush(stderr);
961 #endif /* DEBUG */
962 		return (i + CONID_OFFSET);
963 	}
964 	(void) rw_unlock(&sessionPoolLock);
965 
966 	/*
967 	 * If multiple threads per connection not supported,
968 	 * we are done, just return -1 to tell the caller to
969 	 * proceed with opening a connection
970 	 */
971 	if (MTperConn == 0)
972 		return (-1);
973 
974 	/*
975 	 * No connection can be shared, test to see if
976 	 * one is being opened. If trylock returns
977 	 * EBUSY then it is, so wait until the opening
978 	 * is done and try to see if the new connection
979 	 * can be shared.
980 	 */
981 	rc = mutex_trylock(&sessionLock);
982 	if (rc == EBUSY) {
983 		(void) mutex_lock(&sessionLock);
984 		(void) mutex_unlock(&sessionLock);
985 		(void) rw_rdlock(&sessionPoolLock);
986 #ifdef DEBUG
987 		(void) fprintf(stderr, "tid= %d: check session "
988 			"pool again\n", t);
989 		fflush(stderr);
990 #endif /* DEBUG */
991 		if (try < TRY_TIMES) {
992 			try++;
993 			goto check_again;
994 		} else {
995 			syslog(LOG_WARNING, "libsldap: mutex_trylock "
996 				"%d times. Stop.", TRY_TIMES);
997 			return (-1);
998 		}
999 	} else if (rc == 0) {
1000 		/*
1001 		 * No connection can be shared, none being opened,
1002 		 * exit with sessionLock locked to open one. The
1003 		 * mutex will be unlocked in addConnection() when
1004 		 * this thread adds the new connection to the pool
1005 		 * or in __s_api_getConnection() when it exits
1006 		 * without getting a connection.
1007 		 */
1008 		wait4session = 1;
1009 		sessionTid = thr_self();
1010 #ifdef DEBUG
1011 		(void) fprintf(stderr, "tid= %d: no connection found, "
1012 			"none being opened, get connection ...\n", t);
1013 		fflush(stderr);
1014 #endif /* DEBUG */
1015 		return (-1);
1016 	} else {
1017 		syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
1018 			"error", rc);
1019 		return (-1);
1020 	}
1021 }
1022 
1023 /*
1024  * Free a Connection structure
1025  */
1026 static void
1027 freeConnection(Connection *con)
1028 {
1029 	if (con == NULL)
1030 		return;
1031 	if (con->serverAddr)
1032 		free(con->serverAddr);
1033 	if (con->auth)
1034 		(void) __ns_ldap_freeCred(&(con->auth));
1035 	if (con->saslMechanisms) {
1036 		__s_api_free2dArray(con->saslMechanisms);
1037 	}
1038 	if (con->controls) {
1039 		__s_api_free2dArray(con->controls);
1040 	}
1041 	free(con);
1042 }
1043 
1044 /*
1045  * Find a connection matching the passed in criteria.  If an open
1046  * connection with that criteria exists use it, otherwise open a
1047  * new connection.
1048  * Success: returns the pointer to the Connection structure
1049  * Failure: returns NULL, error code and message should be in errorp
1050  */
1051 
1052 static int
1053 makeConnection(Connection **conp, const char *serverAddr,
1054 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
1055 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
1056 	int nopasswd_acct_mgmt, int flags, char ***badsrvrs)
1057 {
1058 	Connection *con = NULL;
1059 	ConnectionID id;
1060 	char errmsg[MAXERROR];
1061 	int rc, exit_rc = NS_LDAP_SUCCESS;
1062 	ns_server_info_t sinfo;
1063 	char *hReq, *host = NULL;
1064 	LDAP *ld = NULL;
1065 	int passwd_mgmt = 0;
1066 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
1067 	short	memerr = 0; /* Variable for tracking memory allocation */
1068 	char *serverAddrType = NULL;
1069 
1070 
1071 	if (conp == NULL || errorp == NULL || auth == NULL)
1072 		return (NS_LDAP_INVALID_PARAM);
1073 	*errorp = NULL;
1074 	*conp = NULL;
1075 	sinfo.server = NULL;
1076 	sinfo.controls = NULL;
1077 	sinfo.saslMechanisms = NULL;
1078 
1079 	if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) {
1080 		/* connection found in cache */
1081 #ifdef DEBUG
1082 		(void) fprintf(stderr, "tid= %d: connection found in "
1083 			"cache %d\n", thr_self(), id);
1084 		fflush(stderr);
1085 #endif /* DEBUG */
1086 		*cID = id;
1087 		*conp = con;
1088 		return (NS_LDAP_SUCCESS);
1089 	}
1090 
1091 	if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI)
1092 		serverAddrType = NS_CACHE_ADDR_HOSTNAME;
1093 	else
1094 		serverAddrType = NS_CACHE_ADDR_IP;
1095 
1096 	if (serverAddr) {
1097 		rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr,
1098 			&sinfo, errorp, serverAddrType);
1099 		if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
1100 			(void) snprintf(errmsg, sizeof (errmsg),
1101 			gettext("makeConnection: unable to get "
1102 			"server information for %s"), serverAddr);
1103 			syslog(LOG_ERR, "libsldap: %s", errmsg);
1104 			return (NS_LDAP_OP_FAILED);
1105 		}
1106 		rc = openConnection(&ld, sinfo.server, auth, timeoutSec, errorp,
1107 				fail_if_new_pwd_reqd, passwd_mgmt);
1108 		if (rc == NS_LDAP_SUCCESS || rc ==
1109 				NS_LDAP_SUCCESS_WITH_INFO) {
1110 			exit_rc = rc;
1111 			goto create_con;
1112 		} else {
1113 			return (rc);
1114 		}
1115 	}
1116 
1117 	/* No cached connection, create one */
1118 	for (; ; ) {
1119 		if (host == NULL)
1120 			hReq = NS_CACHE_NEW;
1121 		else
1122 			hReq = NS_CACHE_NEXT;
1123 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
1124 				serverAddrType);
1125 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
1126 			(host && (strcasecmp(host, sinfo.server) == 0))) {
1127 			/* Log the error */
1128 			if (*errorp) {
1129 				(void) snprintf(errmsg, sizeof (errmsg),
1130 				"%s: (%s)", gettext("makeConnection: "
1131 				"unable to make LDAP connection, "
1132 				"request for a server failed"),
1133 				    (*errorp)->message);
1134 				syslog(LOG_ERR, "libsldap: %s", errmsg);
1135 			}
1136 
1137 			if (sinfo.server)
1138 				free(sinfo.server);
1139 			__s_api_free2dArray(sinfo.saslMechanisms);
1140 			__s_api_free2dArray(sinfo.controls);
1141 			if (host)
1142 				free(host);
1143 			return (NS_LDAP_OP_FAILED);
1144 		}
1145 		if (host)
1146 			free(host);
1147 		host = strdup(sinfo.server);
1148 		if (host == NULL) {
1149 			free(sinfo.server);
1150 			__s_api_free2dArray(sinfo.saslMechanisms);
1151 			__s_api_free2dArray(sinfo.controls);
1152 			return (NS_LDAP_MEMORY);
1153 		}
1154 
1155 		/* check if server supports password management */
1156 		passwd_mgmt = __s_api_contain_passwd_control_oid(
1157 			sinfo.controls);
1158 		/* check if server supports password less account mgmt */
1159 		if (nopasswd_acct_mgmt &&
1160 			!__s_api_contain_account_usable_control_oid(
1161 			sinfo.controls)) {
1162 			syslog(LOG_WARNING, "libsldap: server %s does not "
1163 				"provide account information without password",
1164 				host);
1165 			free(host);
1166 			free(sinfo.server);
1167 			__s_api_free2dArray(sinfo.saslMechanisms);
1168 			__s_api_free2dArray(sinfo.controls);
1169 			return (NS_LDAP_OP_FAILED);
1170 		}
1171 		/* make the connection */
1172 		rc = openConnection(&ld, host, auth, timeoutSec, errorp,
1173 				fail_if_new_pwd_reqd, passwd_mgmt);
1174 		/* if success, go to create connection structure */
1175 		if (rc == NS_LDAP_SUCCESS ||
1176 				rc == NS_LDAP_SUCCESS_WITH_INFO) {
1177 			exit_rc = rc;
1178 			break;
1179 		}
1180 
1181 		/*
1182 		 * If not able to reach the server, inform the ldap
1183 		 * cache manager that the server should be removed
1184 		 * from its server list. Thus, the manager will not
1185 		 * return this server on the next get-server request
1186 		 * and will also reduce the server list refresh TTL,
1187 		 * so that it will find out sooner when the server
1188 		 * is up again.
1189 		 */
1190 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
1191 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
1192 				(*errorp)->status == LDAP_SERVER_DOWN) {
1193 				/* Reset memory allocation error */
1194 				memerr = 0;
1195 				/*
1196 				 * We contacted a server that we could
1197 				 * not either authenticate to or contact.
1198 				 * If it is due to authentication, then
1199 				 * we need to try the server again. So,
1200 				 * do not remove the server yet, but
1201 				 * add it to the bad server list.
1202 				 * The caller routine will remove
1203 				 * the servers if:
1204 				 *	a). A good server is found or
1205 				 *	b). All the possible methods
1206 				 *	    are tried without finding
1207 				 *	    a good server
1208 				 */
1209 				if (*badsrvrs == NULL) {
1210 				    if (!(*badsrvrs = (char **)malloc
1211 					(sizeof (char *) * NUMTOMALLOC))) {
1212 					memerr = 1;
1213 				    }
1214 				/* Allocate memory in chunks of NUMTOMALLOC */
1215 				} else if ((totalbad % NUMTOMALLOC) ==
1216 					    NUMTOMALLOC - 1) {
1217 				    char **tmpptr;
1218 				    if (!(tmpptr = (char **)realloc(*badsrvrs,
1219 					    (sizeof (char *) * NUMTOMALLOC *
1220 					    ((totalbad/NUMTOMALLOC) + 2))))) {
1221 					memerr = 1;
1222 				    } else {
1223 					*badsrvrs = tmpptr;
1224 				    }
1225 				}
1226 				/*
1227 				 * Store host only if there were no unsuccessful
1228 				 * memory allocations above
1229 				 */
1230 				if (!memerr &&
1231 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
1232 					memerr = 1;
1233 					totalbad--;
1234 				}
1235 				(*badsrvrs)[totalbad] = NULL;
1236 			}
1237 		}
1238 
1239 		/* else, cleanup and go for the next server */
1240 		if (sinfo.server) {
1241 			free(sinfo.server);
1242 			sinfo.server = NULL;
1243 		}
1244 		__s_api_free2dArray(sinfo.saslMechanisms);
1245 		sinfo.saslMechanisms = NULL;
1246 		__s_api_free2dArray(sinfo.controls);
1247 		sinfo.controls = NULL;
1248 		/* Return if we had memory allocation errors */
1249 		if (memerr)
1250 			return (NS_LDAP_MEMORY);
1251 		if (*errorp) {
1252 			/*
1253 			 * If openConnection() failed due to
1254 			 * password policy, or invalid credential,
1255 			 * keep *errorp and exit
1256 			 */
1257 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
1258 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
1259 				free(host);
1260 				return (rc);
1261 			} else {
1262 				(void) __ns_ldap_freeError(errorp);
1263 				*errorp = NULL;
1264 			}
1265 		}
1266 	}
1267 
1268 create_con:
1269 	/* we have created ld, setup con structure */
1270 	if (host)
1271 		free(host);
1272 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
1273 		if (sinfo.server)
1274 			free(sinfo.server);
1275 		__s_api_free2dArray(sinfo.saslMechanisms);
1276 		__s_api_free2dArray(sinfo.controls);
1277 		/*
1278 		 * If password control attached in **errorp,
1279 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1280 		 * free the error structure
1281 		 */
1282 		if (*errorp) {
1283 			(void) __ns_ldap_freeError(errorp);
1284 			*errorp = NULL;
1285 		}
1286 		return (NS_LDAP_MEMORY);
1287 	}
1288 
1289 	con->serverAddr = sinfo.server;
1290 	con->saslMechanisms = sinfo.saslMechanisms;
1291 	con->controls = sinfo.controls;
1292 
1293 	con->auth = __ns_ldap_dupAuth(auth);
1294 	if (con->auth == NULL) {
1295 		free(con);
1296 		/*
1297 		 * If password control attached in **errorp,
1298 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1299 		 * free the error structure
1300 		 */
1301 		if (*errorp) {
1302 			(void) __ns_ldap_freeError(errorp);
1303 			*errorp = NULL;
1304 		}
1305 		return (NS_LDAP_MEMORY);
1306 	}
1307 
1308 	con->threadID = thr_self();
1309 
1310 	con->ld = ld;
1311 	if ((id = addConnection(con)) == -1) {
1312 		freeConnection(con);
1313 		/*
1314 		 * If password control attached in **errorp,
1315 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1316 		 * free the error structure
1317 		 */
1318 		if (*errorp) {
1319 			(void) __ns_ldap_freeError(errorp);
1320 			*errorp = NULL;
1321 		}
1322 		return (NS_LDAP_MEMORY);
1323 	}
1324 #ifdef DEBUG
1325 	(void) fprintf(stderr, "tid= %d: connection added into "
1326 		"cache %d\n", thr_self(), id);
1327 	fflush(stderr);
1328 #endif /* DEBUG */
1329 	*cID = id;
1330 	*conp = con;
1331 	return (exit_rc);
1332 }
1333 
1334 /*
1335  * Return the specified connection to the pool.  If necessary
1336  * delete the connection.
1337  */
1338 
1339 static void
1340 _DropConnection(ConnectionID cID, int flag, int fini)
1341 {
1342 	Connection *cp;
1343 	int id;
1344 	int use_lock = !fini;
1345 #ifdef DEBUG
1346 	thread_t t = thr_self();
1347 #endif /* DEBUG */
1348 
1349 	id = cID - CONID_OFFSET;
1350 	if (id < 0 || id >= sessionPoolSize)
1351 		return;
1352 #ifdef DEBUG
1353 	(void) fprintf(stderr, "tid= %d: "
1354 		"Dropping connection cID=%d flag=0x%x, fini = %d\n",
1355 			t, cID, flag, fini);
1356 	fflush(stderr);
1357 #endif /* DEBUG */
1358 	if (use_lock)
1359 		(void) rw_wrlock(&sessionPoolLock);
1360 
1361 	cp = sessionPool[id];
1362 	/* sanity check before removing */
1363 	if (!cp || (!fini && !cp->shared && (!cp->usedBit ||
1364 		cp->threadID != thr_self()))) {
1365 #ifdef DEBUG
1366 		if (cp == NULL)
1367 			(void) fprintf(stderr, "tid= %d: no "
1368 			"need to remove (fini = %d, cp = %p)\n", t,
1369 			fini, cp);
1370 		else
1371 			(void) fprintf(stderr, "tid= %d: no "
1372 			"need to remove (fini = %d, cp = %p, shared = %d)\n",
1373 			t, fini, cp, cp->shared);
1374 		fflush(stderr);
1375 #endif /* DEBUG */
1376 		if (use_lock)
1377 			(void) rw_unlock(&sessionPoolLock);
1378 		return;
1379 	}
1380 
1381 	if (!fini &&
1382 		((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail &&
1383 		((flag & NS_LDAP_KEEP_CONN) ||
1384 			(MTperConn == 0 && nscd_proc()) ||
1385 			MTperConn)) {
1386 #ifdef DEBUG
1387 		(void) fprintf(stderr, "tid= %d: keep alive (fini = %d "
1388 			"shared = %d)\n", t, fini, cp->shared);
1389 #endif /* DEBUG */
1390 		/* release Connection (keep alive) */
1391 		if (cp->shared)
1392 			cp->shared--;
1393 		cp->usedBit = B_FALSE;
1394 		cp->threadID = 0;	/* unmark the threadID */
1395 		if (use_lock)
1396 			(void) rw_unlock(&sessionPoolLock);
1397 	} else {
1398 		/* delete Connection (disconnect) */
1399 		if (cp->shared > 0) {
1400 #ifdef DEBUG
1401 		(void) fprintf(stderr, "tid= %d: Connection no "
1402 			"longer available (fini = %d, shared = %d)\n",
1403 			t, fini, cp->shared);
1404 		fflush(stderr);
1405 #endif /* DEBUG */
1406 			cp->shared--;
1407 			cp->notAvail = 1;
1408 		}
1409 
1410 		if (cp->shared <= 0) {
1411 #ifdef DEBUG
1412 			(void) fprintf(stderr, "tid= %d: unbind "
1413 				"(fini = %d, shared = %d)\n",
1414 				t, fini, cp->shared);
1415 			fflush(stderr);
1416 #endif /* DEBUG */
1417 			sessionPool[id] = NULL;
1418 			(void) ldap_unbind(cp->ld);
1419 			freeConnection(cp);
1420 		}
1421 
1422 		if (use_lock)
1423 			(void) rw_unlock(&sessionPoolLock);
1424 	}
1425 }
1426 
1427 void
1428 DropConnection(ConnectionID cID, int flag)
1429 {
1430 	_DropConnection(cID, flag, 0);
1431 }
1432 
1433 /*
1434  * This routine is called after a bind operation is
1435  * done in openConnection() to process the password
1436  * management information, if any.
1437  *
1438  * Input:
1439  *   bind_type: "simple" or "sasl/DIGEST-MD5"
1440  *   ldaprc   : ldap rc from the ldap bind operation
1441  *   controls : controls returned by the server
1442  *   errmsg   : error message from the server
1443  *   fail_if_new_pwd_reqd:
1444  *              flag indicating if connection should be open
1445  *              when password needs to change immediately
1446  *   passwd_mgmt:
1447  *              flag indicating if server supports password
1448  *              policy/management
1449  *
1450  * Output     : ns_ldap_error structure, which may contain
1451  *              password status and number of seconds until
1452  *              expired
1453  *
1454  * return rc:
1455  * NS_LDAP_EXTERNAL: error, connection should not open
1456  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
1457  * NS_LDAP_SUCCESS: OK to open connection
1458  *
1459  */
1460 
1461 static int
1462 process_pwd_mgmt(char *bind_type, int ldaprc,
1463 		LDAPControl **controls,
1464 		char *errmsg, ns_ldap_error_t **errorp,
1465 		int fail_if_new_pwd_reqd,
1466 		int passwd_mgmt)
1467 {
1468 	char		errstr[MAXERROR];
1469 	LDAPControl	**ctrl = NULL;
1470 	int		exit_rc;
1471 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
1472 	int		sec_until_exp = 0;
1473 
1474 	/*
1475 	 * errmsg may be an empty string,
1476 	 * even if ldaprc is LDAP_SUCCESS,
1477 	 * free the empty string if that's the case
1478 	 */
1479 	if (errmsg &&
1480 		(*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
1481 		ldap_memfree(errmsg);
1482 		errmsg = NULL;
1483 	}
1484 
1485 	if (ldaprc != LDAP_SUCCESS) {
1486 		/*
1487 		 * try to map ldap rc and error message to
1488 		 * a password status
1489 		 */
1490 		if (errmsg) {
1491 			if (passwd_mgmt)
1492 				pwd_status =
1493 					__s_api_set_passwd_status(
1494 					ldaprc, errmsg);
1495 			ldap_memfree(errmsg);
1496 		}
1497 
1498 		(void) snprintf(errstr, sizeof (errstr),
1499 			gettext("openConnection: "
1500 			"%s bind failed "
1501 			"- %s"), bind_type, ldap_err2string(ldaprc));
1502 
1503 		if (pwd_status != NS_PASSWD_GOOD) {
1504 			MKERROR_PWD_MGMT(*errorp,
1505 				ldaprc, strdup(errstr),
1506 				pwd_status, 0, NULL);
1507 		} else {
1508 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
1509 				NULL);
1510 		}
1511 		if (controls)
1512 			ldap_controls_free(controls);
1513 
1514 		return (NS_LDAP_INTERNAL);
1515 	}
1516 
1517 	/*
1518 	 * ldaprc is LDAP_SUCCESS,
1519 	 * process the password management controls, if any
1520 	 */
1521 	exit_rc = NS_LDAP_SUCCESS;
1522 	if (controls && passwd_mgmt) {
1523 		/*
1524 		 * The control with the OID
1525 		 * 2.16.840.1.113730.3.4.4 (or
1526 		 * LDAP_CONTROL_PWEXPIRED, as defined
1527 		 * in the ldap.h header file) is the
1528 		 * expired password control.
1529 		 *
1530 		 * This control is used if the server
1531 		 * is configured to require users to
1532 		 * change their passwords when first
1533 		 * logging in and whenever the
1534 		 * passwords are reset.
1535 		 *
1536 		 * If the user is logging in for the
1537 		 * first time or if the user's
1538 		 * password has been reset, the
1539 		 * server sends this control to
1540 		 * indicate that the client needs to
1541 		 * change the password immediately.
1542 		 *
1543 		 * At this point, the only operation
1544 		 * that the client can perform is to
1545 		 * change the user's password. If the
1546 		 * client requests any other LDAP
1547 		 * operation, the server sends back
1548 		 * an LDAP_UNWILLING_TO_PERFORM
1549 		 * result code with an expired
1550 		 * password control.
1551 		 *
1552 		 * The control with the OID
1553 		 * 2.16.840.1.113730.3.4.5 (or
1554 		 * LDAP_CONTROL_PWEXPIRING, as
1555 		 * defined in the ldap.h header file)
1556 		 * is the password expiration warning
1557 		 * control.
1558 		 *
1559 		 * This control is used if the server
1560 		 * is configured to expire user
1561 		 * passwords after a certain amount
1562 		 * of time.
1563 		 *
1564 		 * The server sends this control back
1565 		 * to the client if the client binds
1566 		 * using a password that will soon
1567 		 * expire.  The ldctl_value field of
1568 		 * the LDAPControl structure
1569 		 * specifies the number of seconds
1570 		 * before the password will expire.
1571 		 */
1572 		for (ctrl = controls; *ctrl; ctrl++) {
1573 
1574 			if (strcmp((*ctrl)->ldctl_oid,
1575 				LDAP_CONTROL_PWEXPIRED) == 0) {
1576 				/*
1577 				 * if the caller wants this bind
1578 				 * to fail, set up the error info.
1579 				 * If call to this function is
1580 				 * for searching the LDAP directory,
1581 				 * e.g., __ns_ldap_list(),
1582 				 * there's really no sense to
1583 				 * let a connection open and
1584 				 * then fail immediately afterward
1585 				 * on the LDAP search operation with
1586 				 * the LDAP_UNWILLING_TO_PERFORM rc
1587 				 */
1588 				pwd_status =
1589 					NS_PASSWD_CHANGE_NEEDED;
1590 				if (fail_if_new_pwd_reqd) {
1591 					(void) snprintf(errstr,
1592 						sizeof (errstr),
1593 						gettext(
1594 						"openConnection: "
1595 						"%s bind "
1596 						"failed "
1597 						"- password "
1598 						"expired. It "
1599 						" needs to change "
1600 						"immediately!"),
1601 						bind_type);
1602 					MKERROR_PWD_MGMT(*errorp,
1603 						LDAP_SUCCESS,
1604 						strdup(errstr),
1605 						pwd_status,
1606 						0,
1607 						NULL);
1608 					exit_rc = NS_LDAP_INTERNAL;
1609 				} else {
1610 					MKERROR_PWD_MGMT(*errorp,
1611 						LDAP_SUCCESS,
1612 						NULL,
1613 						pwd_status,
1614 						0,
1615 						NULL);
1616 					exit_rc =
1617 					NS_LDAP_SUCCESS_WITH_INFO;
1618 				}
1619 				break;
1620 			} else if (strcmp((*ctrl)->ldctl_oid,
1621 				LDAP_CONTROL_PWEXPIRING) == 0) {
1622 				pwd_status =
1623 					NS_PASSWD_ABOUT_TO_EXPIRE;
1624 				if ((*ctrl)->
1625 					ldctl_value.bv_len > 0 &&
1626 					(*ctrl)->
1627 						ldctl_value.bv_val)
1628 					sec_until_exp =
1629 						atoi((*ctrl)->
1630 						ldctl_value.bv_val);
1631 				MKERROR_PWD_MGMT(*errorp,
1632 					LDAP_SUCCESS,
1633 					NULL,
1634 					pwd_status,
1635 					sec_until_exp,
1636 					NULL);
1637 				exit_rc =
1638 					NS_LDAP_SUCCESS_WITH_INFO;
1639 				break;
1640 			}
1641 		}
1642 	}
1643 
1644 	if (controls)
1645 		ldap_controls_free(controls);
1646 
1647 	return (exit_rc);
1648 }
1649 
1650 static int
1651 ldap_in_hosts_switch()
1652 {
1653 	enum __nsw_parse_err		pserr;
1654 	struct __nsw_switchconfig	*conf;
1655 	struct __nsw_lookup		*lkp;
1656 	const char			*name;
1657 	int				found = 0;
1658 
1659 	conf = __nsw_getconfig("hosts", &pserr);
1660 	if (conf == NULL) {
1661 		return (-1);
1662 	}
1663 
1664 	/* check for skip and count other backends */
1665 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
1666 		name = lkp->service_name;
1667 		if (strcmp(name, "ldap") == 0) {
1668 			found = 1;
1669 			break;
1670 		}
1671 	}
1672 	__nsw_freeconfig(conf);
1673 	return (found);
1674 }
1675 
1676 static int
1677 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
1678 	int timeoutSec, ns_ldap_error_t **errorp,
1679 	int fail_if_new_pwd_reqd, int passwd_mgmt)
1680 {
1681 	LDAP		*ld = NULL;
1682 	char		*binddn, *passwd;
1683 	char		*digest_md5_name;
1684 	const char	*s;
1685 	int		ldapVersion = LDAP_VERSION3;
1686 	int		derefOption = LDAP_DEREF_ALWAYS;
1687 	int		zero = 0;
1688 	int		rc;
1689 	char		errstr[MAXERROR];
1690 	int		errnum = 0;
1691 	LDAPMessage	*resultMsg;
1692 	int		msgId;
1693 	int		useSSL = 0, port = 0;
1694 	struct timeval	tv;
1695 	AuthType_t	bindType;
1696 	int		timeoutMilliSec = timeoutSec * 1000;
1697 	struct berval	cred;
1698 	char		*sslServerAddr;
1699 	char		*s1;
1700 	char		*errmsg, *end = NULL;
1701 	LDAPControl	**controls;
1702 	int		pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
1703 	ns_sasl_cb_param_t	sasl_param;
1704 
1705 	*errorp = NULL;
1706 	*ldp = NULL;
1707 
1708 	switch (auth->auth.type) {
1709 		case NS_LDAP_AUTH_NONE:
1710 		case NS_LDAP_AUTH_SIMPLE:
1711 		case NS_LDAP_AUTH_SASL:
1712 			bindType = auth->auth.type;
1713 			break;
1714 		case NS_LDAP_AUTH_TLS:
1715 			useSSL = 1;
1716 			switch (auth->auth.tlstype) {
1717 				case NS_LDAP_TLS_NONE:
1718 					bindType = NS_LDAP_AUTH_NONE;
1719 					break;
1720 				case NS_LDAP_TLS_SIMPLE:
1721 					bindType = NS_LDAP_AUTH_SIMPLE;
1722 					break;
1723 				case NS_LDAP_TLS_SASL:
1724 					bindType = NS_LDAP_AUTH_SASL;
1725 					break;
1726 				default:
1727 					(void) sprintf(errstr,
1728 					gettext("openConnection: unsupported "
1729 						"TLS authentication method "
1730 						"(%d)"), auth->auth.tlstype);
1731 					MKERROR(LOG_WARNING, *errorp,
1732 						LDAP_AUTH_METHOD_NOT_SUPPORTED,
1733 						strdup(errstr), NULL);
1734 					return (NS_LDAP_INTERNAL);
1735 			}
1736 			break;
1737 		default:
1738 			(void) sprintf(errstr,
1739 				gettext("openConnection: unsupported "
1740 				"authentication method (%d)"), auth->auth.type);
1741 			MKERROR(LOG_WARNING, *errorp,
1742 				LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
1743 				NULL);
1744 			return (NS_LDAP_INTERNAL);
1745 	}
1746 
1747 	if (useSSL) {
1748 		const char	*hostcertpath;
1749 		char		*alloc_hcp = NULL;
1750 #ifdef DEBUG
1751 		(void) fprintf(stderr, "tid= %d: +++TLS transport\n",
1752 			thr_self());
1753 #endif /* DEBUG */
1754 
1755 		if (prldap_set_session_option(NULL, NULL,
1756 		    PRLDAP_OPT_IO_MAX_TIMEOUT,
1757 		    timeoutMilliSec) != LDAP_SUCCESS) {
1758 			(void) snprintf(errstr, sizeof (errstr),
1759 				gettext("openConnection: failed to initialize "
1760 				"TLS security"));
1761 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1762 				strdup(errstr), NULL);
1763 			return (NS_LDAP_INTERNAL);
1764 		}
1765 
1766 		hostcertpath = auth->hostcertpath;
1767 		if (hostcertpath == NULL) {
1768 			alloc_hcp = __s_get_hostcertpath();
1769 			hostcertpath = alloc_hcp;
1770 		}
1771 
1772 		if (hostcertpath == NULL)
1773 			return (NS_LDAP_MEMORY);
1774 
1775 		if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
1776 			if (alloc_hcp)
1777 				free(alloc_hcp);
1778 			(void) snprintf(errstr, sizeof (errstr),
1779 				gettext("openConnection: failed to initialize "
1780 				"TLS security (%s)"),
1781 				ldapssl_err2string(rc));
1782 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1783 				strdup(errstr), NULL);
1784 			return (NS_LDAP_INTERNAL);
1785 		}
1786 		if (alloc_hcp)
1787 			free(alloc_hcp);
1788 
1789 		/* determine if the host name contains a port number */
1790 		s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
1791 		if (s == NULL)
1792 			s = serverAddr;
1793 		s = strchr(s, ':');
1794 		if (s != NULL) {
1795 			/*
1796 			 * If we do get a port number, we will try stripping
1797 			 * it. At present, referrals will always have a
1798 			 * port number.
1799 			 */
1800 			sslServerAddr = strdup(serverAddr);
1801 			if (sslServerAddr == NULL)
1802 				return (NS_LDAP_MEMORY);
1803 			s1 = strrchr(sslServerAddr, ':');
1804 			if (s1 != NULL)
1805 				*s1 = '\0';
1806 			(void) snprintf(errstr, sizeof (errstr),
1807 			    gettext("openConnection: cannot use tls with %s. "
1808 				"Trying %s"),
1809 				serverAddr, sslServerAddr);
1810 			syslog(LOG_ERR, "libsldap: %s", errstr);
1811 		} else
1812 			sslServerAddr = (char *)serverAddr;
1813 
1814 		ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
1815 
1816 		if (sslServerAddr != serverAddr)
1817 			free(sslServerAddr);
1818 
1819 		if (ld == NULL ||
1820 		    ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
1821 			(void) snprintf(errstr, sizeof (errstr),
1822 				gettext("openConnection: failed to connect "
1823 				"using TLS (%s)"), strerror(errno));
1824 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1825 				strdup(errstr), NULL);
1826 			return (NS_LDAP_INTERNAL);
1827 		}
1828 	} else {
1829 #ifdef DEBUG
1830 		(void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
1831 				thr_self());
1832 #endif /* DEBUG */
1833 		port = LDAP_PORT;
1834 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI &&
1835 			(end = strchr(serverAddr, ':')) != NULL) {
1836 			/*
1837 			 * The IP is converted to hostname so it's a
1838 			 * hostname:port up to this point.
1839 			 *
1840 			 * libldap passes hostname:port to the sasl layer.
1841 			 * The ldap service principal is constructed as
1842 			 * ldap/hostname:port@REALM. Kerberos authentication
1843 			 * will fail. So it needs to be parsed to construct
1844 			 * a valid principal ldap/hostname@REALM.
1845 			 *
1846 			 * For useSSL case above, it already parses port so
1847 			 * no need to parse serverAddr
1848 			 */
1849 			*end = '\0';
1850 			port = atoi(end + 1);
1851 		}
1852 
1853 		/* Warning message IF cannot connect to host(s) */
1854 		if ((ld = ldap_init((char *)serverAddr, port)) == NULL) {
1855 			char *p = strerror(errno);
1856 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1857 				strdup(p), NULL);
1858 			if (end)
1859 				*end = ':';
1860 			return (NS_LDAP_INTERNAL);
1861 		} else {
1862 			if (end)
1863 				*end = ':';
1864 			/* check and avoid gethostname recursion */
1865 			if (ldap_in_hosts_switch() > 0 &&
1866 				! __s_api_isipv4((char *)serverAddr) &&
1867 				! __s_api_isipv6((char *)serverAddr)) {
1868 				/* host: ldap - found, attempt to recover */
1869 				if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
1870 						    "ldap") != 0) {
1871 				    (void) snprintf(errstr, sizeof (errstr),
1872 					    gettext("openConnection: "
1873 					    "unrecoverable gethostname "
1874 					    "recursion detected "
1875 					    "in /etc/nsswitch.conf"));
1876 				    MKERROR(LOG_WARNING, *errorp,
1877 					    LDAP_CONNECT_ERROR,
1878 					    strdup(errstr), NULL);
1879 				    (void) ldap_unbind(ld);
1880 				    return (NS_LDAP_INTERNAL);
1881 				}
1882 			}
1883 		}
1884 	}
1885 
1886 	ns_setup_mt_conn_and_tsd(ld);
1887 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
1888 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
1889 	/*
1890 	 * set LDAP_OPT_REFERRALS to OFF.
1891 	 * This library will handle the referral itself
1892 	 * based on API flags or configuration file
1893 	 * specification. If this option is not set
1894 	 * to OFF, libldap will never pass the
1895 	 * referral info up to this library
1896 	 */
1897 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1898 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
1899 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
1900 	/* setup TCP/IP connect timeout */
1901 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
1902 							&timeoutMilliSec);
1903 	/* retry if LDAP I/O was interrupted */
1904 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1905 
1906 	switch (bindType) {
1907 	case NS_LDAP_AUTH_NONE:
1908 #ifdef DEBUG
1909 		(void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
1910 			thr_self());
1911 #endif /* DEBUG */
1912 		break;
1913 	case NS_LDAP_AUTH_SIMPLE:
1914 		binddn = auth->cred.unix_cred.userID;
1915 		passwd = auth->cred.unix_cred.passwd;
1916 		if (passwd == NULL || *passwd == '\0' ||
1917 		    binddn == NULL || *binddn == '\0') {
1918 			(void) sprintf(errstr, gettext("openConnection: "
1919 				"missing credentials for Simple bind"));
1920 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
1921 				strdup(errstr), NULL);
1922 			(void) ldap_unbind(ld);
1923 			return (NS_LDAP_INTERNAL);
1924 		}
1925 
1926 #ifdef DEBUG
1927 		(void) fprintf(stderr, "tid= %d: +++Simple bind\n",
1928 			thr_self());
1929 #endif /* DEBUG */
1930 		msgId = ldap_simple_bind(ld, binddn, passwd);
1931 
1932 		if (msgId == -1) {
1933 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
1934 				(void *)&errnum);
1935 			(void) snprintf(errstr, sizeof (errstr),
1936 				gettext("openConnection: simple bind failed "
1937 				"- %s"), ldap_err2string(errnum));
1938 			(void) ldap_unbind(ld);
1939 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
1940 				NULL);
1941 			return (NS_LDAP_INTERNAL);
1942 		}
1943 
1944 		tv.tv_sec = timeoutSec;
1945 		tv.tv_usec = 0;
1946 		rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
1947 
1948 		if ((rc == -1) || (rc == 0)) {
1949 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
1950 				(void *)&errnum);
1951 			(void) snprintf(errstr, sizeof (errstr),
1952 				gettext("openConnection: simple bind failed "
1953 				"- %s"), ldap_err2string(errnum));
1954 			(void) ldap_msgfree(resultMsg);
1955 			(void) ldap_unbind(ld);
1956 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
1957 				NULL);
1958 			return (NS_LDAP_INTERNAL);
1959 		}
1960 
1961 		/*
1962 		 * get ldaprc, controls, and error msg
1963 		 */
1964 		rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
1965 				&errmsg, NULL, &controls, 1);
1966 
1967 		if (rc != LDAP_SUCCESS) {
1968 			(void) snprintf(errstr, sizeof (errstr),
1969 				gettext("openConnection: simple bind failed "
1970 				"- unable to parse result"));
1971 			(void) ldap_unbind(ld);
1972 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
1973 					strdup(errstr), NULL);
1974 			return (NS_LDAP_INTERNAL);
1975 		}
1976 
1977 		/* process the password management info, if any */
1978 		pwd_rc = process_pwd_mgmt("simple",
1979 					errnum, controls, errmsg,
1980 					errorp,
1981 					fail_if_new_pwd_reqd,
1982 					passwd_mgmt);
1983 
1984 		if (pwd_rc == NS_LDAP_INTERNAL) {
1985 			(void) ldap_unbind(ld);
1986 			return (pwd_rc);
1987 		}
1988 
1989 		if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
1990 			*ldp = ld;
1991 			return (pwd_rc);
1992 		}
1993 
1994 		break;
1995 	case NS_LDAP_AUTH_SASL:
1996 		if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
1997 				auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
1998 			(void) sprintf(errstr,
1999 				gettext("openConnection: SASL options are "
2000 				"not supported (%d) for non-GSSAPI sasl bind"),
2001 				auth->auth.saslopt);
2002 			MKERROR(LOG_WARNING, *errorp,
2003 				LDAP_AUTH_METHOD_NOT_SUPPORTED,
2004 				strdup(errstr), NULL);
2005 			(void) ldap_unbind(ld);
2006 			return (NS_LDAP_INTERNAL);
2007 		}
2008 		if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2009 			binddn = auth->cred.unix_cred.userID;
2010 			passwd = auth->cred.unix_cred.passwd;
2011 			if (passwd == NULL || *passwd == '\0' ||
2012 				binddn == NULL || *binddn == '\0') {
2013 				(void) sprintf(errstr,
2014 				gettext("openConnection: missing credentials "
2015 				"for SASL bind"));
2016 				MKERROR(LOG_WARNING, *errorp,
2017 					LDAP_INVALID_CREDENTIALS,
2018 					strdup(errstr), NULL);
2019 				(void) ldap_unbind(ld);
2020 				return (NS_LDAP_INTERNAL);
2021 			}
2022 			cred.bv_val = passwd;
2023 			cred.bv_len = strlen(passwd);
2024 		}
2025 
2026 		switch (auth->auth.saslmech) {
2027 		case NS_LDAP_SASL_CRAM_MD5:
2028 			/*
2029 			 * NOTE: if iDS changes to support cram_md5,
2030 			 * please add password management code here.
2031 			 * Since ldap_sasl_cram_md5_bind_s does not
2032 			 * return anything that could be used to
2033 			 * extract the ldap rc/errmsg/control to
2034 			 * determine if bind failed due to password
2035 			 * policy, a new cram_md5_bind API will need
2036 			 * to be introduced. See
2037 			 * ldap_x_sasl_digest_md5_bind() and case
2038 			 * NS_LDAP_SASL_DIGEST_MD5 below for details.
2039 			 */
2040 			if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
2041 				&cred, NULL, NULL)) != LDAP_SUCCESS) {
2042 				(void) ldap_get_option(ld,
2043 					LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2044 				(void) snprintf(errstr, sizeof (errstr),
2045 					gettext("openConnection: "
2046 					"sasl/CRAM-MD5 bind failed - %s"),
2047 					ldap_err2string(errnum));
2048 				MKERROR(LOG_WARNING, *errorp, errnum,
2049 					strdup(errstr), NULL);
2050 				(void) ldap_unbind(ld);
2051 				return (NS_LDAP_INTERNAL);
2052 			}
2053 			break;
2054 		case NS_LDAP_SASL_DIGEST_MD5:
2055 			digest_md5_name = malloc(strlen(binddn) + 5);
2056 			/* 5 = strlen("dn: ") + 1 */
2057 			if (digest_md5_name == NULL) {
2058 				(void) ldap_unbind(ld);
2059 				return (NS_LDAP_MEMORY);
2060 			}
2061 			(void) strcpy(digest_md5_name, "dn: ");
2062 			(void) strcat(digest_md5_name, binddn);
2063 
2064 			tv.tv_sec = timeoutSec;
2065 			tv.tv_usec = 0;
2066 			rc = ldap_x_sasl_digest_md5_bind(ld,
2067 				digest_md5_name, &cred, NULL, NULL,
2068 				&tv, &resultMsg);
2069 
2070 			if (resultMsg == NULL) {
2071 				free(digest_md5_name);
2072 				(void) ldap_get_option(ld,
2073 					LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2074 				(void) snprintf(errstr, sizeof (errstr),
2075 					gettext("openConnection: "
2076 					"DIGEST-MD5 bind failed - %s"),
2077 					ldap_err2string(errnum));
2078 				(void) ldap_unbind(ld);
2079 				MKERROR(LOG_WARNING, *errorp, errnum,
2080 						strdup(errstr), NULL);
2081 				return (NS_LDAP_INTERNAL);
2082 			}
2083 
2084 			/*
2085 			 * get ldaprc, controls, and error msg
2086 			 */
2087 			rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2088 				&errmsg, NULL, &controls, 1);
2089 
2090 			if (rc != LDAP_SUCCESS) {
2091 				free(digest_md5_name);
2092 				(void) snprintf(errstr, sizeof (errstr),
2093 					gettext("openConnection: "
2094 					"DIGEST-MD5 bind failed "
2095 					"- unable to parse result"));
2096 				(void) ldap_unbind(ld);
2097 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2098 						strdup(errstr), NULL);
2099 				return (NS_LDAP_INTERNAL);
2100 			}
2101 
2102 			/* process the password management info, if any */
2103 			pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
2104 						errnum, controls, errmsg,
2105 						errorp,
2106 						fail_if_new_pwd_reqd,
2107 						passwd_mgmt);
2108 
2109 			if (pwd_rc == NS_LDAP_INTERNAL) {
2110 				free(digest_md5_name);
2111 				(void) ldap_unbind(ld);
2112 				return (pwd_rc);
2113 			}
2114 
2115 			if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
2116 				*ldp = ld;
2117 				return (pwd_rc);
2118 			}
2119 
2120 			free(digest_md5_name);
2121 			break;
2122 		case NS_LDAP_SASL_GSSAPI:
2123 			if (sasl_gssapi_inited == 0) {
2124 				rc = __s_api_sasl_gssapi_init();
2125 				if (rc != NS_LDAP_SUCCESS) {
2126 					(void) snprintf(errstr, sizeof (errstr),
2127 						gettext("openConnection: "
2128 						"GSSAPI initialization "
2129 						"failed"));
2130 					(void) ldap_unbind(ld);
2131 					MKERROR(LOG_WARNING, *errorp, rc,
2132 						strdup(errstr), NULL);
2133 					return (rc);
2134 				}
2135 			}
2136 			(void) memset(&sasl_param, 0,
2137 					sizeof (ns_sasl_cb_param_t));
2138 			sasl_param.authid = NULL;
2139 			sasl_param.authzid = "";
2140 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
2141 						(void *)&min_ssf);
2142 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
2143 						(void *)&max_ssf);
2144 
2145 			rc = ldap_sasl_interactive_bind_s(
2146 				ld, NULL, "GSSAPI",
2147 				NULL, NULL, LDAP_SASL_INTERACTIVE,
2148 				__s_api_sasl_bind_callback,
2149 				&sasl_param);
2150 
2151 			if (rc != LDAP_SUCCESS) {
2152 				(void) snprintf(errstr, sizeof (errstr),
2153 					gettext("openConnection: "
2154 					"GSSAPI bind failed "
2155 					"- %d %s"), rc, ldap_err2string(rc));
2156 				(void) ldap_unbind(ld);
2157 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2158 						strdup(errstr), NULL);
2159 				return (NS_LDAP_INTERNAL);
2160 			}
2161 
2162 			break;
2163 		default:
2164 			(void) ldap_unbind(ld);
2165 			(void) sprintf(errstr,
2166 				gettext("openConnection: unsupported SASL "
2167 				"mechanism (%d)"), auth->auth.saslmech);
2168 			MKERROR(LOG_WARNING, *errorp,
2169 				LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2170 				NULL);
2171 			return (NS_LDAP_INTERNAL);
2172 		}
2173 	}
2174 
2175 	*ldp = ld;
2176 	return (NS_LDAP_SUCCESS);
2177 }
2178 
2179 /*
2180  * FUNCTION:	__s_api_getDefaultAuth
2181  *
2182  *	Constructs a credential for authentication using the config module.
2183  *
2184  * RETURN VALUES:
2185  *
2186  * NS_LDAP_SUCCESS	If successful
2187  * NS_LDAP_CONFIG	If there are any config errors.
2188  * NS_LDAP_MEMORY	Memory errors.
2189  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
2190  *			not build a new authp.
2191  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
2192  *			necessary fields of a cred for a given auth method
2193  *			are not provided.
2194  * INPUT:
2195  *
2196  * cLevel	Currently requested credential level to be tried
2197  *
2198  * aMethod	Currently requested authentication method to be tried
2199  *
2200  * OUTPUT:
2201  *
2202  * authp		authentication method to use.
2203  */
2204 static int
2205 __s_api_getDefaultAuth(
2206 	int	*cLevel,
2207 	ns_auth_t *aMethod,
2208 	ns_cred_t **authp)
2209 {
2210 	void		**paramVal = NULL;
2211 	char		*modparamVal = NULL;
2212 	int		getUid = 0;
2213 	int		getPasswd = 0;
2214 	int		getCertpath = 0;
2215 	int		rc = 0;
2216 	ns_ldap_error_t	*errorp = NULL;
2217 
2218 #ifdef DEBUG
2219 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
2220 #endif
2221 
2222 	if (aMethod == NULL) {
2223 		/* Require an Auth */
2224 		return (NS_LDAP_INVALID_PARAM);
2225 
2226 	}
2227 	/*
2228 	 * credential level "self" can work with auth method sasl/GSSAPI only
2229 	 */
2230 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
2231 			aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
2232 		return (NS_LDAP_INVALID_PARAM);
2233 
2234 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
2235 	if ((*authp) == NULL)
2236 		return (NS_LDAP_MEMORY);
2237 
2238 	(*authp)->auth = *aMethod;
2239 
2240 	switch (aMethod->type) {
2241 		case NS_LDAP_AUTH_NONE:
2242 			return (NS_LDAP_SUCCESS);
2243 		case NS_LDAP_AUTH_SIMPLE:
2244 			getUid++;
2245 			getPasswd++;
2246 			break;
2247 		case NS_LDAP_AUTH_SASL:
2248 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
2249 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
2250 				getUid++;
2251 				getPasswd++;
2252 			} else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
2253 				(void) __ns_ldap_freeCred(authp);
2254 				*authp = NULL;
2255 				return (NS_LDAP_INVALID_PARAM);
2256 			}
2257 			break;
2258 		case NS_LDAP_AUTH_TLS:
2259 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
2260 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
2261 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
2262 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
2263 				getUid++;
2264 				getPasswd++;
2265 				getCertpath++;
2266 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
2267 				getCertpath++;
2268 			} else {
2269 				(void) __ns_ldap_freeCred(authp);
2270 				*authp = NULL;
2271 				return (NS_LDAP_INVALID_PARAM);
2272 			}
2273 			break;
2274 	}
2275 
2276 	if (getUid) {
2277 		paramVal = NULL;
2278 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
2279 			&paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2280 			(void) __ns_ldap_freeCred(authp);
2281 			(void) __ns_ldap_freeError(&errorp);
2282 			*authp = NULL;
2283 			return (rc);
2284 		}
2285 
2286 		if (paramVal == NULL || *paramVal == NULL) {
2287 			(void) __ns_ldap_freeCred(authp);
2288 			*authp = NULL;
2289 			return (NS_LDAP_INVALID_PARAM);
2290 		}
2291 
2292 		(*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
2293 		(void) __ns_ldap_freeParam(&paramVal);
2294 		if ((*authp)->cred.unix_cred.userID == NULL) {
2295 			(void) __ns_ldap_freeCred(authp);
2296 			*authp = NULL;
2297 			return (NS_LDAP_MEMORY);
2298 		}
2299 	}
2300 	if (getPasswd) {
2301 		paramVal = NULL;
2302 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
2303 			&paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2304 			(void) __ns_ldap_freeCred(authp);
2305 			(void) __ns_ldap_freeError(&errorp);
2306 			*authp = NULL;
2307 			return (rc);
2308 		}
2309 
2310 		if (paramVal == NULL || *paramVal == NULL) {
2311 			(void) __ns_ldap_freeCred(authp);
2312 			*authp = NULL;
2313 			return (NS_LDAP_INVALID_PARAM);
2314 		}
2315 
2316 		modparamVal = dvalue((char *)*paramVal);
2317 		(void) __ns_ldap_freeParam(&paramVal);
2318 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
2319 			(void) __ns_ldap_freeCred(authp);
2320 			if (modparamVal != NULL)
2321 				free(modparamVal);
2322 			*authp = NULL;
2323 			return (NS_LDAP_INVALID_PARAM);
2324 		}
2325 
2326 		(*authp)->cred.unix_cred.passwd = modparamVal;
2327 	}
2328 	if (getCertpath) {
2329 		paramVal = NULL;
2330 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
2331 			&paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2332 			(void) __ns_ldap_freeCred(authp);
2333 			(void) __ns_ldap_freeError(&errorp);
2334 			*authp = NULL;
2335 			return (rc);
2336 		}
2337 
2338 		if (paramVal == NULL || *paramVal == NULL) {
2339 			(void) __ns_ldap_freeCred(authp);
2340 			*authp = NULL;
2341 			return (NS_LDAP_INVALID_PARAM);
2342 		}
2343 
2344 		(*authp)->hostcertpath = strdup((char *)*paramVal);
2345 		(void) __ns_ldap_freeParam(&paramVal);
2346 		if ((*authp)->hostcertpath == NULL) {
2347 			(void) __ns_ldap_freeCred(authp);
2348 			*authp = NULL;
2349 			return (NS_LDAP_MEMORY);
2350 		}
2351 	}
2352 	return (NS_LDAP_SUCCESS);
2353 }
2354 
2355 /*
2356  * FUNCTION:	__s_api_getConnection
2357  *
2358  *	Bind to the specified server or one from the server
2359  *	list and return the pointer.
2360  *
2361  *	This function can rebind or not (NS_LDAP_HARD), it can require a
2362  *	credential or bind anonymously
2363  *
2364  *	This function follows the DUA configuration schema algorithm
2365  *
2366  * RETURN VALUES:
2367  *
2368  * NS_LDAP_SUCCESS	A connection was made successfully.
2369  * NS_LDAP_SUCCESS_WITH_INFO
2370  * 			A connection was made successfully, but with
2371  *			password management info in *errorp
2372  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
2373  * NS_LDAP_CONFIG	If there are any config errors.
2374  * NS_LDAP_MEMORY	Memory errors.
2375  * NS_LDAP_INTERNAL	If there was a ldap error.
2376  *
2377  * INPUT:
2378  *
2379  * server	Bind to this LDAP server only
2380  * flags	If NS_LDAP_HARD is set function will not return until it has
2381  *		a connection unless there is a authentication problem.
2382  *		If NS_LDAP_NEW_CONN is set the function must force a new
2383  *              connection to be created
2384  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
2385  * auth		Credentials for bind. This could be NULL in which case
2386  *		a default cred built from the config module is used.
2387  * sessionId	cookie that points to a previous session
2388  * fail_if_new_pwd_reqd
2389  *		a flag indicating this function should fail if the passwd
2390  *		in auth needs to change immediately
2391  * nopasswd_acct_mgmt
2392  *		a flag indicating that makeConnection should check before
2393  *		binding if server supports LDAP V3 password less
2394  *		account management
2395  *
2396  * OUTPUT:
2397  *
2398  * session	pointer to a session with connection information
2399  * errorp	Set if there are any INTERNAL, or CONFIG error.
2400  */
2401 int
2402 __s_api_getConnection(
2403 	const char *server,
2404 	const int flags,
2405 	const ns_cred_t *cred,		/* credentials for bind */
2406 	ConnectionID *sessionId,
2407 	Connection **session,
2408 	ns_ldap_error_t **errorp,
2409 	int fail_if_new_pwd_reqd,
2410 	int nopasswd_acct_mgmt)
2411 {
2412 	char		errmsg[MAXERROR];
2413 	ns_auth_t	**aMethod = NULL;
2414 	ns_auth_t	**aNext = NULL;
2415 	int		**cLevel = NULL;
2416 	int		**cNext = NULL;
2417 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
2418 	int		rc;
2419 	Connection	*con = NULL;
2420 	int		sec = 1;
2421 	ns_cred_t 	*authp = NULL;
2422 	ns_cred_t	anon;
2423 	int		version = NS_LDAP_V2, self_gssapi_only = 0;
2424 	void		**paramVal = NULL;
2425 	char		**badSrvrs = NULL; /* List of problem hostnames */
2426 
2427 	if ((session == NULL) || (sessionId == NULL)) {
2428 		return (NS_LDAP_INVALID_PARAM);
2429 	}
2430 	*session = NULL;
2431 
2432 	/* if we already have a session id try to reuse connection */
2433 	if (*sessionId > 0) {
2434 		rc = findConnectionById(flags, cred, *sessionId, &con);
2435 		if (rc == *sessionId && con) {
2436 			*session = con;
2437 			return (NS_LDAP_SUCCESS);
2438 		}
2439 		*sessionId = 0;
2440 	}
2441 
2442 	/* get profile version number */
2443 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
2444 			&paramVal, errorp)) != NS_LDAP_SUCCESS)
2445 		return (rc);
2446 	if (paramVal == NULL) {
2447 		(void) sprintf(errmsg, gettext("getConnection: no file "
2448 			"version"));
2449 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
2450 			NS_LDAP_CONFIG);
2451 		return (NS_LDAP_CONFIG);
2452 	}
2453 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
2454 		version = NS_LDAP_V1;
2455 	(void) __ns_ldap_freeParam((void ***)&paramVal);
2456 
2457 	/* Get the bind timeout value */
2458 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
2459 	if (paramVal != NULL && *paramVal != NULL) {
2460 		timeoutSec = **((int **)paramVal);
2461 		(void) __ns_ldap_freeParam(&paramVal);
2462 	}
2463 	if (*errorp)
2464 		(void) __ns_ldap_freeError(errorp);
2465 
2466 	if (cred == NULL) {
2467 		/* Get the authentication method list */
2468 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
2469 			(void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
2470 			return (rc);
2471 		if (aMethod == NULL) {
2472 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
2473 			if (aMethod == NULL)
2474 				return (NS_LDAP_MEMORY);
2475 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
2476 			if (aMethod[0] == NULL) {
2477 				free(aMethod);
2478 				return (NS_LDAP_MEMORY);
2479 			}
2480 			if (version == NS_LDAP_V1)
2481 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
2482 			else {
2483 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
2484 				(aMethod[0])->saslmech =
2485 					NS_LDAP_SASL_DIGEST_MD5;
2486 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
2487 			}
2488 		}
2489 
2490 		/* Get the credential level list */
2491 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
2492 			(void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
2493 			(void) __ns_ldap_freeParam((void ***)&aMethod);
2494 			return (rc);
2495 		}
2496 		if (cLevel == NULL) {
2497 			cLevel = (int **)calloc(2, sizeof (int *));
2498 			if (cLevel == NULL)
2499 				return (NS_LDAP_MEMORY);
2500 			cLevel[0] = (int *)calloc(1, sizeof (int));
2501 			if (cLevel[0] == NULL)
2502 				return (NS_LDAP_MEMORY);
2503 			if (version == NS_LDAP_V1)
2504 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
2505 			else
2506 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
2507 		}
2508 	}
2509 
2510 	/* setup the anon credential for anonymous connection */
2511 	(void) memset(&anon, 0, sizeof (ns_cred_t));
2512 	anon.auth.type = NS_LDAP_AUTH_NONE;
2513 
2514 	for (; ; ) {
2515 		if (cred != NULL) {
2516 			/* using specified auth method */
2517 			rc = makeConnection(&con, server, cred,
2518 				sessionId, timeoutSec, errorp,
2519 				fail_if_new_pwd_reqd,
2520 				nopasswd_acct_mgmt, flags, &badSrvrs);
2521 			if (rc == NS_LDAP_SUCCESS ||
2522 				rc == NS_LDAP_SUCCESS_WITH_INFO) {
2523 				*session = con;
2524 				break;
2525 			}
2526 		} else {
2527 			self_gssapi_only = __s_api_self_gssapi_only_get();
2528 			/* for every cred level */
2529 			for (cNext = cLevel; *cNext != NULL; cNext++) {
2530 				if (self_gssapi_only &&
2531 					**cNext != NS_LDAP_CRED_SELF)
2532 					continue;
2533 				if (**cNext == NS_LDAP_CRED_ANON) {
2534 					/*
2535 					 * make connection anonymously
2536 					 * Free the down server list before
2537 					 * looping through
2538 					 */
2539 					if (badSrvrs && *badSrvrs) {
2540 						__s_api_free2dArray(badSrvrs);
2541 						badSrvrs = NULL;
2542 					}
2543 					rc = makeConnection(&con, server, &anon,
2544 						sessionId, timeoutSec, errorp,
2545 						fail_if_new_pwd_reqd,
2546 						nopasswd_acct_mgmt, flags,
2547 						&badSrvrs);
2548 					if (rc == NS_LDAP_SUCCESS ||
2549 						rc ==
2550 						NS_LDAP_SUCCESS_WITH_INFO) {
2551 						*session = con;
2552 						goto done;
2553 					}
2554 					continue;
2555 				}
2556 				/* for each cred level */
2557 				for (aNext = aMethod; *aNext != NULL; aNext++) {
2558 					if (self_gssapi_only &&
2559 						(*aNext)->saslmech !=
2560 						NS_LDAP_SASL_GSSAPI)
2561 						continue;
2562 					/*
2563 					 * self coexists with sasl/GSSAPI only
2564 					 * and non-self coexists with non-gssapi
2565 					 * only
2566 					 */
2567 					if ((**cNext == NS_LDAP_CRED_SELF &&
2568 						(*aNext)->saslmech !=
2569 						NS_LDAP_SASL_GSSAPI) ||
2570 						(**cNext != NS_LDAP_CRED_SELF &&
2571 						(*aNext)->saslmech ==
2572 						NS_LDAP_SASL_GSSAPI))
2573 						continue;
2574 					/* make connection and authenticate */
2575 					/* with default credentials */
2576 					authp = NULL;
2577 					rc = __s_api_getDefaultAuth(*cNext,
2578 						*aNext, &authp);
2579 					if (rc != NS_LDAP_SUCCESS) {
2580 						continue;
2581 					}
2582 					/*
2583 					 * Free the down server list before
2584 					 * looping through
2585 					 */
2586 					if (badSrvrs && *badSrvrs) {
2587 						__s_api_free2dArray(badSrvrs);
2588 						badSrvrs = NULL;
2589 					}
2590 					rc = makeConnection(&con, server, authp,
2591 						sessionId, timeoutSec, errorp,
2592 						fail_if_new_pwd_reqd,
2593 						nopasswd_acct_mgmt, flags,
2594 						&badSrvrs);
2595 					(void) __ns_ldap_freeCred(&authp);
2596 					if (rc == NS_LDAP_SUCCESS ||
2597 						rc ==
2598 						NS_LDAP_SUCCESS_WITH_INFO) {
2599 						*session = con;
2600 						goto done;
2601 					}
2602 				}
2603 			}
2604 		}
2605 		if (flags & NS_LDAP_HARD) {
2606 			if (sec < LDAPMAXHARDLOOKUPTIME)
2607 				sec *= 2;
2608 			_sleep(sec);
2609 		} else {
2610 			break;
2611 		}
2612 	}
2613 
2614 done:
2615 	/*
2616 	 * If unable to get a connection, and this is
2617 	 * the thread opening the shared connection,
2618 	 * unlock the session mutex and let other
2619 	 * threads try to get their own connection.
2620 	 */
2621 	if (wait4session != 0 && sessionTid == thr_self()) {
2622 		wait4session = 0;
2623 		sessionTid = 0;
2624 #ifdef DEBUG
2625 		(void) fprintf(stderr, "tid= %d: __s_api_getConnection: "
2626 			"unlocking sessionLock \n", thr_self());
2627 		fflush(stderr);
2628 #endif /* DEBUG */
2629 		(void) mutex_unlock(&sessionLock);
2630 	}
2631 	if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
2632 		/*
2633 		 * self_gssapi_only is true but no self/sasl/gssapi is
2634 		 * configured
2635 		 */
2636 		rc = NS_LDAP_CONFIG;
2637 	}
2638 
2639 	(void) __ns_ldap_freeParam((void ***)&aMethod);
2640 	(void) __ns_ldap_freeParam((void ***)&cLevel);
2641 
2642 	if (badSrvrs && *badSrvrs) {
2643 		/*
2644 		 * At this point, either we have a successful
2645 		 * connection or exhausted all the possible auths.
2646 		 * and creds. Mark the problem servers as down
2647 		 * so that the problem servers are not contacted
2648 		 * again until the refresh_ttl expires.
2649 		 */
2650 		(void) __s_api_removeBadServers(badSrvrs);
2651 		__s_api_free2dArray(badSrvrs);
2652 	}
2653 	return (rc);
2654 }
2655 
2656 #pragma fini(_free_sessionPool)
2657 static void
2658 _free_sessionPool()
2659 {
2660 	int id;
2661 
2662 	(void) rw_wrlock(&sessionPoolLock);
2663 	if (sessionPool != NULL) {
2664 		for (id = 0; id < sessionPoolSize; id++)
2665 			_DropConnection(id + CONID_OFFSET, 0, 1);
2666 		free(sessionPool);
2667 		sessionPool = NULL;
2668 		sessionPoolSize = 0;
2669 	}
2670 	(void) rw_unlock(&sessionPoolLock);
2671 }
2672