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