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