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