10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51687Sjanga  * Common Development and Distribution License (the "License").
61687Sjanga  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5840Smj162486  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <stdlib.h>
290Sstevel@tonic-gate #include <stdio.h>
300Sstevel@tonic-gate #include <errno.h>
310Sstevel@tonic-gate #include <string.h>
320Sstevel@tonic-gate #include <synch.h>
330Sstevel@tonic-gate #include <time.h>
340Sstevel@tonic-gate #include <libintl.h>
350Sstevel@tonic-gate #include <thread.h>
360Sstevel@tonic-gate #include <syslog.h>
370Sstevel@tonic-gate #include <sys/mman.h>
380Sstevel@tonic-gate #include <nsswitch.h>
390Sstevel@tonic-gate #include <nss_dbdefs.h>
400Sstevel@tonic-gate #include "solaris-priv.h"
412830Sdjl #include "solaris-int.h"
420Sstevel@tonic-gate #include "ns_sldap.h"
430Sstevel@tonic-gate #include "ns_internal.h"
440Sstevel@tonic-gate #include "ns_cache_door.h"
452333Sjanga #include "ldappr.h"
460Sstevel@tonic-gate #include <sys/stat.h>
470Sstevel@tonic-gate #include <fcntl.h>
480Sstevel@tonic-gate #include <procfs.h>
490Sstevel@tonic-gate #include <unistd.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate extern unsigned int _sleep(unsigned int);
520Sstevel@tonic-gate extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
530Sstevel@tonic-gate 		LDAPControl **, LDAPControl **);
540Sstevel@tonic-gate extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
550Sstevel@tonic-gate 
560Sstevel@tonic-gate static int openConnection(LDAP **, const char *, const ns_cred_t *,
570Sstevel@tonic-gate 		int, ns_ldap_error_t **, int, int);
584048Schinlong static void
594048Schinlong _DropConnection(ConnectionID cID, int flag, int fini);
602830Sdjl /*
612830Sdjl  * sessionLock, wait4session, sessionTid
622830Sdjl  * are variables to synchronize the creation/retrieval of a connection.
632830Sdjl  * MTperCon is a flag to enable/disable multiple threads sharing the same
642830Sdjl  * connection.
652830Sdjl  * sessionPoolLock is a mutex lock for the connection pool.
663387Schinlong  * sharedConnNumber is the number of sharable connections in the pool.
673387Schinlong  * sharedConnNumberLock is a mutex for sharedConnNumber.
682830Sdjl  */
692830Sdjl static mutex_t	sessionLock = DEFAULTMUTEX;
702830Sdjl static int	wait4session = 0;
712830Sdjl static thread_t sessionTid = 0;
722830Sdjl int	MTperConn = 1;
732830Sdjl static rwlock_t sessionPoolLock = DEFAULTRWLOCK;
740Sstevel@tonic-gate 
750Sstevel@tonic-gate static Connection **sessionPool = NULL;
760Sstevel@tonic-gate static int sessionPoolSize = 0;
773387Schinlong static int sharedConnNumber = 0;
783387Schinlong static mutex_t sharedConnNumberLock = DEFAULTMUTEX;
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 
810Sstevel@tonic-gate static mutex_t	nscdLock = DEFAULTMUTEX;
820Sstevel@tonic-gate static int	nscdChecked = 0;
830Sstevel@tonic-gate static pid_t	checkedPid = -1;
840Sstevel@tonic-gate static int	isNscd = 0;
852830Sdjl /*
862830Sdjl  * SSF values are for SASL integrity & privacy.
872830Sdjl  * JES DS5.2 does not support this feature but DS6 does.
882830Sdjl  * The values between 0 and 65535 can work with both server versions.
892830Sdjl  */
902830Sdjl #define	MAX_SASL_SSF	65535
912830Sdjl #define	MIN_SASL_SSF	0
920Sstevel@tonic-gate 
931687Sjanga /* Number of hostnames to allocate memory for */
941687Sjanga #define	NUMTOMALLOC	32
952830Sdjl /*
962830Sdjl  * ns_mtckey is for sharing a ldap connection among multiple
972830Sdjl  * threads; created by ns_ldap_init() in ns_init.c
982830Sdjl  */
992830Sdjl extern thread_key_t ns_mtckey;
1001687Sjanga 
1012830Sdjl /* Per thread LDAP error resides in thread-specific data. */
1022830Sdjl struct ldap_error {
1032830Sdjl 	int	le_errno;
1042830Sdjl 	char	*le_matched;
1052830Sdjl 	char	*le_errmsg;
1062830Sdjl };
1072830Sdjl 
1083428Smichen static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
1093428Smichen 
1102830Sdjl /* destructor */
1112830Sdjl void
1122830Sdjl ns_tsd_cleanup(void *key) {
1132830Sdjl 	struct ldap_error *le = (struct ldap_error *)key;
1142830Sdjl 
1152830Sdjl 	if (le == NULL)
1162830Sdjl 		return;
1172830Sdjl 	if (le->le_matched != NULL) {
1182830Sdjl 		ldap_memfree(le->le_matched);
1192830Sdjl 	}
1202830Sdjl 	if (le->le_errmsg != NULL) {
1212830Sdjl 		ldap_memfree(le->le_errmsg);
1222830Sdjl 	}
1232830Sdjl 	free(le);
1242830Sdjl }
1252830Sdjl 
1262830Sdjl /* Callback function for allocating a mutex */
1272830Sdjl static void *
1282830Sdjl ns_mutex_alloc(void)
1292830Sdjl {
1302830Sdjl 	mutex_t *mutexp = NULL;
1312830Sdjl 
1322830Sdjl 	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
1332830Sdjl 		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
1342830Sdjl 			free(mutexp);
1352830Sdjl 			mutexp = NULL;
1362830Sdjl 		}
1372830Sdjl 	}
1382830Sdjl 	return (mutexp);
1392830Sdjl }
1402830Sdjl 
1412830Sdjl /* Callback function for freeing a mutex */
1422830Sdjl static void
1432830Sdjl ns_mutex_free(void *mutexp)
1442830Sdjl {
1452830Sdjl 	(void) mutex_destroy((mutex_t *)mutexp);
1462830Sdjl 	free(mutexp);
1472830Sdjl }
1482830Sdjl 
1492830Sdjl /*
1502830Sdjl  * Function for setting up thread-specific data
1512830Sdjl  * where per thread LDAP error is stored
1522830Sdjl  */
1532830Sdjl static int
1542830Sdjl tsd_setup()
1552830Sdjl {
1562830Sdjl 	void	*tsd;
1572830Sdjl 	int	rc;
1582830Sdjl 
1592830Sdjl 	/* return success if TSD already set */
1602830Sdjl 	rc = thr_getspecific(ns_mtckey, &tsd);
1612830Sdjl 	if (rc == 0 && tsd != NULL)
1622830Sdjl 		return (0);
1632830Sdjl 
1642830Sdjl 	/* allocate and set TSD */
1652830Sdjl 	tsd = (void *) calloc(1, sizeof (struct ldap_error));
1662830Sdjl 	if (tsd == NULL)
1672830Sdjl 		return (-1);
1682830Sdjl 	rc = thr_setspecific(ns_mtckey, tsd);
1692830Sdjl 	if (rc != 0) { /* must be ENOMEM */
1702830Sdjl 		free(tsd);
1712830Sdjl 		return (-1);
1722830Sdjl 	}
1732830Sdjl 	return (0);
1742830Sdjl 
1752830Sdjl 
1762830Sdjl }
1772830Sdjl 
1782830Sdjl /* Callback function for setting the per thread LDAP error */
1792830Sdjl /*ARGSUSED*/
1802830Sdjl static void
1812830Sdjl set_ld_error(int err, char *matched, char *errmsg, void *dummy)
1822830Sdjl {
1832830Sdjl 	struct ldap_error	*le;
1842830Sdjl 
1852830Sdjl 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
1862830Sdjl 		syslog(LOG_ERR, "set_ld_error: thr_getspecific failed. errno"
1874522Schinlong 		    " %d", errno);
1882830Sdjl 		return;
1892830Sdjl 	}
1903428Smichen 
1913428Smichen 	/* play safe, do nothing if TSD pointer is NULL */
1923428Smichen 	if (le == NULL)
1933428Smichen 		return;
1943428Smichen 
1952830Sdjl 	le->le_errno = err;
1962830Sdjl 	if (le->le_matched != NULL) {
1972830Sdjl 		ldap_memfree(le->le_matched);
1982830Sdjl 	}
1992830Sdjl 	le->le_matched = matched;
2002830Sdjl 	if (le->le_errmsg != NULL) {
2012830Sdjl 		ldap_memfree(le->le_errmsg);
2022830Sdjl 	}
2032830Sdjl 	le->le_errmsg = errmsg;
2042830Sdjl }
2052830Sdjl 
2063387Schinlong int
2073387Schinlong /* check and allocate the thread-specific data for using a shared connection */
2083387Schinlong __s_api_check_MTC_tsd()
2093387Schinlong {
2103387Schinlong 	if (tsd_setup() != 0)
2113387Schinlong 		return (NS_LDAP_MEMORY);
2123387Schinlong 
2133387Schinlong 	return (NS_LDAP_SUCCESS);
2143387Schinlong }
2153387Schinlong 
2162830Sdjl /* Callback function for getting the per thread LDAP error */
2172830Sdjl /*ARGSUSED*/
2182830Sdjl static int
2192830Sdjl get_ld_error(char **matched, char **errmsg, void *dummy)
2202830Sdjl {
2212830Sdjl 	struct ldap_error	*le;
2222830Sdjl 
2232830Sdjl 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
2242830Sdjl 		syslog(LOG_ERR, "get_ld_error: thr_getspecific failed. errno"
2254522Schinlong 		    " %d", errno);
2262830Sdjl 		return (errno);
2272830Sdjl 	}
2283428Smichen 
2293428Smichen 	/* play safe, return NULL error data, if TSD pointer is NULL */
2303428Smichen 	if (le == NULL)
2313428Smichen 		le = &ldap_error_NULL;
2323428Smichen 
2332830Sdjl 	if (matched != NULL) {
2342830Sdjl 		*matched = le->le_matched;
2352830Sdjl 	}
2362830Sdjl 	if (errmsg != NULL) {
2372830Sdjl 		*errmsg = le->le_errmsg;
2382830Sdjl 	}
2392830Sdjl 	return (le->le_errno);
2402830Sdjl }
2412830Sdjl 
2422830Sdjl /* Callback function for setting per thread errno */
2432830Sdjl static void
2442830Sdjl set_errno(int err)
2452830Sdjl {
2462830Sdjl 	errno = err;
2472830Sdjl }
2482830Sdjl 
2492830Sdjl /* Callback function for getting per thread errno */
2502830Sdjl static int
2512830Sdjl get_errno(void)
2522830Sdjl {
2532830Sdjl 	return (errno);
2542830Sdjl }
2552830Sdjl 
2562830Sdjl /*
2572830Sdjl  * set up to allow multiple threads to use the same ldap connection
2582830Sdjl  */
2592830Sdjl static int
2602830Sdjl setup_mt_conn(LDAP *ld)
2612830Sdjl {
2622830Sdjl 
2632830Sdjl 	struct ldap_thread_fns		tfns;
2642830Sdjl 	struct ldap_extra_thread_fns	extrafns;
2652830Sdjl 	int				rc;
2662830Sdjl 
2672830Sdjl 	/*
2682830Sdjl 	 * Set the function pointers for dealing with mutexes
2692830Sdjl 	 * and error information
2702830Sdjl 	 */
2712830Sdjl 	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
2722830Sdjl 	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
2732830Sdjl 	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
2742830Sdjl 	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
2752830Sdjl 	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
2762830Sdjl 	tfns.ltf_get_errno = get_errno;
2772830Sdjl 	tfns.ltf_set_errno = set_errno;
2782830Sdjl 	tfns.ltf_get_lderrno = get_ld_error;
2792830Sdjl 	tfns.ltf_set_lderrno = set_ld_error;
2802830Sdjl 	tfns.ltf_lderrno_arg = NULL;
2812830Sdjl 
2822830Sdjl 	/*
2832830Sdjl 	 * Set up this session to use those function pointers
2842830Sdjl 	 */
2852830Sdjl 	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
2864522Schinlong 	    (void *) &tfns);
2872830Sdjl 	if (rc < 0) {
2882830Sdjl 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
2892830Sdjl 		"(LDAP_OPT_THREAD_FN_PTRS)");
2902830Sdjl 		return (-1);
2912830Sdjl 	}
2922830Sdjl 
2932830Sdjl 	/*
2942830Sdjl 	 * Set the function pointers for working with semaphores
2952830Sdjl 	 */
2962830Sdjl 	(void) memset(&extrafns, '\0',
2974522Schinlong 	    sizeof (struct ldap_extra_thread_fns));
2982830Sdjl 	extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
2992830Sdjl 	extrafns.ltf_mutex_trylock = NULL;
3002830Sdjl 	extrafns.ltf_sema_alloc = NULL;
3012830Sdjl 	extrafns.ltf_sema_free = NULL;
3022830Sdjl 	extrafns.ltf_sema_wait = NULL;
3032830Sdjl 	extrafns.ltf_sema_post = NULL;
3042830Sdjl 
3052830Sdjl 	/* Set up this session to use those function pointers */
3062830Sdjl 	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
3074522Schinlong 	    (void *) &extrafns);
3082830Sdjl 	if (rc < 0) {
3092830Sdjl 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
3102830Sdjl 		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)");
3112830Sdjl 		return (-1);
3122830Sdjl 	}
3132830Sdjl 
3142830Sdjl 	return (0);
3152830Sdjl }
3162830Sdjl 
3172830Sdjl static void
3182830Sdjl ns_setup_mt_conn_and_tsd(LDAP *ld) {
3192830Sdjl 	thread_t t = thr_self();
3202830Sdjl 	void *tsd;
3212830Sdjl 	/* set up to share this connection among threads */
3222830Sdjl 	if (MTperConn == 1) {
3232830Sdjl 		if (tsd_setup() == -1) {
3242830Sdjl 			syslog(LOG_ERR, "tid= %d: unable "
3252830Sdjl 				"to set up TSD\n", t);
3262830Sdjl 		} else {
3272830Sdjl 			if (setup_mt_conn(ld) == -1) {
3282830Sdjl 			/* multiple threads per connection not supported */
3292830Sdjl 				syslog(LOG_ERR, "tid= %d: multiple "
3302830Sdjl 					"threads per connection not "
3312830Sdjl 					"supported\n", t);
3322830Sdjl 				(void) thr_getspecific(ns_mtckey, &tsd);
3332830Sdjl 				ns_tsd_cleanup(tsd);
3342830Sdjl 				(void) thr_setspecific(ns_mtckey, NULL);
3352830Sdjl 				MTperConn = 0;
3362830Sdjl 			}
3372830Sdjl 		}
3382830Sdjl 	}
3392830Sdjl }
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate /*
3420Sstevel@tonic-gate  * Check /proc/PID/psinfo to see if this process is nscd
3430Sstevel@tonic-gate  * If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce
3440Sstevel@tonic-gate  * constant reconnects for many operations.
3450Sstevel@tonic-gate  * A more complete solution is to develop true connection pooling.
3460Sstevel@tonic-gate  * However, this is much better than a new connection for every request.
3470Sstevel@tonic-gate  */
3485399Schinlong int
3495399Schinlong __s_api_nscd_proc(void)
3500Sstevel@tonic-gate {
3510Sstevel@tonic-gate 	pid_t		my_pid;
3520Sstevel@tonic-gate 	psinfo_t	pinfo;
3530Sstevel@tonic-gate 	char		fname[BUFSIZ];
3540Sstevel@tonic-gate 	int		ret;
3550Sstevel@tonic-gate 	int		fd;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	/* Don't bother checking if this process isn't root. */
3580Sstevel@tonic-gate 	/* It can't be nscd */
3590Sstevel@tonic-gate 	if (getuid() != 0)
3600Sstevel@tonic-gate 		return (0);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	my_pid = getpid();
3630Sstevel@tonic-gate 	if (nscdChecked && (my_pid == checkedPid)) {
3640Sstevel@tonic-gate 		return (isNscd);
3650Sstevel@tonic-gate 	}
3660Sstevel@tonic-gate 	(void) mutex_lock(&nscdLock);
3670Sstevel@tonic-gate 	if (nscdChecked && (my_pid == checkedPid)) {
3680Sstevel@tonic-gate 		(void) mutex_unlock(&nscdLock);
3690Sstevel@tonic-gate 		return (isNscd);
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate 	nscdChecked = 1;
3720Sstevel@tonic-gate 	checkedPid = my_pid;
3730Sstevel@tonic-gate 	isNscd = 0;
3740Sstevel@tonic-gate 	if (snprintf(fname, BUFSIZ, "/proc/%d/psinfo", my_pid) != 0) {
3750Sstevel@tonic-gate 		if ((fd = open(fname,  O_RDONLY)) > 0) {
3760Sstevel@tonic-gate 			ret = read(fd, &pinfo, sizeof (psinfo_t));
3770Sstevel@tonic-gate 			(void) close(fd);
3780Sstevel@tonic-gate 			if (ret == sizeof (psinfo_t) &&
3790Sstevel@tonic-gate 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
3800Sstevel@tonic-gate 				/* process runs as root and is named nscd */
3810Sstevel@tonic-gate 				/* that's good enough for now */
3820Sstevel@tonic-gate 				isNscd = 1;
3830Sstevel@tonic-gate 			}
3840Sstevel@tonic-gate 		}
3850Sstevel@tonic-gate 	}
3860Sstevel@tonic-gate 	(void) mutex_unlock(&nscdLock);
3870Sstevel@tonic-gate 	return (isNscd);
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate /*
3910Sstevel@tonic-gate  * This function requests a server from the cache manager through
3920Sstevel@tonic-gate  * the door functionality
3930Sstevel@tonic-gate  */
3940Sstevel@tonic-gate 
3950Sstevel@tonic-gate static int
3960Sstevel@tonic-gate __s_api_requestServer(const char *request, const char *server,
3972830Sdjl 	ns_server_info_t *ret, ns_ldap_error_t **error,  const char *addrType)
3980Sstevel@tonic-gate {
3990Sstevel@tonic-gate 	union {
4000Sstevel@tonic-gate 		ldap_data_t	s_d;
4010Sstevel@tonic-gate 		char		s_b[DOORBUFFERSIZE];
4020Sstevel@tonic-gate 	} space;
4030Sstevel@tonic-gate 	ldap_data_t	*sptr;
4040Sstevel@tonic-gate 	int		ndata;
4050Sstevel@tonic-gate 	int		adata;
4060Sstevel@tonic-gate 	char		errstr[MAXERROR];
4070Sstevel@tonic-gate 	const char	*ireq;
4080Sstevel@tonic-gate 	char		*rbuf, *ptr, *rest;
4090Sstevel@tonic-gate 	char		*dptr;
4100Sstevel@tonic-gate 	char		**mptr, **mptr1, **cptr, **cptr1;
4110Sstevel@tonic-gate 	int		mcnt, ccnt;
4120Sstevel@tonic-gate 	char		**servers;
4132830Sdjl 	int		rc, len;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	if (ret == NULL || error == NULL) {
4160Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
4170Sstevel@tonic-gate 	}
4180Sstevel@tonic-gate 	(void) memset(ret, 0, sizeof (ns_server_info_t));
4190Sstevel@tonic-gate 	*error = NULL;
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	if (request == NULL)
4240Sstevel@tonic-gate 		ireq = NS_CACHE_NEW;
4250Sstevel@tonic-gate 	else
4260Sstevel@tonic-gate 		ireq = request;
4270Sstevel@tonic-gate 
4282830Sdjl 	adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1);
4290Sstevel@tonic-gate 	if (server != NULL) {
4300Sstevel@tonic-gate 		adata += strlen(DOORLINESEP) + 1;
4310Sstevel@tonic-gate 		adata += strlen(server) + 1;
4320Sstevel@tonic-gate 	}
4330Sstevel@tonic-gate 	ndata = sizeof (space);
4342830Sdjl 	len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
4350Sstevel@tonic-gate 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
4362830Sdjl 	if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
4372830Sdjl 		return (NS_LDAP_MEMORY);
4382830Sdjl 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >=
4394522Schinlong 	    len)
4402830Sdjl 		return (NS_LDAP_MEMORY);
4410Sstevel@tonic-gate 	if (server != NULL) {
4422830Sdjl 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
4434522Schinlong 		    DOORLINESEP, len) >= len)
4442830Sdjl 			return (NS_LDAP_MEMORY);
4452830Sdjl 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server,
4464522Schinlong 		    len) >= len)
4472830Sdjl 			return (NS_LDAP_MEMORY);
4480Sstevel@tonic-gate 	}
4490Sstevel@tonic-gate 	sptr = &space.s_d;
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
4520Sstevel@tonic-gate 	case SUCCESS:
4530Sstevel@tonic-gate 		break;
4540Sstevel@tonic-gate 	/* this case is for when the $mgr is not running, but ldapclient */
4550Sstevel@tonic-gate 	/* is trying to initialize things */
4560Sstevel@tonic-gate 	case NOSERVER:
4570Sstevel@tonic-gate 		/* get first server from config list unavailable otherwise */
4580Sstevel@tonic-gate 		servers = NULL;
4590Sstevel@tonic-gate 		rc = __s_api_getServers(&servers, error);
4600Sstevel@tonic-gate 		if (rc != NS_LDAP_SUCCESS) {
4610Sstevel@tonic-gate 			if (servers != NULL) {
4620Sstevel@tonic-gate 				__s_api_free2dArray(servers);
4630Sstevel@tonic-gate 				servers = NULL;
4640Sstevel@tonic-gate 			}
4650Sstevel@tonic-gate 			return (rc);
4660Sstevel@tonic-gate 		}
4670Sstevel@tonic-gate 		if (servers == NULL || servers[0] == NULL) {
4680Sstevel@tonic-gate 			__s_api_free2dArray(servers);
4690Sstevel@tonic-gate 			servers = NULL;
4700Sstevel@tonic-gate 			(void) sprintf(errstr,
4714522Schinlong 			    gettext("No server found in configuration"));
4720Sstevel@tonic-gate 			MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
4734522Schinlong 			    strdup(errstr), NULL);
4740Sstevel@tonic-gate 			return (NS_LDAP_CONFIG);
4750Sstevel@tonic-gate 		}
4760Sstevel@tonic-gate 		ret->server = strdup(servers[0]);
4770Sstevel@tonic-gate 		if (ret->server == NULL) {
4780Sstevel@tonic-gate 			__s_api_free2dArray(servers);
4790Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
4800Sstevel@tonic-gate 		}
4810Sstevel@tonic-gate 		ret->saslMechanisms = NULL;
4820Sstevel@tonic-gate 		ret->controls = NULL;
4830Sstevel@tonic-gate 		__s_api_free2dArray(servers);
4840Sstevel@tonic-gate 		servers = NULL;
4850Sstevel@tonic-gate 		return (NS_LDAP_SUCCESS);
4860Sstevel@tonic-gate 	case NOTFOUND:
4870Sstevel@tonic-gate 	default:
4880Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
4890Sstevel@tonic-gate 	}
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	/* copy info from door call return structure here */
4920Sstevel@tonic-gate 	rbuf =  space.s_d.ldap_ret.ldap_u.config;
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 	/* Get the host */
4950Sstevel@tonic-gate 	ptr = strtok_r(rbuf, DOORLINESEP, &rest);
4960Sstevel@tonic-gate 	if (ptr == NULL) {
4970Sstevel@tonic-gate 		(void) sprintf(errstr, gettext("No server returned from "
4984522Schinlong 		    "ldap_cachemgr"));
4990Sstevel@tonic-gate 		MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
5004522Schinlong 		    strdup(errstr), NULL);
5010Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
5020Sstevel@tonic-gate 	}
5030Sstevel@tonic-gate 	ret->server = strdup(ptr);
5040Sstevel@tonic-gate 	if (ret->server == NULL) {
5050Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
5060Sstevel@tonic-gate 	}
5074522Schinlong 	/* Get the host FQDN format */
5084522Schinlong 	if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
5094522Schinlong 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
5104522Schinlong 		if (ptr == NULL) {
5114522Schinlong 			(void) sprintf(errstr, gettext("No server FQDN format "
5124522Schinlong 			    "returned from ldap_cachemgr"));
5134522Schinlong 			MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
5144522Schinlong 			    strdup(errstr), NULL);
5154522Schinlong 			free(ret->server);
5164522Schinlong 			ret->server = NULL;
5174522Schinlong 			return (NS_LDAP_OP_FAILED);
5184522Schinlong 		}
5194522Schinlong 		ret->serverFQDN = strdup(ptr);
5204522Schinlong 		if (ret->serverFQDN == NULL) {
5214522Schinlong 			free(ret->server);
5224522Schinlong 			ret->server = NULL;
5234522Schinlong 			return (NS_LDAP_MEMORY);
5244522Schinlong 		}
5254522Schinlong 	}
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	/* get the Supported Controls/SASL mechs */
5280Sstevel@tonic-gate 	mptr = NULL;
5290Sstevel@tonic-gate 	mcnt = 0;
5300Sstevel@tonic-gate 	cptr = NULL;
5310Sstevel@tonic-gate 	ccnt = 0;
5320Sstevel@tonic-gate 	for (; ; ) {
5330Sstevel@tonic-gate 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
5340Sstevel@tonic-gate 		if (ptr == NULL)
5350Sstevel@tonic-gate 			break;
5360Sstevel@tonic-gate 		if (strncasecmp(ptr, _SASLMECHANISM,
5374522Schinlong 		    _SASLMECHANISM_LEN) == 0) {
5380Sstevel@tonic-gate 			dptr = strchr(ptr, '=');
5390Sstevel@tonic-gate 			if (dptr == NULL)
5400Sstevel@tonic-gate 				continue;
5410Sstevel@tonic-gate 			dptr++;
5420Sstevel@tonic-gate 			mptr1 = (char **)realloc((void *)mptr,
5434522Schinlong 			    sizeof (char *) * (mcnt+2));
5440Sstevel@tonic-gate 			if (mptr1 == NULL) {
5450Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
5460Sstevel@tonic-gate 				if (sptr != &space.s_d) {
5474522Schinlong 					(void) munmap((char *)sptr, ndata);
5480Sstevel@tonic-gate 				}
5490Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
5504522Schinlong 				__s_api_free_server_info(ret);
5510Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
5520Sstevel@tonic-gate 			}
5530Sstevel@tonic-gate 			mptr = mptr1;
5540Sstevel@tonic-gate 			mptr[mcnt] = strdup(dptr);
5550Sstevel@tonic-gate 			if (mptr[mcnt] == NULL) {
5560Sstevel@tonic-gate 				if (sptr != &space.s_d) {
5574522Schinlong 					(void) munmap((char *)sptr, ndata);
5580Sstevel@tonic-gate 				}
5590Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
5600Sstevel@tonic-gate 				cptr = NULL;
5610Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
5620Sstevel@tonic-gate 				mptr = NULL;
5634522Schinlong 				__s_api_free_server_info(ret);
5640Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
5650Sstevel@tonic-gate 			}
5660Sstevel@tonic-gate 			mcnt++;
5670Sstevel@tonic-gate 			mptr[mcnt] = NULL;
5680Sstevel@tonic-gate 		}
5690Sstevel@tonic-gate 		if (strncasecmp(ptr, _SUPPORTEDCONTROL,
5704522Schinlong 		    _SUPPORTEDCONTROL_LEN) == 0) {
5710Sstevel@tonic-gate 			dptr = strchr(ptr, '=');
5720Sstevel@tonic-gate 			if (dptr == NULL)
5730Sstevel@tonic-gate 				continue;
5740Sstevel@tonic-gate 			dptr++;
5750Sstevel@tonic-gate 			cptr1 = (char **)realloc((void *)cptr,
5764522Schinlong 			    sizeof (char *) * (ccnt+2));
5770Sstevel@tonic-gate 			if (cptr1 == NULL) {
5780Sstevel@tonic-gate 				if (sptr != &space.s_d) {
5794522Schinlong 					(void) munmap((char *)sptr, ndata);
5800Sstevel@tonic-gate 				}
5810Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
5820Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
5830Sstevel@tonic-gate 				mptr = NULL;
5844522Schinlong 				__s_api_free_server_info(ret);
5850Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
5860Sstevel@tonic-gate 			}
5870Sstevel@tonic-gate 			cptr = cptr1;
5880Sstevel@tonic-gate 			cptr[ccnt] = strdup(dptr);
5890Sstevel@tonic-gate 			if (cptr[ccnt] == NULL) {
5900Sstevel@tonic-gate 				if (sptr != &space.s_d) {
5914522Schinlong 					(void) munmap((char *)sptr, ndata);
5920Sstevel@tonic-gate 				}
5930Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
5940Sstevel@tonic-gate 				cptr = NULL;
5950Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
5960Sstevel@tonic-gate 				mptr = NULL;
5974522Schinlong 				__s_api_free_server_info(ret);
5980Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
5990Sstevel@tonic-gate 			}
6000Sstevel@tonic-gate 			ccnt++;
6010Sstevel@tonic-gate 			cptr[ccnt] = NULL;
6020Sstevel@tonic-gate 		}
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 	if (mptr != NULL) {
6050Sstevel@tonic-gate 		ret->saslMechanisms = mptr;
6060Sstevel@tonic-gate 	}
6070Sstevel@tonic-gate 	if (cptr != NULL) {
6080Sstevel@tonic-gate 		ret->controls = cptr;
6090Sstevel@tonic-gate 	}
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	/* clean up door call */
6130Sstevel@tonic-gate 	if (sptr != &space.s_d) {
6140Sstevel@tonic-gate 		(void) munmap((char *)sptr, ndata);
6150Sstevel@tonic-gate 	}
6160Sstevel@tonic-gate 	*error = NULL;
6170Sstevel@tonic-gate 
6180Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
6190Sstevel@tonic-gate }
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate /*
6230Sstevel@tonic-gate  * printCred(): prints the credential structure
6240Sstevel@tonic-gate  */
6250Sstevel@tonic-gate static void
6262830Sdjl printCred(int pri, const ns_cred_t *cred)
6270Sstevel@tonic-gate {
6282830Sdjl 	thread_t	t = thr_self();
6292830Sdjl 
6300Sstevel@tonic-gate 	if (cred == NULL) {
6312830Sdjl 		syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t);
6320Sstevel@tonic-gate 		return;
6330Sstevel@tonic-gate 	}
6340Sstevel@tonic-gate 
6352830Sdjl 	syslog(pri, "tid= %d: AuthType=%d", t, cred->auth.type);
6362830Sdjl 	syslog(pri, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
6372830Sdjl 	syslog(pri, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
6382830Sdjl 	syslog(pri, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
6390Sstevel@tonic-gate 	if (cred->hostcertpath)
6402830Sdjl 		syslog(pri, "tid= %d: hostCertPath=%s\n",
6414522Schinlong 		    t, cred->hostcertpath);
6420Sstevel@tonic-gate 	if (cred->cred.unix_cred.userID)
6432830Sdjl 		syslog(pri, "tid= %d: userID=%s\n",
6444522Schinlong 		    t, cred->cred.unix_cred.userID);
6453387Schinlong #ifdef DEBUG
6460Sstevel@tonic-gate 	if (cred->cred.unix_cred.passwd)
6472830Sdjl 		syslog(pri, "tid= %d: passwd=%s\n",
6484522Schinlong 		    t, cred->cred.unix_cred.passwd);
6493387Schinlong #endif
6500Sstevel@tonic-gate }
6510Sstevel@tonic-gate 
6520Sstevel@tonic-gate /*
6530Sstevel@tonic-gate  * printConnection(): prints the connection structure
6540Sstevel@tonic-gate  */
6550Sstevel@tonic-gate static void
6562830Sdjl printConnection(int pri, Connection *con)
6570Sstevel@tonic-gate {
6582830Sdjl 	thread_t	t = thr_self();
6592830Sdjl 
6602830Sdjl 	if (con == NULL)
6610Sstevel@tonic-gate 		return;
6620Sstevel@tonic-gate 
6632830Sdjl 	syslog(pri, "tid= %d: connectionID=%d\n", t, con->connectionId);
6642830Sdjl 	syslog(pri, "tid= %d: shared=%d\n", t, con->shared);
6652830Sdjl 	syslog(pri, "tid= %d: usedBit=%d\n", t, con->usedBit);
6662830Sdjl 	syslog(pri, "tid= %d: threadID=%d\n", t, con->threadID);
6670Sstevel@tonic-gate 	if (con->serverAddr) {
6682830Sdjl 		syslog(pri, "tid= %d: serverAddr=%s\n",
6694522Schinlong 		    t, con->serverAddr);
6700Sstevel@tonic-gate 	}
6712830Sdjl 	printCred(pri, con->auth);
6720Sstevel@tonic-gate }
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate /*
6772830Sdjl  * addConnection(): set up a connection so that it can be shared
6782830Sdjl  * among multiple threads and then insert the connection in the
6792830Sdjl  * connection list.
6800Sstevel@tonic-gate  * Returns: -1 = failure, new Connection ID = success
6812830Sdjl  *
6822830Sdjl  * This function could exit with sessionLock locked. It will be
6832830Sdjl  * be unlocked in __s_api_getConnection() when it exits without getting a
6842830Sdjl  * connection.
6850Sstevel@tonic-gate  */
6860Sstevel@tonic-gate static int
6870Sstevel@tonic-gate addConnection(Connection *con)
6880Sstevel@tonic-gate {
6892830Sdjl 	int i, noMTperC = 0;
6902830Sdjl 	thread_t t = thr_self();
6912830Sdjl 	struct ldap_thread_fns tfns;
6922830Sdjl 	void *tsd;
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	if (!con)
6950Sstevel@tonic-gate 		return (-1);
6962830Sdjl 
6972830Sdjl 	syslog(LOG_DEBUG, "tid= %d: Adding connection (serverAddr=%s)",
6984522Schinlong 	    t, con->serverAddr);
6992830Sdjl 
7002830Sdjl 	if (MTperConn == 1) {
7012830Sdjl 		/*
7022830Sdjl 		 * Make sure ld has proper thread functions and tsd
7032830Sdjl 		 * is set up.
7042830Sdjl 		 */
7052830Sdjl 		(void) memset(&tfns, 0, sizeof (struct ldap_thread_fns));
7062830Sdjl 		/*
7072830Sdjl 		 * ldap_init sets ltf_get_lderrno and ltf_set_lderrno to NULLs.
7082830Sdjl 		 * It's supposed to be overwritten by ns_setup_mt_conn_and_tsd.
7092830Sdjl 		 */
7102830Sdjl 		if (ldap_get_option(con->ld, LDAP_OPT_THREAD_FN_PTRS,
7114522Schinlong 		    (void *)&tfns) != 0 ||
7124522Schinlong 		    tfns.ltf_get_lderrno != get_ld_error ||
7134522Schinlong 		    tfns.ltf_set_lderrno != set_ld_error) {
7142830Sdjl 			MTperConn = 0;
7152830Sdjl 			noMTperC = 1;
7162830Sdjl 		} else {
7172830Sdjl 			if (thr_getspecific(ns_mtckey, &tsd) != 0 ||
7184522Schinlong 			    tsd == NULL)
7192830Sdjl 				noMTperC = 1;
7202830Sdjl 		}
7212830Sdjl 
7222830Sdjl 	} else {
7232830Sdjl 		noMTperC = 1;
7242830Sdjl 	}
7252830Sdjl 
7262830Sdjl 	(void) rw_wrlock(&sessionPoolLock);
7270Sstevel@tonic-gate 	if (sessionPool == NULL) {
7280Sstevel@tonic-gate 		sessionPoolSize = SESSION_CACHE_INC;
7290Sstevel@tonic-gate 		sessionPool = calloc(sessionPoolSize,
7304522Schinlong 		    sizeof (struct connection **));
7310Sstevel@tonic-gate 		if (!sessionPool) {
7322830Sdjl 			(void) rw_unlock(&sessionPoolLock);
7330Sstevel@tonic-gate 			return (-1);
7340Sstevel@tonic-gate 		}
7352830Sdjl 
7362830Sdjl 		syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t);
7370Sstevel@tonic-gate 	}
7380Sstevel@tonic-gate 	for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
7390Sstevel@tonic-gate 		;
7400Sstevel@tonic-gate 	if (i == sessionPoolSize) {
7410Sstevel@tonic-gate 		/* run out of array, need to increase sessionPool */
7420Sstevel@tonic-gate 		Connection **cl;
7430Sstevel@tonic-gate 		cl = (Connection **) realloc(sessionPool,
7444522Schinlong 		    (sessionPoolSize + SESSION_CACHE_INC) *
7454522Schinlong 		    sizeof (Connection *));
7460Sstevel@tonic-gate 		if (!cl) {
7472830Sdjl 			(void) rw_unlock(&sessionPoolLock);
7480Sstevel@tonic-gate 			return (-1);
7490Sstevel@tonic-gate 		}
7500Sstevel@tonic-gate 		(void) memset(cl + sessionPoolSize, 0,
7514522Schinlong 		    SESSION_CACHE_INC * sizeof (struct connection *));
7520Sstevel@tonic-gate 		sessionPool = cl;
7530Sstevel@tonic-gate 		sessionPoolSize += SESSION_CACHE_INC;
7542830Sdjl 		syslog(LOG_DEBUG, "tid: %d: Increased "
7554522Schinlong 		    "sessionPoolSize to: %d\n",
7564522Schinlong 		    t, sessionPoolSize);
7570Sstevel@tonic-gate 	}
7580Sstevel@tonic-gate 	sessionPool[i] = con;
7593387Schinlong 	if (noMTperC == 0) {
7602830Sdjl 		con->shared++;
7613387Schinlong 		con->pid = getpid();
7623387Schinlong 		(void) mutex_lock(&sharedConnNumberLock);
7633387Schinlong 		sharedConnNumber++;
7643387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
7653387Schinlong 	} else
7662830Sdjl 		con->usedBit = B_TRUE;
7672830Sdjl 
7682830Sdjl 	(void) rw_unlock(&sessionPoolLock);
7692830Sdjl 
7700Sstevel@tonic-gate 	con->connectionId = i + CONID_OFFSET;
7712830Sdjl 
7722830Sdjl 	syslog(LOG_DEBUG, "tid= %d: Connection added [%d]\n",
7734522Schinlong 	    t, i);
7742830Sdjl 	printConnection(LOG_DEBUG, con);
7752830Sdjl 
7762830Sdjl 	/*
7772830Sdjl 	 * A connection can be shared now, unlock
7782830Sdjl 	 * the session mutex and let other
7792830Sdjl 	 * threads try to use this connection or
7802830Sdjl 	 * get their own.
7812830Sdjl 	 */
7822830Sdjl 	if (wait4session != 0 && sessionTid == thr_self()) {
7832830Sdjl 		wait4session = 0;
7842830Sdjl 		sessionTid = 0;
7852830Sdjl 		syslog(LOG_DEBUG, "tid= %d: unlocking sessionLock\n", t);
7862830Sdjl 		(void) mutex_unlock(&sessionLock);
7872830Sdjl 	}
7882830Sdjl 
7890Sstevel@tonic-gate 	return (i + CONID_OFFSET);
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate /*
7930Sstevel@tonic-gate  * See if the specified session matches a currently available
7940Sstevel@tonic-gate  */
7950Sstevel@tonic-gate 
7960Sstevel@tonic-gate static int
7970Sstevel@tonic-gate findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
7980Sstevel@tonic-gate 	Connection **conp)
7990Sstevel@tonic-gate {
8000Sstevel@tonic-gate 	Connection *cp;
8010Sstevel@tonic-gate 	int id;
8020Sstevel@tonic-gate 
8030Sstevel@tonic-gate 	if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
8040Sstevel@tonic-gate 		return (-1);
8052830Sdjl 
8065532Smj162486 	/*
8075532Smj162486 	 * If a new connection is requested, no need to continue.
8085532Smj162486 	 * If the process is not nscd and is not requesting keep connections
8095532Smj162486 	 * alive, no need to continue.
8105532Smj162486 	 */
8115532Smj162486 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
8125532Smj162486 	    !(flags & NS_LDAP_KEEP_CONN)))
8132830Sdjl 		return (-1);
8142830Sdjl 
8150Sstevel@tonic-gate 	*conp = NULL;
8160Sstevel@tonic-gate 	if (sessionPool == NULL)
8170Sstevel@tonic-gate 		return (-1);
8180Sstevel@tonic-gate 	id = cID - CONID_OFFSET;
8190Sstevel@tonic-gate 	if (id < 0 || id >= sessionPoolSize)
8200Sstevel@tonic-gate 		return (-1);
8210Sstevel@tonic-gate 
8222830Sdjl 	(void) rw_rdlock(&sessionPoolLock);
8230Sstevel@tonic-gate 	if (sessionPool[id] == NULL) {
8242830Sdjl 		(void) rw_unlock(&sessionPoolLock);
8250Sstevel@tonic-gate 		return (-1);
8260Sstevel@tonic-gate 	}
8270Sstevel@tonic-gate 	cp = sessionPool[id];
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	/*
8300Sstevel@tonic-gate 	 * Make sure the connection has the same type of authentication method
8310Sstevel@tonic-gate 	 */
8320Sstevel@tonic-gate 	if ((cp->usedBit) ||
8332830Sdjl 	    (cp->notAvail) ||
8340Sstevel@tonic-gate 	    (cp->auth->auth.type != auth->auth.type) ||
8350Sstevel@tonic-gate 	    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
8360Sstevel@tonic-gate 	    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
8370Sstevel@tonic-gate 	    (cp->auth->auth.saslopt != auth->auth.saslopt)) {
8382830Sdjl 		(void) rw_unlock(&sessionPoolLock);
8390Sstevel@tonic-gate 		return (-1);
8400Sstevel@tonic-gate 	}
8410Sstevel@tonic-gate 	if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
8424522Schinlong 	    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
8434522Schinlong 	    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
8444522Schinlong 	    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
8454522Schinlong 	    ((cp->auth->cred.unix_cred.userID == NULL) ||
8464522Schinlong 	    (strcasecmp(cp->auth->cred.unix_cred.userID,
8474522Schinlong 	    auth->cred.unix_cred.userID) != 0))) {
8482830Sdjl 		(void) rw_unlock(&sessionPoolLock);
8490Sstevel@tonic-gate 		return (-1);
8500Sstevel@tonic-gate 	}
8512830Sdjl 
8520Sstevel@tonic-gate 	/* An existing connection is found but it needs to be reset */
8530Sstevel@tonic-gate 	if (flags & NS_LDAP_NEW_CONN) {
8542830Sdjl 		(void) rw_unlock(&sessionPoolLock);
8550Sstevel@tonic-gate 		DropConnection(cID, 0);
8560Sstevel@tonic-gate 		return (-1);
8570Sstevel@tonic-gate 	}
8580Sstevel@tonic-gate 	/* found an available connection */
8590Sstevel@tonic-gate 	cp->usedBit = B_TRUE;
8602830Sdjl 	(void) rw_unlock(&sessionPoolLock);
8610Sstevel@tonic-gate 	cp->threadID = thr_self();
8620Sstevel@tonic-gate 	*conp = cp;
8630Sstevel@tonic-gate 	return (cID);
8640Sstevel@tonic-gate }
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate /*
8670Sstevel@tonic-gate  * findConnection(): find an available connection from the list
8680Sstevel@tonic-gate  * that matches the criteria specified in Connection structure.
8690Sstevel@tonic-gate  * If serverAddr is NULL, then find a connection to any server
8700Sstevel@tonic-gate  * as long as it matches the rest of the parameters.
8710Sstevel@tonic-gate  * Returns: -1 = failure, the Connection ID found = success.
8722830Sdjl  *
8732830Sdjl  * This function could exit with sessionLock locked. It will be
8742830Sdjl  * be unlocked in addConnection() when this thread adds the connection
8752830Sdjl  * to the pool or in __s_api_getConnection() when it exits without getting a
8762830Sdjl  * connection.
8770Sstevel@tonic-gate  */
8782830Sdjl #define	TRY_TIMES	10
8790Sstevel@tonic-gate static int
8802830Sdjl findConnection(int flags, const char *serverAddr,
8812830Sdjl 	const ns_cred_t *auth, Connection **conp)
8820Sstevel@tonic-gate {
8830Sstevel@tonic-gate 	Connection *cp;
8844048Schinlong 	int i, j, conn_server_index, up_server_index, drop_conn;
8852830Sdjl 	int rc;
8862830Sdjl 	int try;
8874048Schinlong 	ns_server_info_t sinfo;
8884048Schinlong 	ns_ldap_error_t *errorp = NULL;
8894522Schinlong 	char **servers;
8904048Schinlong 	void **paramVal = NULL;
8912830Sdjl #ifdef DEBUG
8922830Sdjl 	thread_t t = thr_self();
8932830Sdjl #endif /* DEBUG */
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 	if (auth == NULL || conp == NULL)
8960Sstevel@tonic-gate 		return (-1);
8970Sstevel@tonic-gate 	*conp = NULL;
8980Sstevel@tonic-gate 
899*5840Smj162486 	/*
900*5840Smj162486 	 * If a new connection is requested, no need to continue.
901*5840Smj162486 	 * If the process is not nscd and is not requesting keep connections
902*5840Smj162486 	 * alive, no need to continue.
903*5840Smj162486 	 */
904*5840Smj162486 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
905*5840Smj162486 	    !(flags & NS_LDAP_KEEP_CONN)))
9062830Sdjl 		return (-1);
9072830Sdjl 
9080Sstevel@tonic-gate #ifdef DEBUG
9092830Sdjl 	(void) fprintf(stderr, "tid= %d: Find connection\n", t);
9102830Sdjl 	(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
9110Sstevel@tonic-gate 	if (serverAddr && *serverAddr)
9122830Sdjl 		(void) fprintf(stderr, "tid= %d: serverAddr=%s\n",
9134522Schinlong 		    t, serverAddr);
9140Sstevel@tonic-gate 	else
9152830Sdjl 		(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
9164048Schinlong 	printCred(LOG_DEBUG, auth);
9170Sstevel@tonic-gate 	fflush(stderr);
9180Sstevel@tonic-gate #endif /* DEBUG */
9192830Sdjl 
9202830Sdjl 	/*
9212830Sdjl 	 * If multiple threads per connection not supported,
9222830Sdjl 	 * no sessionPool means no connection
9232830Sdjl 	 */
9242830Sdjl 	(void) rw_rdlock(&sessionPoolLock);
9252830Sdjl 	if (MTperConn == 0 && sessionPool == NULL) {
9262830Sdjl 		(void) rw_unlock(&sessionPoolLock);
9270Sstevel@tonic-gate 		return (-1);
9282830Sdjl 	}
9292830Sdjl 
9302830Sdjl 	/*
9313387Schinlong 	 * If no sharable connections in cache, then serialize the opening
9322830Sdjl 	 * of connections. Make sure only one is being opened
9332830Sdjl 	 * at a time. Otherwise, we may end up with more
9342830Sdjl 	 * connections than we want (if multiple threads get
9352830Sdjl 	 * here at the same time)
9362830Sdjl 	 */
9373387Schinlong 	(void) mutex_lock(&sharedConnNumberLock);
9383387Schinlong 	if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) {
9393387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
9402830Sdjl 		(void) rw_unlock(&sessionPoolLock);
9412830Sdjl 		(void) mutex_lock(&sessionLock);
9423387Schinlong 		(void) mutex_lock(&sharedConnNumberLock);
9433387Schinlong 		if (sessionPool == NULL || (sharedConnNumber == 0 &&
9444522Schinlong 		    MTperConn == 1)) {
9453387Schinlong 			(void) mutex_unlock(&sharedConnNumberLock);
9462830Sdjl 			wait4session = 1;
9472830Sdjl 			sessionTid = thr_self();
9482830Sdjl #ifdef DEBUG
9492830Sdjl 			(void) fprintf(stderr, "tid= %d: get "
9504522Schinlong 			    "connection ... \n", t);
9512830Sdjl 			fflush(stderr);
9522830Sdjl #endif /* DEBUG */
9532830Sdjl 			/*
9542830Sdjl 			 * Exit with sessionLock locked. It will be
9552830Sdjl 			 * be unlocked in addConnection() when this
9562830Sdjl 			 * thread adds the connection to the pool or
9572830Sdjl 			 * in __s_api_getConnection() when it exits
9582830Sdjl 			 * without getting a connection.
9592830Sdjl 			 */
9602830Sdjl 			return (-1);
9612830Sdjl 		}
9622830Sdjl 
9632830Sdjl #ifdef DEBUG
9643387Schinlong 		(void) fprintf(stderr, "tid= %d: shareable connections "
9654522Schinlong 		    "exist\n", t);
9662830Sdjl 		fflush(stderr);
9672830Sdjl #endif /* DEBUG */
9683387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
9692830Sdjl 		/*
9703387Schinlong 		 * There are sharable connections, check to see if
9712830Sdjl 		 * one can be shared.
9722830Sdjl 		 */
9732830Sdjl 		(void) mutex_unlock(&sessionLock);
9742830Sdjl 		(void) rw_rdlock(&sessionPoolLock);
9753387Schinlong 	} else
9763387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
9773387Schinlong 
9782830Sdjl 	try = 0;
9792830Sdjl 	check_again:
9802830Sdjl 
9810Sstevel@tonic-gate 	for (i = 0; i < sessionPoolSize; ++i) {
9820Sstevel@tonic-gate 		if (sessionPool[i] == NULL)
9830Sstevel@tonic-gate 			continue;
9840Sstevel@tonic-gate 		cp = sessionPool[i];
9850Sstevel@tonic-gate #ifdef DEBUG
9862830Sdjl 		(void) fprintf(stderr, "tid= %d: checking connection "
9874522Schinlong 		    "[%d] ....\n", t, i);
9884048Schinlong 		printConnection(LOG_DEBUG, cp);
9890Sstevel@tonic-gate #endif /* DEBUG */
9902830Sdjl 		if ((cp->usedBit) || (cp->notAvail) ||
9910Sstevel@tonic-gate 		    (cp->auth->auth.type != auth->auth.type) ||
9920Sstevel@tonic-gate 		    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
9930Sstevel@tonic-gate 		    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
9940Sstevel@tonic-gate 		    (cp->auth->auth.saslopt != auth->auth.saslopt) ||
9950Sstevel@tonic-gate 		    (serverAddr && *serverAddr &&
9960Sstevel@tonic-gate 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
9970Sstevel@tonic-gate 			continue;
9980Sstevel@tonic-gate 		if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
9990Sstevel@tonic-gate 		    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
10000Sstevel@tonic-gate 		    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
10010Sstevel@tonic-gate 		    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
10020Sstevel@tonic-gate 		    ((cp->auth->cred.unix_cred.userID == NULL) ||
10030Sstevel@tonic-gate 		    (cp->auth->cred.unix_cred.passwd == NULL) ||
10040Sstevel@tonic-gate 		    ((strcasecmp(cp->auth->cred.unix_cred.userID,
10054522Schinlong 		    auth->cred.unix_cred.userID) != 0)) ||
10060Sstevel@tonic-gate 		    ((strcmp(cp->auth->cred.unix_cred.passwd,
10074522Schinlong 		    auth->cred.unix_cred.passwd) != 0))))
10080Sstevel@tonic-gate 				continue;
10094048Schinlong 		if (!(serverAddr && *serverAddr)) {
10104048Schinlong 			/*
10114048Schinlong 			 * Get preferred server list.
10124048Schinlong 			 * When preferred servers are merged with default
10134048Schinlong 			 * servers (S_LDAP_SERVER_P) by __s_api_getServer,
10144048Schinlong 			 * preferred servers are copied sequencially.
10154048Schinlong 			 * The order should be the same as the order retrieved
10164048Schinlong 			 * by __ns_ldap_getParam.
10174048Schinlong 			 */
10184048Schinlong 			if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
10194522Schinlong 			    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
10204048Schinlong 				(void) __ns_ldap_freeError(&errorp);
10214048Schinlong 				(void) __ns_ldap_freeParam(&paramVal);
10224048Schinlong 				(void) rw_unlock(&sessionPoolLock);
10234048Schinlong 				return (-1);
10244048Schinlong 			}
10254048Schinlong 			servers = (char **)paramVal;
10264048Schinlong 			/*
10274048Schinlong 			 * Do fallback only if preferred servers are defined.
10284048Schinlong 			 */
10294048Schinlong 			if (servers != NULL) {
10304048Schinlong 				/*
10314048Schinlong 				 * Find the 1st available server
10324048Schinlong 				 */
10334048Schinlong 				rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
10344522Schinlong 				    &sinfo, &errorp, NS_CACHE_ADDR_IP);
10354048Schinlong 				if (rc != NS_LDAP_SUCCESS) {
10364048Schinlong 					/*
10374048Schinlong 					 * Drop the connection.
10384048Schinlong 					 * Pass 1 to fini so it won't be locked
10394048Schinlong 					 * inside _DropConnection
10404048Schinlong 					 */
10414048Schinlong 					_DropConnection(
10424522Schinlong 					    cp->connectionId,
10434522Schinlong 					    NS_LDAP_NEW_CONN, 1);
10444048Schinlong 					(void) rw_unlock(
10454048Schinlong 					    &sessionPoolLock);
10464522Schinlong 					(void) __ns_ldap_freeError(&errorp);
10474048Schinlong 					(void) __ns_ldap_freeParam(
10484522Schinlong 					    (void ***)&servers);
10494048Schinlong 					return (-1);
10504048Schinlong 				}
10514048Schinlong 
10524048Schinlong 				if (sinfo.server) {
10534048Schinlong 					/*
10544048Schinlong 					 * Test if cp->serverAddr is a
10554048Schinlong 					 * preferred server.
10564048Schinlong 					 */
10574048Schinlong 					conn_server_index = -1;
10584048Schinlong 					for (j = 0; servers[j] != NULL; j++) {
10594048Schinlong 						if (strcasecmp(servers[j],
10604522Schinlong 						    cp->serverAddr) == 0) {
10614048Schinlong 							conn_server_index = j;
10624048Schinlong 							break;
10634048Schinlong 						}
10644048Schinlong 					}
10654048Schinlong 					/*
10664048Schinlong 					 * Test if sinfo.server is a preferred
10674048Schinlong 					 * server.
10684048Schinlong 					 */
10694048Schinlong 					up_server_index = -1;
10704048Schinlong 					for (j = 0; servers[j] != NULL; j++) {
10714048Schinlong 						if (strcasecmp(sinfo.server,
10724522Schinlong 						    servers[j]) == 0) {
10734048Schinlong 							up_server_index = j;
10744048Schinlong 							break;
10754048Schinlong 						}
10764048Schinlong 					}
10774048Schinlong 
10784048Schinlong 					/*
10794048Schinlong 					 * The following code is to fall back
10804048Schinlong 					 * to preferred servers if servers
10814048Schinlong 					 * are previously down but are up now.
10824048Schinlong 					 * If cp->serverAddr is a preferred
10834048Schinlong 					 * server, it falls back to the servers
10844048Schinlong 					 * ahead of it. If cp->serverAddr is
10854048Schinlong 					 * not a preferred server, it falls
10864048Schinlong 					 * back to any of preferred servers
10874048Schinlong 					 * returned by ldap_cachemgr.
10884048Schinlong 					 */
10894048Schinlong 					if (conn_server_index >= 0 &&
10904522Schinlong 					    up_server_index >= 0) {
10914048Schinlong 						/*
10924048Schinlong 						 * cp->serverAddr and
10934048Schinlong 						 * sinfo.server are preferred
10944048Schinlong 						 * servers.
10954048Schinlong 						 */
10964048Schinlong 						if (up_server_index ==
10974522Schinlong 						    conn_server_index)
10984048Schinlong 							/*
10994048Schinlong 							 * sinfo.server is the
11004048Schinlong 							 * same as
11014048Schinlong 							 * cp->serverAddr.
11024048Schinlong 							 * Keep the connection.
11034048Schinlong 							 */
11044048Schinlong 							drop_conn = 0;
11054048Schinlong 						else
11064048Schinlong 							/*
11074048Schinlong 							 * 1.
11084048Schinlong 							 * up_server_index <
11094048Schinlong 							 * conn_server_index
11104048Schinlong 							 *
11114048Schinlong 							 * sinfo is ahead of
11124048Schinlong 							 * cp->serverAddr in
11134048Schinlong 							 * Need to fall back.
11144048Schinlong 							 * 2.
11154048Schinlong 							 * up_server_index >
11164048Schinlong 							 * conn_server_index
11174048Schinlong 							 * cp->serverAddr is
11184048Schinlong 							 * down. Drop it.
11194048Schinlong 							 */
11204048Schinlong 							drop_conn = 1;
11214048Schinlong 					} else if (conn_server_index >= 0 &&
11224522Schinlong 					    up_server_index == -1) {
11234048Schinlong 						/*
11244048Schinlong 						 * cp->serverAddr is a preferred
11254048Schinlong 						 * server but sinfo.server is
11264048Schinlong 						 * not. Preferred servers are
11274048Schinlong 						 * ahead of default servers.
11284048Schinlong 						 * This means cp->serverAddr is
11294048Schinlong 						 * down. Drop it.
11304048Schinlong 						 */
11314048Schinlong 						drop_conn = 1;
11324048Schinlong 					} else if (conn_server_index == -1 &&
11334522Schinlong 					    up_server_index >= 0) {
11344048Schinlong 						/*
11354048Schinlong 						 * cp->serverAddr is not a
11364048Schinlong 						 * preferred server but
11374048Schinlong 						 * sinfo.server is.
11384048Schinlong 						 * Fall back.
11394048Schinlong 						 */
11404048Schinlong 						drop_conn = 1;
11414048Schinlong 					} else {
11424048Schinlong 						/*
11434048Schinlong 						 * Both index are -1
11444048Schinlong 						 * cp->serverAddr and
11454048Schinlong 						 * sinfo.server are not
11464048Schinlong 						 * preferred servers.
11474048Schinlong 						 * No fallback.
11484048Schinlong 						 */
11494048Schinlong 						drop_conn = 0;
11504048Schinlong 					}
11514048Schinlong 					if (drop_conn) {
11524048Schinlong 						/*
11534048Schinlong 						 * Drop the connection so the
11544048Schinlong 						 * new conection can fall back
11554048Schinlong 						 * to a new server later.
11564048Schinlong 						 * Pass 1 to fini so it won't
11574048Schinlong 						 * be locked inside
11584048Schinlong 						 * _DropConnection
11594048Schinlong 						 */
11604048Schinlong 						_DropConnection(
11614522Schinlong 						    cp->connectionId,
11624522Schinlong 						    NS_LDAP_NEW_CONN, 1);
11634048Schinlong 						(void) rw_unlock(
11644048Schinlong 						    &sessionPoolLock);
11654048Schinlong 						(void) __ns_ldap_freeParam(
11664522Schinlong 						    (void ***)&servers);
11674522Schinlong 						__s_api_free_server_info(
11684522Schinlong 						    &sinfo);
11694048Schinlong 						return (-1);
11704048Schinlong 					} else {
11714048Schinlong 						/*
11724048Schinlong 						 * Keep the connection
11734048Schinlong 						 */
11744048Schinlong 						(void) __ns_ldap_freeParam(
11754522Schinlong 						    (void ***)&servers);
11764522Schinlong 						__s_api_free_server_info(
11774522Schinlong 						    &sinfo);
11784048Schinlong 					}
11794048Schinlong 				} else {
11804048Schinlong 					(void) rw_unlock(&sessionPoolLock);
11814048Schinlong 					syslog(LOG_WARNING, "libsldap: Null "
11824522Schinlong 					    "sinfo.server from "
11834522Schinlong 					    "__s_api_requestServer");
11844048Schinlong 					(void) __ns_ldap_freeParam(
11854522Schinlong 					    (void ***)&servers);
11864048Schinlong 					return (-1);
11874048Schinlong 				}
11884048Schinlong 			}
11894048Schinlong 		}
11904048Schinlong 
11910Sstevel@tonic-gate 		/* found an available connection */
11922830Sdjl 		if (MTperConn == 0)
11932830Sdjl 			cp->usedBit = B_TRUE;
11942830Sdjl 		else {
11953387Schinlong 			/*
11963387Schinlong 			 * if connection was established in a different
11973387Schinlong 			 * process, drop it and get a new one
11983387Schinlong 			 */
11993387Schinlong 			if (cp->pid != getpid()) {
12003387Schinlong 				(void) rw_unlock(&sessionPoolLock);
12013387Schinlong 				DropConnection(cp->connectionId,
12024522Schinlong 				    NS_LDAP_NEW_CONN);
12033387Schinlong 
12043387Schinlong 				goto get_conn;
12053387Schinlong 			}
12062830Sdjl 			/* allocate TSD for per thread ldap error */
12072830Sdjl 			rc = tsd_setup();
12082830Sdjl 
12092830Sdjl 			/* if we got TSD, this connection is shared */
12102830Sdjl 			if (rc != -1)
12112830Sdjl 				cp->shared++;
12122830Sdjl 			else if (cp->shared == 0) {
12132830Sdjl 				cp->usedBit = B_TRUE;
12142830Sdjl 				cp->threadID = thr_self();
12152830Sdjl 				(void) rw_unlock(&sessionPoolLock);
12162830Sdjl 				return (-1);
12172830Sdjl 			}
12182830Sdjl 		}
12192830Sdjl 		(void) rw_unlock(&sessionPoolLock);
12202830Sdjl 
12210Sstevel@tonic-gate 		*conp = cp;
12220Sstevel@tonic-gate #ifdef DEBUG
12232830Sdjl 		(void) fprintf(stderr, "tid= %d: Connection found "
12244522Schinlong 		    "cID=%d, shared =%d\n", t, i, cp->shared);
12250Sstevel@tonic-gate 		fflush(stderr);
12260Sstevel@tonic-gate #endif /* DEBUG */
12270Sstevel@tonic-gate 		return (i + CONID_OFFSET);
12280Sstevel@tonic-gate 	}
12293387Schinlong 
12303387Schinlong 	get_conn:
12313387Schinlong 
12322830Sdjl 	(void) rw_unlock(&sessionPoolLock);
12332830Sdjl 
12342830Sdjl 	/*
12352830Sdjl 	 * If multiple threads per connection not supported,
12362830Sdjl 	 * we are done, just return -1 to tell the caller to
12372830Sdjl 	 * proceed with opening a connection
12382830Sdjl 	 */
12392830Sdjl 	if (MTperConn == 0)
12402830Sdjl 		return (-1);
12412830Sdjl 
12422830Sdjl 	/*
12432830Sdjl 	 * No connection can be shared, test to see if
12442830Sdjl 	 * one is being opened. If trylock returns
12452830Sdjl 	 * EBUSY then it is, so wait until the opening
12462830Sdjl 	 * is done and try to see if the new connection
12472830Sdjl 	 * can be shared.
12482830Sdjl 	 */
12492830Sdjl 	rc = mutex_trylock(&sessionLock);
12502830Sdjl 	if (rc == EBUSY) {
12512830Sdjl 		(void) mutex_lock(&sessionLock);
12522830Sdjl 		(void) mutex_unlock(&sessionLock);
12532830Sdjl 		(void) rw_rdlock(&sessionPoolLock);
12542830Sdjl #ifdef DEBUG
12552830Sdjl 		(void) fprintf(stderr, "tid= %d: check session "
12564522Schinlong 		    "pool again\n", t);
12572830Sdjl 		fflush(stderr);
12582830Sdjl #endif /* DEBUG */
12592830Sdjl 		if (try < TRY_TIMES) {
12602830Sdjl 			try++;
12612830Sdjl 			goto check_again;
12622830Sdjl 		} else {
12632830Sdjl 			syslog(LOG_WARNING, "libsldap: mutex_trylock "
12644522Schinlong 			    "%d times. Stop.", TRY_TIMES);
12653387Schinlong 			(void) rw_unlock(&sessionPoolLock);
12662830Sdjl 			return (-1);
12672830Sdjl 		}
12682830Sdjl 	} else if (rc == 0) {
12692830Sdjl 		/*
12702830Sdjl 		 * No connection can be shared, none being opened,
12712830Sdjl 		 * exit with sessionLock locked to open one. The
12722830Sdjl 		 * mutex will be unlocked in addConnection() when
12732830Sdjl 		 * this thread adds the new connection to the pool
12742830Sdjl 		 * or in __s_api_getConnection() when it exits
12752830Sdjl 		 * without getting a connection.
12762830Sdjl 		 */
12772830Sdjl 		wait4session = 1;
12782830Sdjl 		sessionTid = thr_self();
12792830Sdjl #ifdef DEBUG
12802830Sdjl 		(void) fprintf(stderr, "tid= %d: no connection found, "
12814522Schinlong 		    "none being opened, get connection ...\n", t);
12822830Sdjl 		fflush(stderr);
12832830Sdjl #endif /* DEBUG */
12842830Sdjl 		return (-1);
12852830Sdjl 	} else {
12862830Sdjl 		syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
12874522Schinlong 		    "error %d", rc);
12882830Sdjl 		return (-1);
12892830Sdjl 	}
12900Sstevel@tonic-gate }
12910Sstevel@tonic-gate 
12920Sstevel@tonic-gate /*
12930Sstevel@tonic-gate  * Free a Connection structure
12940Sstevel@tonic-gate  */
12950Sstevel@tonic-gate static void
12960Sstevel@tonic-gate freeConnection(Connection *con)
12970Sstevel@tonic-gate {
12980Sstevel@tonic-gate 	if (con == NULL)
12990Sstevel@tonic-gate 		return;
13000Sstevel@tonic-gate 	if (con->serverAddr)
13010Sstevel@tonic-gate 		free(con->serverAddr);
13020Sstevel@tonic-gate 	if (con->auth)
13030Sstevel@tonic-gate 		(void) __ns_ldap_freeCred(&(con->auth));
13040Sstevel@tonic-gate 	if (con->saslMechanisms) {
13050Sstevel@tonic-gate 		__s_api_free2dArray(con->saslMechanisms);
13060Sstevel@tonic-gate 	}
13070Sstevel@tonic-gate 	if (con->controls) {
13080Sstevel@tonic-gate 		__s_api_free2dArray(con->controls);
13090Sstevel@tonic-gate 	}
13100Sstevel@tonic-gate 	free(con);
13110Sstevel@tonic-gate }
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate /*
13140Sstevel@tonic-gate  * Find a connection matching the passed in criteria.  If an open
13150Sstevel@tonic-gate  * connection with that criteria exists use it, otherwise open a
13160Sstevel@tonic-gate  * new connection.
13170Sstevel@tonic-gate  * Success: returns the pointer to the Connection structure
13180Sstevel@tonic-gate  * Failure: returns NULL, error code and message should be in errorp
13190Sstevel@tonic-gate  */
13201687Sjanga 
13210Sstevel@tonic-gate static int
13220Sstevel@tonic-gate makeConnection(Connection **conp, const char *serverAddr,
13230Sstevel@tonic-gate 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
13241179Svv149972 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
13252830Sdjl 	int nopasswd_acct_mgmt, int flags, char ***badsrvrs)
13260Sstevel@tonic-gate {
13270Sstevel@tonic-gate 	Connection *con = NULL;
13280Sstevel@tonic-gate 	ConnectionID id;
13290Sstevel@tonic-gate 	char errmsg[MAXERROR];
13300Sstevel@tonic-gate 	int rc, exit_rc = NS_LDAP_SUCCESS;
13310Sstevel@tonic-gate 	ns_server_info_t sinfo;
13320Sstevel@tonic-gate 	char *hReq, *host = NULL;
13330Sstevel@tonic-gate 	LDAP *ld = NULL;
13340Sstevel@tonic-gate 	int passwd_mgmt = 0;
13351687Sjanga 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
13362830Sdjl 	short	memerr = 0; /* Variable for tracking memory allocation */
13374522Schinlong 	char *serverAddrType = NULL, **bindHost = NULL;
13382830Sdjl 
13390Sstevel@tonic-gate 
13400Sstevel@tonic-gate 	if (conp == NULL || errorp == NULL || auth == NULL)
13410Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
13420Sstevel@tonic-gate 	*errorp = NULL;
13430Sstevel@tonic-gate 	*conp = NULL;
13444522Schinlong 	(void) memset(&sinfo, 0, sizeof (sinfo));
13450Sstevel@tonic-gate 
13464048Schinlong 	if ((wait4session == 0 || sessionTid != thr_self()) &&
13474522Schinlong 	    (id = findConnection(flags, serverAddr, auth, &con)) != -1) {
13480Sstevel@tonic-gate 		/* connection found in cache */
13490Sstevel@tonic-gate #ifdef DEBUG
13502830Sdjl 		(void) fprintf(stderr, "tid= %d: connection found in "
13514522Schinlong 		    "cache %d\n", thr_self(), id);
13520Sstevel@tonic-gate 		fflush(stderr);
13530Sstevel@tonic-gate #endif /* DEBUG */
13540Sstevel@tonic-gate 		*cID = id;
13550Sstevel@tonic-gate 		*conp = con;
13560Sstevel@tonic-gate 		return (NS_LDAP_SUCCESS);
13570Sstevel@tonic-gate 	}
13580Sstevel@tonic-gate 
13594522Schinlong 	if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
13602830Sdjl 		serverAddrType = NS_CACHE_ADDR_HOSTNAME;
13614522Schinlong 		bindHost = &sinfo.serverFQDN;
13624522Schinlong 	} else {
13632830Sdjl 		serverAddrType = NS_CACHE_ADDR_IP;
13644522Schinlong 		bindHost = &sinfo.server;
13654522Schinlong 	}
13662830Sdjl 
13670Sstevel@tonic-gate 	if (serverAddr) {
13685559Ssdussud 		/*
13695559Ssdussud 		 * We're given the server address, just use it.
13705559Ssdussud 		 * In case of sasl/GSSAPI, serverAddr would need to be a FQDN.
13715559Ssdussud 		 * We assume this is the case for now.
13725559Ssdussud 		 *
13735559Ssdussud 		 * Only the server address fields of sinfo structure are filled
13745559Ssdussud 		 * in since these are the only relevant data that we have. Other
13755559Ssdussud 		 * fields of this structure (controls, saslMechanisms) are
13765559Ssdussud 		 * kept to NULL.
13775559Ssdussud 		 */
13785559Ssdussud 		sinfo.server = strdup(serverAddr);
13795559Ssdussud 		if (sinfo.server == NULL)  {
13805559Ssdussud 			return (NS_LDAP_MEMORY);
13815559Ssdussud 		}
13825559Ssdussud 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
13835559Ssdussud 			sinfo.serverFQDN = strdup(serverAddr);
13845559Ssdussud 			if (sinfo.serverFQDN == NULL) {
13855559Ssdussud 				free(sinfo.server);
13865559Ssdussud 				return (NS_LDAP_MEMORY);
13875559Ssdussud 			}
13882830Sdjl 		}
13894522Schinlong 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
13904522Schinlong 		    fail_if_new_pwd_reqd, passwd_mgmt);
13910Sstevel@tonic-gate 		if (rc == NS_LDAP_SUCCESS || rc ==
13924522Schinlong 		    NS_LDAP_SUCCESS_WITH_INFO) {
13930Sstevel@tonic-gate 			exit_rc = rc;
13940Sstevel@tonic-gate 			goto create_con;
13950Sstevel@tonic-gate 		} else {
13965559Ssdussud 			if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
13975559Ssdussud 				(void) snprintf(errmsg, sizeof (errmsg),
13985559Ssdussud 				    "%s %s", gettext("makeConnection: "
13995559Ssdussud 				    "failed to open connection using "
14005559Ssdussud 				    "sasl/GSSAPI to"), *bindHost);
14015559Ssdussud 			} else {
14025559Ssdussud 				(void) snprintf(errmsg, sizeof (errmsg),
14035559Ssdussud 				    "%s %s", gettext("makeConnection: "
14045559Ssdussud 				    "failed to open connection to"),
14055559Ssdussud 				    *bindHost);
14065559Ssdussud 			}
14075559Ssdussud 			syslog(LOG_ERR, "libsldap: %s", errmsg);
14085559Ssdussud 			__s_api_free_server_info(&sinfo);
14090Sstevel@tonic-gate 			return (rc);
14100Sstevel@tonic-gate 		}
14110Sstevel@tonic-gate 	}
14120Sstevel@tonic-gate 
14130Sstevel@tonic-gate 	/* No cached connection, create one */
14140Sstevel@tonic-gate 	for (; ; ) {
14150Sstevel@tonic-gate 		if (host == NULL)
14160Sstevel@tonic-gate 			hReq = NS_CACHE_NEW;
14170Sstevel@tonic-gate 		else
14180Sstevel@tonic-gate 			hReq = NS_CACHE_NEXT;
14192830Sdjl 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
14204522Schinlong 		    serverAddrType);
14210Sstevel@tonic-gate 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
14224522Schinlong 		    (host && (strcasecmp(host, sinfo.server) == 0))) {
14230Sstevel@tonic-gate 			/* Log the error */
14240Sstevel@tonic-gate 			if (*errorp) {
14250Sstevel@tonic-gate 				(void) snprintf(errmsg, sizeof (errmsg),
14260Sstevel@tonic-gate 				"%s: (%s)", gettext("makeConnection: "
14270Sstevel@tonic-gate 				"unable to make LDAP connection, "
14280Sstevel@tonic-gate 				"request for a server failed"),
14290Sstevel@tonic-gate 				    (*errorp)->message);
14300Sstevel@tonic-gate 				syslog(LOG_ERR, "libsldap: %s", errmsg);
14310Sstevel@tonic-gate 			}
14320Sstevel@tonic-gate 
14334522Schinlong 			__s_api_free_server_info(&sinfo);
14340Sstevel@tonic-gate 			if (host)
14350Sstevel@tonic-gate 				free(host);
14360Sstevel@tonic-gate 			return (NS_LDAP_OP_FAILED);
14370Sstevel@tonic-gate 		}
14380Sstevel@tonic-gate 		if (host)
14390Sstevel@tonic-gate 			free(host);
14400Sstevel@tonic-gate 		host = strdup(sinfo.server);
14410Sstevel@tonic-gate 		if (host == NULL) {
14424522Schinlong 			__s_api_free_server_info(&sinfo);
14430Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
14440Sstevel@tonic-gate 		}
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate 		/* check if server supports password management */
14470Sstevel@tonic-gate 		passwd_mgmt = __s_api_contain_passwd_control_oid(
14484522Schinlong 		    sinfo.controls);
14491179Svv149972 		/* check if server supports password less account mgmt */
14501179Svv149972 		if (nopasswd_acct_mgmt &&
14514522Schinlong 		    !__s_api_contain_account_usable_control_oid(
14524522Schinlong 		    sinfo.controls)) {
14531179Svv149972 			syslog(LOG_WARNING, "libsldap: server %s does not "
14544522Schinlong 			    "provide account information without password",
14554522Schinlong 			    host);
14561179Svv149972 			free(host);
14574522Schinlong 			__s_api_free_server_info(&sinfo);
14581179Svv149972 			return (NS_LDAP_OP_FAILED);
14591179Svv149972 		}
14600Sstevel@tonic-gate 		/* make the connection */
14614522Schinlong 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
14624522Schinlong 		    fail_if_new_pwd_reqd, passwd_mgmt);
14630Sstevel@tonic-gate 		/* if success, go to create connection structure */
14640Sstevel@tonic-gate 		if (rc == NS_LDAP_SUCCESS ||
14654522Schinlong 		    rc == NS_LDAP_SUCCESS_WITH_INFO) {
14660Sstevel@tonic-gate 			exit_rc = rc;
14670Sstevel@tonic-gate 			break;
14680Sstevel@tonic-gate 		}
14690Sstevel@tonic-gate 
14700Sstevel@tonic-gate 		/*
14710Sstevel@tonic-gate 		 * If not able to reach the server, inform the ldap
14720Sstevel@tonic-gate 		 * cache manager that the server should be removed
14730Sstevel@tonic-gate 		 * from its server list. Thus, the manager will not
14740Sstevel@tonic-gate 		 * return this server on the next get-server request
14750Sstevel@tonic-gate 		 * and will also reduce the server list refresh TTL,
14760Sstevel@tonic-gate 		 * so that it will find out sooner when the server
14770Sstevel@tonic-gate 		 * is up again.
14780Sstevel@tonic-gate 		 */
14790Sstevel@tonic-gate 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
14800Sstevel@tonic-gate 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
14814522Schinlong 			    (*errorp)->status == LDAP_SERVER_DOWN) {
14821687Sjanga 				/* Reset memory allocation error */
14831687Sjanga 				memerr = 0;
14841687Sjanga 				/*
14851687Sjanga 				 * We contacted a server that we could
14861687Sjanga 				 * not either authenticate to or contact.
14871687Sjanga 				 * If it is due to authentication, then
14881687Sjanga 				 * we need to try the server again. So,
14891687Sjanga 				 * do not remove the server yet, but
14901687Sjanga 				 * add it to the bad server list.
14911687Sjanga 				 * The caller routine will remove
14921687Sjanga 				 * the servers if:
14931687Sjanga 				 *	a). A good server is found or
14941687Sjanga 				 *	b). All the possible methods
14951687Sjanga 				 *	    are tried without finding
14961687Sjanga 				 *	    a good server
14971687Sjanga 				 */
14981687Sjanga 				if (*badsrvrs == NULL) {
14994522Schinlong 					if (!(*badsrvrs = (char **)malloc
15004522Schinlong 					    (sizeof (char *) * NUMTOMALLOC))) {
15014522Schinlong 						memerr = 1;
15024522Schinlong 					}
15031687Sjanga 				/* Allocate memory in chunks of NUMTOMALLOC */
15041687Sjanga 				} else if ((totalbad % NUMTOMALLOC) ==
15054522Schinlong 				    NUMTOMALLOC - 1) {
15064522Schinlong 					char **tmpptr;
15074522Schinlong 					if (!(tmpptr = (char **)realloc(
15084522Schinlong 					    *badsrvrs,
15091687Sjanga 					    (sizeof (char *) * NUMTOMALLOC *
15101687Sjanga 					    ((totalbad/NUMTOMALLOC) + 2))))) {
15114522Schinlong 						memerr = 1;
15124522Schinlong 					} else {
15134522Schinlong 						*badsrvrs = tmpptr;
15144522Schinlong 					}
1515493Ssdussud 				}
15161687Sjanga 				/*
15171687Sjanga 				 * Store host only if there were no unsuccessful
15181687Sjanga 				 * memory allocations above
15191687Sjanga 				 */
15201687Sjanga 				if (!memerr &&
15211687Sjanga 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
15221687Sjanga 					memerr = 1;
15231687Sjanga 					totalbad--;
15241687Sjanga 				}
15251687Sjanga 				(*badsrvrs)[totalbad] = NULL;
1526493Ssdussud 			}
15270Sstevel@tonic-gate 		}
15280Sstevel@tonic-gate 
15290Sstevel@tonic-gate 		/* else, cleanup and go for the next server */
15304522Schinlong 		__s_api_free_server_info(&sinfo);
15314522Schinlong 
15321687Sjanga 		/* Return if we had memory allocation errors */
15331687Sjanga 		if (memerr)
15341687Sjanga 			return (NS_LDAP_MEMORY);
15350Sstevel@tonic-gate 		if (*errorp) {
15360Sstevel@tonic-gate 			/*
15370Sstevel@tonic-gate 			 * If openConnection() failed due to
15380Sstevel@tonic-gate 			 * password policy, or invalid credential,
15390Sstevel@tonic-gate 			 * keep *errorp and exit
15400Sstevel@tonic-gate 			 */
15410Sstevel@tonic-gate 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
15420Sstevel@tonic-gate 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
15430Sstevel@tonic-gate 				free(host);
15440Sstevel@tonic-gate 				return (rc);
15450Sstevel@tonic-gate 			} else {
15460Sstevel@tonic-gate 				(void) __ns_ldap_freeError(errorp);
15470Sstevel@tonic-gate 				*errorp = NULL;
15480Sstevel@tonic-gate 			}
15490Sstevel@tonic-gate 		}
15500Sstevel@tonic-gate 	}
15510Sstevel@tonic-gate 
15520Sstevel@tonic-gate create_con:
15530Sstevel@tonic-gate 	/* we have created ld, setup con structure */
15540Sstevel@tonic-gate 	if (host)
15550Sstevel@tonic-gate 		free(host);
15560Sstevel@tonic-gate 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
15574522Schinlong 		__s_api_free_server_info(&sinfo);
15580Sstevel@tonic-gate 		/*
15590Sstevel@tonic-gate 		 * If password control attached in **errorp,
15600Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
15610Sstevel@tonic-gate 		 * free the error structure
15620Sstevel@tonic-gate 		 */
15630Sstevel@tonic-gate 		if (*errorp) {
15640Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
15650Sstevel@tonic-gate 			*errorp = NULL;
15660Sstevel@tonic-gate 		}
15675559Ssdussud 		(void) ldap_unbind(ld);
15680Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
15690Sstevel@tonic-gate 	}
15700Sstevel@tonic-gate 
15714522Schinlong 	con->serverAddr = sinfo.server; /* Store original format */
15724522Schinlong 	if (sinfo.serverFQDN != NULL) {
15734522Schinlong 		free(sinfo.serverFQDN);
15744522Schinlong 		sinfo.serverFQDN = NULL;
15754522Schinlong 	}
15760Sstevel@tonic-gate 	con->saslMechanisms = sinfo.saslMechanisms;
15770Sstevel@tonic-gate 	con->controls = sinfo.controls;
15780Sstevel@tonic-gate 
15790Sstevel@tonic-gate 	con->auth = __ns_ldap_dupAuth(auth);
15800Sstevel@tonic-gate 	if (con->auth == NULL) {
15815559Ssdussud 		(void) ldap_unbind(ld);
15825559Ssdussud 		freeConnection(con);
15830Sstevel@tonic-gate 		/*
15840Sstevel@tonic-gate 		 * If password control attached in **errorp,
15850Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
15860Sstevel@tonic-gate 		 * free the error structure
15870Sstevel@tonic-gate 		 */
15880Sstevel@tonic-gate 		if (*errorp) {
15890Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
15900Sstevel@tonic-gate 			*errorp = NULL;
15910Sstevel@tonic-gate 		}
15920Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
15930Sstevel@tonic-gate 	}
15940Sstevel@tonic-gate 
15950Sstevel@tonic-gate 	con->threadID = thr_self();
15963387Schinlong 	con->pid = getpid();
15972830Sdjl 
15980Sstevel@tonic-gate 	con->ld = ld;
15990Sstevel@tonic-gate 	if ((id = addConnection(con)) == -1) {
16005559Ssdussud 		(void) ldap_unbind(ld);
16010Sstevel@tonic-gate 		freeConnection(con);
16020Sstevel@tonic-gate 		/*
16030Sstevel@tonic-gate 		 * If password control attached in **errorp,
16040Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
16050Sstevel@tonic-gate 		 * free the error structure
16060Sstevel@tonic-gate 		 */
16070Sstevel@tonic-gate 		if (*errorp) {
16080Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
16090Sstevel@tonic-gate 			*errorp = NULL;
16100Sstevel@tonic-gate 		}
16110Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
16120Sstevel@tonic-gate 	}
16130Sstevel@tonic-gate #ifdef DEBUG
16142830Sdjl 	(void) fprintf(stderr, "tid= %d: connection added into "
16154522Schinlong 	    "cache %d\n", thr_self(), id);
16160Sstevel@tonic-gate 	fflush(stderr);
16170Sstevel@tonic-gate #endif /* DEBUG */
16180Sstevel@tonic-gate 	*cID = id;
16190Sstevel@tonic-gate 	*conp = con;
16200Sstevel@tonic-gate 	return (exit_rc);
16210Sstevel@tonic-gate }
16220Sstevel@tonic-gate 
16230Sstevel@tonic-gate /*
16240Sstevel@tonic-gate  * Return the specified connection to the pool.  If necessary
16250Sstevel@tonic-gate  * delete the connection.
16260Sstevel@tonic-gate  */
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate static void
16290Sstevel@tonic-gate _DropConnection(ConnectionID cID, int flag, int fini)
16300Sstevel@tonic-gate {
16310Sstevel@tonic-gate 	Connection *cp;
16320Sstevel@tonic-gate 	int id;
16332830Sdjl 	int use_lock = !fini;
16342830Sdjl #ifdef DEBUG
16352830Sdjl 	thread_t t = thr_self();
16362830Sdjl #endif /* DEBUG */
16370Sstevel@tonic-gate 
16380Sstevel@tonic-gate 	id = cID - CONID_OFFSET;
16390Sstevel@tonic-gate 	if (id < 0 || id >= sessionPoolSize)
16400Sstevel@tonic-gate 		return;
16410Sstevel@tonic-gate #ifdef DEBUG
16422830Sdjl 	(void) fprintf(stderr, "tid= %d: "
16434522Schinlong 	    "Dropping connection cID=%d flag=0x%x, fini = %d\n",
16444522Schinlong 	    t, cID, flag, fini);
16450Sstevel@tonic-gate 	fflush(stderr);
16460Sstevel@tonic-gate #endif /* DEBUG */
16472830Sdjl 	if (use_lock)
16482830Sdjl 		(void) rw_wrlock(&sessionPoolLock);
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 	cp = sessionPool[id];
16510Sstevel@tonic-gate 	/* sanity check before removing */
16524048Schinlong 	if (!cp || (!fini && !cp->shared && !cp->usedBit)) {
16532830Sdjl #ifdef DEBUG
16542830Sdjl 		if (cp == NULL)
16552830Sdjl 			(void) fprintf(stderr, "tid= %d: no "
16564522Schinlong 			    "need to remove (fini = %d, cp = %p)\n", t,
16574522Schinlong 			    fini, cp);
16582830Sdjl 		else
16592830Sdjl 			(void) fprintf(stderr, "tid= %d: no "
16604522Schinlong 			    "need to remove (fini = %d, cp = %p, shared = %d)"
16614522Schinlong 			    "\n", t, fini, cp, cp->shared);
16622830Sdjl 		fflush(stderr);
16632830Sdjl #endif /* DEBUG */
16642830Sdjl 		if (use_lock)
16652830Sdjl 			(void) rw_unlock(&sessionPoolLock);
16660Sstevel@tonic-gate 		return;
16670Sstevel@tonic-gate 	}
16680Sstevel@tonic-gate 
16690Sstevel@tonic-gate 	if (!fini &&
16704522Schinlong 	    ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail &&
16715532Smj162486 	    ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc())) {
16722830Sdjl #ifdef DEBUG
16732830Sdjl 		(void) fprintf(stderr, "tid= %d: keep alive (fini = %d "
16744522Schinlong 		    "shared = %d)\n", t, fini, cp->shared);
16752830Sdjl #endif /* DEBUG */
16760Sstevel@tonic-gate 		/* release Connection (keep alive) */
16772830Sdjl 		if (cp->shared)
16782830Sdjl 			cp->shared--;
16790Sstevel@tonic-gate 		cp->usedBit = B_FALSE;
16800Sstevel@tonic-gate 		cp->threadID = 0;	/* unmark the threadID */
16812830Sdjl 		if (use_lock)
16822830Sdjl 			(void) rw_unlock(&sessionPoolLock);
16830Sstevel@tonic-gate 	} else {
16840Sstevel@tonic-gate 		/* delete Connection (disconnect) */
16852830Sdjl 		if (cp->shared > 0) {
16862830Sdjl #ifdef DEBUG
16872830Sdjl 		(void) fprintf(stderr, "tid= %d: Connection no "
16884522Schinlong 		    "longer available (fini = %d, shared = %d)\n",
16894522Schinlong 		    t, fini, cp->shared);
16902830Sdjl 		fflush(stderr);
16912830Sdjl #endif /* DEBUG */
16922830Sdjl 			cp->shared--;
16933387Schinlong 			/*
16943387Schinlong 			 * Mark this connection not available and decrement
16953387Schinlong 			 * sharedConnNumber. There could be multiple threads
16963387Schinlong 			 * sharing this connection so decrement
16973387Schinlong 			 * sharedConnNumber only once per connection.
16983387Schinlong 			 */
16993387Schinlong 			if (cp->notAvail == 0) {
17003387Schinlong 				cp->notAvail = 1;
17013387Schinlong 				(void) mutex_lock(&sharedConnNumberLock);
17023387Schinlong 				sharedConnNumber--;
17033387Schinlong 				(void) mutex_unlock(&sharedConnNumberLock);
17043387Schinlong 			}
17052830Sdjl 		}
17062830Sdjl 
17072830Sdjl 		if (cp->shared <= 0) {
17082830Sdjl #ifdef DEBUG
17092830Sdjl 			(void) fprintf(stderr, "tid= %d: unbind "
17104522Schinlong 			    "(fini = %d, shared = %d)\n",
17114522Schinlong 			    t, fini, cp->shared);
17122830Sdjl 			fflush(stderr);
17132830Sdjl #endif /* DEBUG */
17142830Sdjl 			sessionPool[id] = NULL;
17152830Sdjl 			(void) ldap_unbind(cp->ld);
17162830Sdjl 			freeConnection(cp);
17172830Sdjl 		}
17182830Sdjl 
17192830Sdjl 		if (use_lock)
17202830Sdjl 			(void) rw_unlock(&sessionPoolLock);
17210Sstevel@tonic-gate 	}
17220Sstevel@tonic-gate }
17230Sstevel@tonic-gate 
17240Sstevel@tonic-gate void
17250Sstevel@tonic-gate DropConnection(ConnectionID cID, int flag)
17260Sstevel@tonic-gate {
17270Sstevel@tonic-gate 	_DropConnection(cID, flag, 0);
17280Sstevel@tonic-gate }
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate /*
17310Sstevel@tonic-gate  * This routine is called after a bind operation is
17320Sstevel@tonic-gate  * done in openConnection() to process the password
17330Sstevel@tonic-gate  * management information, if any.
17340Sstevel@tonic-gate  *
17350Sstevel@tonic-gate  * Input:
17360Sstevel@tonic-gate  *   bind_type: "simple" or "sasl/DIGEST-MD5"
17370Sstevel@tonic-gate  *   ldaprc   : ldap rc from the ldap bind operation
17380Sstevel@tonic-gate  *   controls : controls returned by the server
17390Sstevel@tonic-gate  *   errmsg   : error message from the server
17400Sstevel@tonic-gate  *   fail_if_new_pwd_reqd:
17410Sstevel@tonic-gate  *              flag indicating if connection should be open
17420Sstevel@tonic-gate  *              when password needs to change immediately
17430Sstevel@tonic-gate  *   passwd_mgmt:
17440Sstevel@tonic-gate  *              flag indicating if server supports password
17450Sstevel@tonic-gate  *              policy/management
17460Sstevel@tonic-gate  *
17470Sstevel@tonic-gate  * Output     : ns_ldap_error structure, which may contain
17480Sstevel@tonic-gate  *              password status and number of seconds until
17490Sstevel@tonic-gate  *              expired
17500Sstevel@tonic-gate  *
17510Sstevel@tonic-gate  * return rc:
17520Sstevel@tonic-gate  * NS_LDAP_EXTERNAL: error, connection should not open
17530Sstevel@tonic-gate  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
17540Sstevel@tonic-gate  * NS_LDAP_SUCCESS: OK to open connection
17550Sstevel@tonic-gate  *
17560Sstevel@tonic-gate  */
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate static int
17590Sstevel@tonic-gate process_pwd_mgmt(char *bind_type, int ldaprc,
17600Sstevel@tonic-gate 		LDAPControl **controls,
17610Sstevel@tonic-gate 		char *errmsg, ns_ldap_error_t **errorp,
17620Sstevel@tonic-gate 		int fail_if_new_pwd_reqd,
17630Sstevel@tonic-gate 		int passwd_mgmt)
17640Sstevel@tonic-gate {
17650Sstevel@tonic-gate 	char		errstr[MAXERROR];
17660Sstevel@tonic-gate 	LDAPControl	**ctrl = NULL;
17670Sstevel@tonic-gate 	int		exit_rc;
17680Sstevel@tonic-gate 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
17690Sstevel@tonic-gate 	int		sec_until_exp = 0;
17700Sstevel@tonic-gate 
17710Sstevel@tonic-gate 	/*
17720Sstevel@tonic-gate 	 * errmsg may be an empty string,
17730Sstevel@tonic-gate 	 * even if ldaprc is LDAP_SUCCESS,
17740Sstevel@tonic-gate 	 * free the empty string if that's the case
17750Sstevel@tonic-gate 	 */
17760Sstevel@tonic-gate 	if (errmsg &&
17774522Schinlong 	    (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
17780Sstevel@tonic-gate 		ldap_memfree(errmsg);
17790Sstevel@tonic-gate 		errmsg = NULL;
17800Sstevel@tonic-gate 	}
17810Sstevel@tonic-gate 
17820Sstevel@tonic-gate 	if (ldaprc != LDAP_SUCCESS) {
17830Sstevel@tonic-gate 		/*
17840Sstevel@tonic-gate 		 * try to map ldap rc and error message to
17850Sstevel@tonic-gate 		 * a password status
17860Sstevel@tonic-gate 		 */
17870Sstevel@tonic-gate 		if (errmsg) {
17880Sstevel@tonic-gate 			if (passwd_mgmt)
17890Sstevel@tonic-gate 				pwd_status =
17904522Schinlong 				    __s_api_set_passwd_status(
17914522Schinlong 				    ldaprc, errmsg);
17920Sstevel@tonic-gate 			ldap_memfree(errmsg);
17930Sstevel@tonic-gate 		}
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 		(void) snprintf(errstr, sizeof (errstr),
17964522Schinlong 		    gettext("openConnection: "
17974522Schinlong 		    "%s bind failed "
17984522Schinlong 		    "- %s"), bind_type, ldap_err2string(ldaprc));
17990Sstevel@tonic-gate 
18000Sstevel@tonic-gate 		if (pwd_status != NS_PASSWD_GOOD) {
18010Sstevel@tonic-gate 			MKERROR_PWD_MGMT(*errorp,
18024522Schinlong 			    ldaprc, strdup(errstr),
18034522Schinlong 			    pwd_status, 0, NULL);
18040Sstevel@tonic-gate 		} else {
18050Sstevel@tonic-gate 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
18064522Schinlong 			    NULL);
18070Sstevel@tonic-gate 		}
18080Sstevel@tonic-gate 		if (controls)
18090Sstevel@tonic-gate 			ldap_controls_free(controls);
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 		return (NS_LDAP_INTERNAL);
18120Sstevel@tonic-gate 	}
18130Sstevel@tonic-gate 
18140Sstevel@tonic-gate 	/*
18150Sstevel@tonic-gate 	 * ldaprc is LDAP_SUCCESS,
18160Sstevel@tonic-gate 	 * process the password management controls, if any
18170Sstevel@tonic-gate 	 */
18180Sstevel@tonic-gate 	exit_rc = NS_LDAP_SUCCESS;
18190Sstevel@tonic-gate 	if (controls && passwd_mgmt) {
18200Sstevel@tonic-gate 		/*
18210Sstevel@tonic-gate 		 * The control with the OID
18220Sstevel@tonic-gate 		 * 2.16.840.1.113730.3.4.4 (or
18230Sstevel@tonic-gate 		 * LDAP_CONTROL_PWEXPIRED, as defined
18240Sstevel@tonic-gate 		 * in the ldap.h header file) is the
18250Sstevel@tonic-gate 		 * expired password control.
18260Sstevel@tonic-gate 		 *
18270Sstevel@tonic-gate 		 * This control is used if the server
18280Sstevel@tonic-gate 		 * is configured to require users to
18290Sstevel@tonic-gate 		 * change their passwords when first
18300Sstevel@tonic-gate 		 * logging in and whenever the
18310Sstevel@tonic-gate 		 * passwords are reset.
18320Sstevel@tonic-gate 		 *
18330Sstevel@tonic-gate 		 * If the user is logging in for the
18340Sstevel@tonic-gate 		 * first time or if the user's
18350Sstevel@tonic-gate 		 * password has been reset, the
18360Sstevel@tonic-gate 		 * server sends this control to
18370Sstevel@tonic-gate 		 * indicate that the client needs to
18380Sstevel@tonic-gate 		 * change the password immediately.
18390Sstevel@tonic-gate 		 *
18400Sstevel@tonic-gate 		 * At this point, the only operation
18410Sstevel@tonic-gate 		 * that the client can perform is to
18420Sstevel@tonic-gate 		 * change the user's password. If the
18430Sstevel@tonic-gate 		 * client requests any other LDAP
18440Sstevel@tonic-gate 		 * operation, the server sends back
18450Sstevel@tonic-gate 		 * an LDAP_UNWILLING_TO_PERFORM
18460Sstevel@tonic-gate 		 * result code with an expired
18470Sstevel@tonic-gate 		 * password control.
18480Sstevel@tonic-gate 		 *
18490Sstevel@tonic-gate 		 * The control with the OID
18500Sstevel@tonic-gate 		 * 2.16.840.1.113730.3.4.5 (or
18510Sstevel@tonic-gate 		 * LDAP_CONTROL_PWEXPIRING, as
18520Sstevel@tonic-gate 		 * defined in the ldap.h header file)
18530Sstevel@tonic-gate 		 * is the password expiration warning
18540Sstevel@tonic-gate 		 * control.
18550Sstevel@tonic-gate 		 *
18560Sstevel@tonic-gate 		 * This control is used if the server
18570Sstevel@tonic-gate 		 * is configured to expire user
18580Sstevel@tonic-gate 		 * passwords after a certain amount
18590Sstevel@tonic-gate 		 * of time.
18600Sstevel@tonic-gate 		 *
18610Sstevel@tonic-gate 		 * The server sends this control back
18620Sstevel@tonic-gate 		 * to the client if the client binds
18630Sstevel@tonic-gate 		 * using a password that will soon
18640Sstevel@tonic-gate 		 * expire.  The ldctl_value field of
18650Sstevel@tonic-gate 		 * the LDAPControl structure
18660Sstevel@tonic-gate 		 * specifies the number of seconds
18670Sstevel@tonic-gate 		 * before the password will expire.
18680Sstevel@tonic-gate 		 */
18690Sstevel@tonic-gate 		for (ctrl = controls; *ctrl; ctrl++) {
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 			if (strcmp((*ctrl)->ldctl_oid,
18724522Schinlong 			    LDAP_CONTROL_PWEXPIRED) == 0) {
18730Sstevel@tonic-gate 				/*
18740Sstevel@tonic-gate 				 * if the caller wants this bind
18750Sstevel@tonic-gate 				 * to fail, set up the error info.
18760Sstevel@tonic-gate 				 * If call to this function is
18770Sstevel@tonic-gate 				 * for searching the LDAP directory,
18780Sstevel@tonic-gate 				 * e.g., __ns_ldap_list(),
18790Sstevel@tonic-gate 				 * there's really no sense to
18800Sstevel@tonic-gate 				 * let a connection open and
18810Sstevel@tonic-gate 				 * then fail immediately afterward
18820Sstevel@tonic-gate 				 * on the LDAP search operation with
18830Sstevel@tonic-gate 				 * the LDAP_UNWILLING_TO_PERFORM rc
18840Sstevel@tonic-gate 				 */
18850Sstevel@tonic-gate 				pwd_status =
18864522Schinlong 				    NS_PASSWD_CHANGE_NEEDED;
18870Sstevel@tonic-gate 				if (fail_if_new_pwd_reqd) {
18880Sstevel@tonic-gate 					(void) snprintf(errstr,
18894522Schinlong 					    sizeof (errstr),
18904522Schinlong 					    gettext(
18914522Schinlong 					    "openConnection: "
18924522Schinlong 					    "%s bind "
18934522Schinlong 					    "failed "
18944522Schinlong 					    "- password "
18954522Schinlong 					    "expired. It "
18964522Schinlong 					    " needs to change "
18974522Schinlong 					    "immediately!"),
18984522Schinlong 					    bind_type);
18990Sstevel@tonic-gate 					MKERROR_PWD_MGMT(*errorp,
19004522Schinlong 					    LDAP_SUCCESS,
19014522Schinlong 					    strdup(errstr),
19024522Schinlong 					    pwd_status,
19034522Schinlong 					    0,
19044522Schinlong 					    NULL);
19050Sstevel@tonic-gate 					exit_rc = NS_LDAP_INTERNAL;
19060Sstevel@tonic-gate 				} else {
19070Sstevel@tonic-gate 					MKERROR_PWD_MGMT(*errorp,
19084522Schinlong 					    LDAP_SUCCESS,
19094522Schinlong 					    NULL,
19104522Schinlong 					    pwd_status,
19114522Schinlong 					    0,
19124522Schinlong 					    NULL);
19130Sstevel@tonic-gate 					exit_rc =
19144522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO;
19150Sstevel@tonic-gate 				}
19160Sstevel@tonic-gate 				break;
19170Sstevel@tonic-gate 			} else if (strcmp((*ctrl)->ldctl_oid,
19184522Schinlong 			    LDAP_CONTROL_PWEXPIRING) == 0) {
19190Sstevel@tonic-gate 				pwd_status =
19204522Schinlong 				    NS_PASSWD_ABOUT_TO_EXPIRE;
19210Sstevel@tonic-gate 				if ((*ctrl)->
19224522Schinlong 				    ldctl_value.bv_len > 0 &&
19234522Schinlong 				    (*ctrl)->
19244522Schinlong 				    ldctl_value.bv_val)
19250Sstevel@tonic-gate 					sec_until_exp =
19264522Schinlong 					    atoi((*ctrl)->
19274522Schinlong 					    ldctl_value.bv_val);
19280Sstevel@tonic-gate 				MKERROR_PWD_MGMT(*errorp,
19294522Schinlong 				    LDAP_SUCCESS,
19304522Schinlong 				    NULL,
19314522Schinlong 				    pwd_status,
19324522Schinlong 				    sec_until_exp,
19334522Schinlong 				    NULL);
19340Sstevel@tonic-gate 				exit_rc =
19354522Schinlong 				    NS_LDAP_SUCCESS_WITH_INFO;
19360Sstevel@tonic-gate 				break;
19370Sstevel@tonic-gate 			}
19380Sstevel@tonic-gate 		}
19390Sstevel@tonic-gate 	}
19400Sstevel@tonic-gate 
19410Sstevel@tonic-gate 	if (controls)
19420Sstevel@tonic-gate 		ldap_controls_free(controls);
19430Sstevel@tonic-gate 
19440Sstevel@tonic-gate 	return (exit_rc);
19450Sstevel@tonic-gate }
19460Sstevel@tonic-gate 
19470Sstevel@tonic-gate static int
19480Sstevel@tonic-gate ldap_in_hosts_switch()
19490Sstevel@tonic-gate {
19500Sstevel@tonic-gate 	enum __nsw_parse_err		pserr;
19510Sstevel@tonic-gate 	struct __nsw_switchconfig	*conf;
19520Sstevel@tonic-gate 	struct __nsw_lookup		*lkp;
19530Sstevel@tonic-gate 	const char			*name;
19540Sstevel@tonic-gate 	int				found = 0;
19550Sstevel@tonic-gate 
19560Sstevel@tonic-gate 	conf = __nsw_getconfig("hosts", &pserr);
19570Sstevel@tonic-gate 	if (conf == NULL) {
19580Sstevel@tonic-gate 		return (-1);
19590Sstevel@tonic-gate 	}
19600Sstevel@tonic-gate 
19610Sstevel@tonic-gate 	/* check for skip and count other backends */
19620Sstevel@tonic-gate 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
19630Sstevel@tonic-gate 		name = lkp->service_name;
19640Sstevel@tonic-gate 		if (strcmp(name, "ldap") == 0) {
19650Sstevel@tonic-gate 			found = 1;
19660Sstevel@tonic-gate 			break;
19670Sstevel@tonic-gate 		}
19680Sstevel@tonic-gate 	}
19690Sstevel@tonic-gate 	__nsw_freeconfig(conf);
19700Sstevel@tonic-gate 	return (found);
19710Sstevel@tonic-gate }
19720Sstevel@tonic-gate 
19730Sstevel@tonic-gate static int
19740Sstevel@tonic-gate openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
19750Sstevel@tonic-gate 	int timeoutSec, ns_ldap_error_t **errorp,
19760Sstevel@tonic-gate 	int fail_if_new_pwd_reqd, int passwd_mgmt)
19770Sstevel@tonic-gate {
19780Sstevel@tonic-gate 	LDAP		*ld = NULL;
19790Sstevel@tonic-gate 	char		*binddn, *passwd;
19800Sstevel@tonic-gate 	char		*digest_md5_name;
19810Sstevel@tonic-gate 	const char	*s;
19820Sstevel@tonic-gate 	int		ldapVersion = LDAP_VERSION3;
19830Sstevel@tonic-gate 	int		derefOption = LDAP_DEREF_ALWAYS;
19840Sstevel@tonic-gate 	int		zero = 0;
19850Sstevel@tonic-gate 	int		rc;
19860Sstevel@tonic-gate 	char		errstr[MAXERROR];
19870Sstevel@tonic-gate 	int		errnum = 0;
19880Sstevel@tonic-gate 	LDAPMessage	*resultMsg;
19890Sstevel@tonic-gate 	int		msgId;
19902830Sdjl 	int		useSSL = 0, port = 0;
19910Sstevel@tonic-gate 	struct timeval	tv;
19920Sstevel@tonic-gate 	AuthType_t	bindType;
19930Sstevel@tonic-gate 	int		timeoutMilliSec = timeoutSec * 1000;
19940Sstevel@tonic-gate 	struct berval	cred;
19950Sstevel@tonic-gate 	char		*sslServerAddr;
19960Sstevel@tonic-gate 	char		*s1;
19972830Sdjl 	char		*errmsg, *end = NULL;
19980Sstevel@tonic-gate 	LDAPControl	**controls;
19992830Sdjl 	int		pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
20002830Sdjl 	ns_sasl_cb_param_t	sasl_param;
20010Sstevel@tonic-gate 
20020Sstevel@tonic-gate 	*errorp = NULL;
20030Sstevel@tonic-gate 	*ldp = NULL;
20040Sstevel@tonic-gate 
20050Sstevel@tonic-gate 	switch (auth->auth.type) {
20060Sstevel@tonic-gate 		case NS_LDAP_AUTH_NONE:
20070Sstevel@tonic-gate 		case NS_LDAP_AUTH_SIMPLE:
20080Sstevel@tonic-gate 		case NS_LDAP_AUTH_SASL:
20090Sstevel@tonic-gate 			bindType = auth->auth.type;
20100Sstevel@tonic-gate 			break;
20110Sstevel@tonic-gate 		case NS_LDAP_AUTH_TLS:
20120Sstevel@tonic-gate 			useSSL = 1;
20130Sstevel@tonic-gate 			switch (auth->auth.tlstype) {
20140Sstevel@tonic-gate 				case NS_LDAP_TLS_NONE:
20150Sstevel@tonic-gate 					bindType = NS_LDAP_AUTH_NONE;
20160Sstevel@tonic-gate 					break;
20170Sstevel@tonic-gate 				case NS_LDAP_TLS_SIMPLE:
20180Sstevel@tonic-gate 					bindType = NS_LDAP_AUTH_SIMPLE;
20190Sstevel@tonic-gate 					break;
20200Sstevel@tonic-gate 				case NS_LDAP_TLS_SASL:
20210Sstevel@tonic-gate 					bindType = NS_LDAP_AUTH_SASL;
20220Sstevel@tonic-gate 					break;
20230Sstevel@tonic-gate 				default:
20240Sstevel@tonic-gate 					(void) sprintf(errstr,
20250Sstevel@tonic-gate 					gettext("openConnection: unsupported "
20264522Schinlong 					    "TLS authentication method "
20274522Schinlong 					    "(%d)"), auth->auth.tlstype);
20280Sstevel@tonic-gate 					MKERROR(LOG_WARNING, *errorp,
20294522Schinlong 					    LDAP_AUTH_METHOD_NOT_SUPPORTED,
20304522Schinlong 					    strdup(errstr), NULL);
20310Sstevel@tonic-gate 					return (NS_LDAP_INTERNAL);
20320Sstevel@tonic-gate 			}
20330Sstevel@tonic-gate 			break;
20340Sstevel@tonic-gate 		default:
20350Sstevel@tonic-gate 			(void) sprintf(errstr,
20364522Schinlong 			    gettext("openConnection: unsupported "
20374522Schinlong 			    "authentication method (%d)"), auth->auth.type);
20380Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp,
20394522Schinlong 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
20404522Schinlong 			    NULL);
20410Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
20420Sstevel@tonic-gate 	}
20430Sstevel@tonic-gate 
20440Sstevel@tonic-gate 	if (useSSL) {
20450Sstevel@tonic-gate 		const char	*hostcertpath;
20460Sstevel@tonic-gate 		char		*alloc_hcp = NULL;
20470Sstevel@tonic-gate #ifdef DEBUG
20482830Sdjl 		(void) fprintf(stderr, "tid= %d: +++TLS transport\n",
20494522Schinlong 		    thr_self());
20500Sstevel@tonic-gate #endif /* DEBUG */
20512333Sjanga 
20522333Sjanga 		if (prldap_set_session_option(NULL, NULL,
20532333Sjanga 		    PRLDAP_OPT_IO_MAX_TIMEOUT,
20542333Sjanga 		    timeoutMilliSec) != LDAP_SUCCESS) {
20552333Sjanga 			(void) snprintf(errstr, sizeof (errstr),
20564522Schinlong 			    gettext("openConnection: failed to initialize "
20574522Schinlong 			    "TLS security"));
20582333Sjanga 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
20594522Schinlong 			    strdup(errstr), NULL);
20602333Sjanga 			return (NS_LDAP_INTERNAL);
20612333Sjanga 		}
20622333Sjanga 
20630Sstevel@tonic-gate 		hostcertpath = auth->hostcertpath;
20640Sstevel@tonic-gate 		if (hostcertpath == NULL) {
20650Sstevel@tonic-gate 			alloc_hcp = __s_get_hostcertpath();
20660Sstevel@tonic-gate 			hostcertpath = alloc_hcp;
20670Sstevel@tonic-gate 		}
20680Sstevel@tonic-gate 
20690Sstevel@tonic-gate 		if (hostcertpath == NULL)
20700Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
20710Sstevel@tonic-gate 
20720Sstevel@tonic-gate 		if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
20730Sstevel@tonic-gate 			if (alloc_hcp)
20740Sstevel@tonic-gate 				free(alloc_hcp);
20750Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
20764522Schinlong 			    gettext("openConnection: failed to initialize "
20774522Schinlong 			    "TLS security (%s)"),
20784522Schinlong 			    ldapssl_err2string(rc));
20790Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
20804522Schinlong 			    strdup(errstr), NULL);
20810Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
20820Sstevel@tonic-gate 		}
20830Sstevel@tonic-gate 		if (alloc_hcp)
20840Sstevel@tonic-gate 			free(alloc_hcp);
20850Sstevel@tonic-gate 
20860Sstevel@tonic-gate 		/* determine if the host name contains a port number */
20870Sstevel@tonic-gate 		s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
20880Sstevel@tonic-gate 		if (s == NULL)
20890Sstevel@tonic-gate 			s = serverAddr;
20900Sstevel@tonic-gate 		s = strchr(s, ':');
20910Sstevel@tonic-gate 		if (s != NULL) {
20920Sstevel@tonic-gate 			/*
20930Sstevel@tonic-gate 			 * If we do get a port number, we will try stripping
20940Sstevel@tonic-gate 			 * it. At present, referrals will always have a
20950Sstevel@tonic-gate 			 * port number.
20960Sstevel@tonic-gate 			 */
20970Sstevel@tonic-gate 			sslServerAddr = strdup(serverAddr);
20980Sstevel@tonic-gate 			if (sslServerAddr == NULL)
20990Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
21000Sstevel@tonic-gate 			s1 = strrchr(sslServerAddr, ':');
21010Sstevel@tonic-gate 			if (s1 != NULL)
21020Sstevel@tonic-gate 				*s1 = '\0';
21030Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
21040Sstevel@tonic-gate 			    gettext("openConnection: cannot use tls with %s. "
21054522Schinlong 			    "Trying %s"),
21064522Schinlong 			    serverAddr, sslServerAddr);
21070Sstevel@tonic-gate 			syslog(LOG_ERR, "libsldap: %s", errstr);
21080Sstevel@tonic-gate 		} else
21090Sstevel@tonic-gate 			sslServerAddr = (char *)serverAddr;
21100Sstevel@tonic-gate 
21110Sstevel@tonic-gate 		ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
21120Sstevel@tonic-gate 
21130Sstevel@tonic-gate 		if (sslServerAddr != serverAddr)
21140Sstevel@tonic-gate 			free(sslServerAddr);
21150Sstevel@tonic-gate 
21160Sstevel@tonic-gate 		if (ld == NULL ||
21170Sstevel@tonic-gate 		    ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
21180Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
21194522Schinlong 			    gettext("openConnection: failed to connect "
21204522Schinlong 			    "using TLS (%s)"), strerror(errno));
21210Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
21224522Schinlong 			    strdup(errstr), NULL);
21230Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
21240Sstevel@tonic-gate 		}
21250Sstevel@tonic-gate 	} else {
21260Sstevel@tonic-gate #ifdef DEBUG
21272830Sdjl 		(void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
21284522Schinlong 		    thr_self());
21290Sstevel@tonic-gate #endif /* DEBUG */
21302830Sdjl 		port = LDAP_PORT;
21312830Sdjl 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI &&
21324522Schinlong 		    (end = strchr(serverAddr, ':')) != NULL) {
21332830Sdjl 			/*
21342830Sdjl 			 * The IP is converted to hostname so it's a
21352830Sdjl 			 * hostname:port up to this point.
21362830Sdjl 			 *
21372830Sdjl 			 * libldap passes hostname:port to the sasl layer.
21382830Sdjl 			 * The ldap service principal is constructed as
21392830Sdjl 			 * ldap/hostname:port@REALM. Kerberos authentication
21402830Sdjl 			 * will fail. So it needs to be parsed to construct
21412830Sdjl 			 * a valid principal ldap/hostname@REALM.
21422830Sdjl 			 *
21432830Sdjl 			 * For useSSL case above, it already parses port so
21442830Sdjl 			 * no need to parse serverAddr
21452830Sdjl 			 */
21462830Sdjl 			*end = '\0';
21472830Sdjl 			port = atoi(end + 1);
21482830Sdjl 		}
21492830Sdjl 
21502830Sdjl 		/* Warning message IF cannot connect to host(s) */
21512830Sdjl 		if ((ld = ldap_init((char *)serverAddr, port)) == NULL) {
21520Sstevel@tonic-gate 			char *p = strerror(errno);
21530Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
21544522Schinlong 			    strdup(p), NULL);
21552830Sdjl 			if (end)
21562830Sdjl 				*end = ':';
21570Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
21580Sstevel@tonic-gate 		} else {
21592830Sdjl 			if (end)
21602830Sdjl 				*end = ':';
21610Sstevel@tonic-gate 			/* check and avoid gethostname recursion */
21620Sstevel@tonic-gate 			if (ldap_in_hosts_switch() > 0 &&
21634522Schinlong 			    ! __s_api_isipv4((char *)serverAddr) &&
21644522Schinlong 			    ! __s_api_isipv6((char *)serverAddr)) {
21650Sstevel@tonic-gate 				/* host: ldap - found, attempt to recover */
21660Sstevel@tonic-gate 				if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
21674522Schinlong 				    "ldap") != 0) {
21684522Schinlong 					(void) snprintf(errstr, sizeof (errstr),
21692830Sdjl 					    gettext("openConnection: "
21702830Sdjl 					    "unrecoverable gethostname "
21712830Sdjl 					    "recursion detected "
21722830Sdjl 					    "in /etc/nsswitch.conf"));
21734522Schinlong 					MKERROR(LOG_WARNING, *errorp,
21742830Sdjl 					    LDAP_CONNECT_ERROR,
21752830Sdjl 					    strdup(errstr), NULL);
21764522Schinlong 					(void) ldap_unbind(ld);
21774522Schinlong 					return (NS_LDAP_INTERNAL);
21780Sstevel@tonic-gate 				}
21790Sstevel@tonic-gate 			}
21800Sstevel@tonic-gate 		}
21810Sstevel@tonic-gate 	}
21820Sstevel@tonic-gate 
21832830Sdjl 	ns_setup_mt_conn_and_tsd(ld);
21840Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
21850Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
21860Sstevel@tonic-gate 	/*
21870Sstevel@tonic-gate 	 * set LDAP_OPT_REFERRALS to OFF.
21880Sstevel@tonic-gate 	 * This library will handle the referral itself
21890Sstevel@tonic-gate 	 * based on API flags or configuration file
21900Sstevel@tonic-gate 	 * specification. If this option is not set
21910Sstevel@tonic-gate 	 * to OFF, libldap will never pass the
21920Sstevel@tonic-gate 	 * referral info up to this library
21930Sstevel@tonic-gate 	 */
21940Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
21950Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
21960Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
21970Sstevel@tonic-gate 	/* setup TCP/IP connect timeout */
21980Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
21994522Schinlong 	    &timeoutMilliSec);
22000Sstevel@tonic-gate 	/* retry if LDAP I/O was interrupted */
22010Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 	switch (bindType) {
22040Sstevel@tonic-gate 	case NS_LDAP_AUTH_NONE:
22050Sstevel@tonic-gate #ifdef DEBUG
22062830Sdjl 		(void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
22074522Schinlong 		    thr_self());
22080Sstevel@tonic-gate #endif /* DEBUG */
22090Sstevel@tonic-gate 		break;
22100Sstevel@tonic-gate 	case NS_LDAP_AUTH_SIMPLE:
22110Sstevel@tonic-gate 		binddn = auth->cred.unix_cred.userID;
22120Sstevel@tonic-gate 		passwd = auth->cred.unix_cred.passwd;
22130Sstevel@tonic-gate 		if (passwd == NULL || *passwd == '\0' ||
22140Sstevel@tonic-gate 		    binddn == NULL || *binddn == '\0') {
22150Sstevel@tonic-gate 			(void) sprintf(errstr, gettext("openConnection: "
22164522Schinlong 			    "missing credentials for Simple bind"));
22170Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
22184522Schinlong 			    strdup(errstr), NULL);
22190Sstevel@tonic-gate 			(void) ldap_unbind(ld);
22200Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
22210Sstevel@tonic-gate 		}
22220Sstevel@tonic-gate 
22230Sstevel@tonic-gate #ifdef DEBUG
22242830Sdjl 		(void) fprintf(stderr, "tid= %d: +++Simple bind\n",
22254522Schinlong 		    thr_self());
22260Sstevel@tonic-gate #endif /* DEBUG */
22270Sstevel@tonic-gate 		msgId = ldap_simple_bind(ld, binddn, passwd);
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 		if (msgId == -1) {
22300Sstevel@tonic-gate 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
22314522Schinlong 			    (void *)&errnum);
22320Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
22334522Schinlong 			    gettext("openConnection: simple bind failed "
22344522Schinlong 			    "- %s"), ldap_err2string(errnum));
22350Sstevel@tonic-gate 			(void) ldap_unbind(ld);
22360Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
22374522Schinlong 			    NULL);
22380Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
22390Sstevel@tonic-gate 		}
22400Sstevel@tonic-gate 
22410Sstevel@tonic-gate 		tv.tv_sec = timeoutSec;
22420Sstevel@tonic-gate 		tv.tv_usec = 0;
22430Sstevel@tonic-gate 		rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
22440Sstevel@tonic-gate 
22450Sstevel@tonic-gate 		if ((rc == -1) || (rc == 0)) {
22460Sstevel@tonic-gate 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
22474522Schinlong 			    (void *)&errnum);
22480Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
22494522Schinlong 			    gettext("openConnection: simple bind failed "
22504522Schinlong 			    "- %s"), ldap_err2string(errnum));
22510Sstevel@tonic-gate 			(void) ldap_msgfree(resultMsg);
22520Sstevel@tonic-gate 			(void) ldap_unbind(ld);
22530Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
22544522Schinlong 			    NULL);
22550Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
22560Sstevel@tonic-gate 		}
22570Sstevel@tonic-gate 
22580Sstevel@tonic-gate 		/*
22590Sstevel@tonic-gate 		 * get ldaprc, controls, and error msg
22600Sstevel@tonic-gate 		 */
22610Sstevel@tonic-gate 		rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
22624522Schinlong 		    &errmsg, NULL, &controls, 1);
22630Sstevel@tonic-gate 
22640Sstevel@tonic-gate 		if (rc != LDAP_SUCCESS) {
22650Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
22664522Schinlong 			    gettext("openConnection: simple bind failed "
22674522Schinlong 			    "- unable to parse result"));
22680Sstevel@tonic-gate 			(void) ldap_unbind(ld);
22690Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
22704522Schinlong 			    strdup(errstr), NULL);
22710Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
22720Sstevel@tonic-gate 		}
22730Sstevel@tonic-gate 
22740Sstevel@tonic-gate 		/* process the password management info, if any */
22750Sstevel@tonic-gate 		pwd_rc = process_pwd_mgmt("simple",
22764522Schinlong 		    errnum, controls, errmsg,
22774522Schinlong 		    errorp,
22784522Schinlong 		    fail_if_new_pwd_reqd,
22794522Schinlong 		    passwd_mgmt);
22800Sstevel@tonic-gate 
22810Sstevel@tonic-gate 		if (pwd_rc == NS_LDAP_INTERNAL) {
22820Sstevel@tonic-gate 			(void) ldap_unbind(ld);
22830Sstevel@tonic-gate 			return (pwd_rc);
22840Sstevel@tonic-gate 		}
22850Sstevel@tonic-gate 
22860Sstevel@tonic-gate 		if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
22870Sstevel@tonic-gate 			*ldp = ld;
22880Sstevel@tonic-gate 			return (pwd_rc);
22890Sstevel@tonic-gate 		}
22900Sstevel@tonic-gate 
22910Sstevel@tonic-gate 		break;
22920Sstevel@tonic-gate 	case NS_LDAP_AUTH_SASL:
22932830Sdjl 		if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
22944522Schinlong 		    auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
22950Sstevel@tonic-gate 			(void) sprintf(errstr,
22964522Schinlong 			    gettext("openConnection: SASL options are "
22974522Schinlong 			    "not supported (%d) for non-GSSAPI sasl bind"),
22984522Schinlong 			    auth->auth.saslopt);
22990Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp,
23004522Schinlong 			    LDAP_AUTH_METHOD_NOT_SUPPORTED,
23014522Schinlong 			    strdup(errstr), NULL);
23020Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23030Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
23040Sstevel@tonic-gate 		}
23052830Sdjl 		if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
23062830Sdjl 			binddn = auth->cred.unix_cred.userID;
23072830Sdjl 			passwd = auth->cred.unix_cred.passwd;
23082830Sdjl 			if (passwd == NULL || *passwd == '\0' ||
23094522Schinlong 			    binddn == NULL || *binddn == '\0') {
23102830Sdjl 				(void) sprintf(errstr,
23114522Schinlong 				    gettext("openConnection: missing "
23124522Schinlong 				    "credentials for SASL bind"));
23132830Sdjl 				MKERROR(LOG_WARNING, *errorp,
23144522Schinlong 				    LDAP_INVALID_CREDENTIALS,
23154522Schinlong 				    strdup(errstr), NULL);
23162830Sdjl 				(void) ldap_unbind(ld);
23172830Sdjl 				return (NS_LDAP_INTERNAL);
23182830Sdjl 			}
23192830Sdjl 			cred.bv_val = passwd;
23202830Sdjl 			cred.bv_len = strlen(passwd);
23210Sstevel@tonic-gate 		}
23220Sstevel@tonic-gate 
23230Sstevel@tonic-gate 		switch (auth->auth.saslmech) {
23240Sstevel@tonic-gate 		case NS_LDAP_SASL_CRAM_MD5:
23250Sstevel@tonic-gate 			/*
23260Sstevel@tonic-gate 			 * NOTE: if iDS changes to support cram_md5,
23270Sstevel@tonic-gate 			 * please add password management code here.
23280Sstevel@tonic-gate 			 * Since ldap_sasl_cram_md5_bind_s does not
23290Sstevel@tonic-gate 			 * return anything that could be used to
23300Sstevel@tonic-gate 			 * extract the ldap rc/errmsg/control to
23310Sstevel@tonic-gate 			 * determine if bind failed due to password
23320Sstevel@tonic-gate 			 * policy, a new cram_md5_bind API will need
23330Sstevel@tonic-gate 			 * to be introduced. See
23340Sstevel@tonic-gate 			 * ldap_x_sasl_digest_md5_bind() and case
23350Sstevel@tonic-gate 			 * NS_LDAP_SASL_DIGEST_MD5 below for details.
23360Sstevel@tonic-gate 			 */
23370Sstevel@tonic-gate 			if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
23384522Schinlong 			    &cred, NULL, NULL)) != LDAP_SUCCESS) {
23390Sstevel@tonic-gate 				(void) ldap_get_option(ld,
23404522Schinlong 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
23410Sstevel@tonic-gate 				(void) snprintf(errstr, sizeof (errstr),
23424522Schinlong 				    gettext("openConnection: "
23434522Schinlong 				    "sasl/CRAM-MD5 bind failed - %s"),
23444522Schinlong 				    ldap_err2string(errnum));
23450Sstevel@tonic-gate 				MKERROR(LOG_WARNING, *errorp, errnum,
23464522Schinlong 				    strdup(errstr), NULL);
23470Sstevel@tonic-gate 				(void) ldap_unbind(ld);
23480Sstevel@tonic-gate 				return (NS_LDAP_INTERNAL);
23490Sstevel@tonic-gate 			}
23500Sstevel@tonic-gate 			break;
23510Sstevel@tonic-gate 		case NS_LDAP_SASL_DIGEST_MD5:
23520Sstevel@tonic-gate 			digest_md5_name = malloc(strlen(binddn) + 5);
23530Sstevel@tonic-gate 			/* 5 = strlen("dn: ") + 1 */
23540Sstevel@tonic-gate 			if (digest_md5_name == NULL) {
23550Sstevel@tonic-gate 				(void) ldap_unbind(ld);
23560Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
23570Sstevel@tonic-gate 			}
23580Sstevel@tonic-gate 			(void) strcpy(digest_md5_name, "dn: ");
23590Sstevel@tonic-gate 			(void) strcat(digest_md5_name, binddn);
23600Sstevel@tonic-gate 
23610Sstevel@tonic-gate 			tv.tv_sec = timeoutSec;
23620Sstevel@tonic-gate 			tv.tv_usec = 0;
23630Sstevel@tonic-gate 			rc = ldap_x_sasl_digest_md5_bind(ld,
23644522Schinlong 			    digest_md5_name, &cred, NULL, NULL,
23654522Schinlong 			    &tv, &resultMsg);
23660Sstevel@tonic-gate 
23670Sstevel@tonic-gate 			if (resultMsg == NULL) {
23680Sstevel@tonic-gate 				free(digest_md5_name);
23690Sstevel@tonic-gate 				(void) ldap_get_option(ld,
23704522Schinlong 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
23710Sstevel@tonic-gate 				(void) snprintf(errstr, sizeof (errstr),
23724522Schinlong 				    gettext("openConnection: "
23734522Schinlong 				    "DIGEST-MD5 bind failed - %s"),
23744522Schinlong 				    ldap_err2string(errnum));
23750Sstevel@tonic-gate 				(void) ldap_unbind(ld);
23760Sstevel@tonic-gate 				MKERROR(LOG_WARNING, *errorp, errnum,
23774522Schinlong 				    strdup(errstr), NULL);
23780Sstevel@tonic-gate 				return (NS_LDAP_INTERNAL);
23790Sstevel@tonic-gate 			}
23800Sstevel@tonic-gate 
23810Sstevel@tonic-gate 			/*
23820Sstevel@tonic-gate 			 * get ldaprc, controls, and error msg
23830Sstevel@tonic-gate 			 */
23840Sstevel@tonic-gate 			rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
23854522Schinlong 			    &errmsg, NULL, &controls, 1);
23860Sstevel@tonic-gate 
23870Sstevel@tonic-gate 			if (rc != LDAP_SUCCESS) {
23880Sstevel@tonic-gate 				free(digest_md5_name);
23890Sstevel@tonic-gate 				(void) snprintf(errstr, sizeof (errstr),
23904522Schinlong 				    gettext("openConnection: "
23914522Schinlong 				    "DIGEST-MD5 bind failed "
23924522Schinlong 				    "- unable to parse result"));
23930Sstevel@tonic-gate 				(void) ldap_unbind(ld);
23940Sstevel@tonic-gate 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
23954522Schinlong 				    strdup(errstr), NULL);
23960Sstevel@tonic-gate 				return (NS_LDAP_INTERNAL);
23970Sstevel@tonic-gate 			}
23980Sstevel@tonic-gate 
23990Sstevel@tonic-gate 			/* process the password management info, if any */
24000Sstevel@tonic-gate 			pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
24014522Schinlong 			    errnum, controls, errmsg,
24024522Schinlong 			    errorp,
24034522Schinlong 			    fail_if_new_pwd_reqd,
24044522Schinlong 			    passwd_mgmt);
24050Sstevel@tonic-gate 
24060Sstevel@tonic-gate 			if (pwd_rc == NS_LDAP_INTERNAL) {
24070Sstevel@tonic-gate 				free(digest_md5_name);
24080Sstevel@tonic-gate 				(void) ldap_unbind(ld);
24090Sstevel@tonic-gate 				return (pwd_rc);
24100Sstevel@tonic-gate 			}
24110Sstevel@tonic-gate 
24120Sstevel@tonic-gate 			if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
24130Sstevel@tonic-gate 				*ldp = ld;
24140Sstevel@tonic-gate 				return (pwd_rc);
24150Sstevel@tonic-gate 			}
24160Sstevel@tonic-gate 
24170Sstevel@tonic-gate 			free(digest_md5_name);
24180Sstevel@tonic-gate 			break;
24192830Sdjl 		case NS_LDAP_SASL_GSSAPI:
24202830Sdjl 			if (sasl_gssapi_inited == 0) {
24212830Sdjl 				rc = __s_api_sasl_gssapi_init();
24222830Sdjl 				if (rc != NS_LDAP_SUCCESS) {
24232830Sdjl 					(void) snprintf(errstr, sizeof (errstr),
24244522Schinlong 					    gettext("openConnection: "
24254522Schinlong 					    "GSSAPI initialization "
24264522Schinlong 					    "failed"));
24272830Sdjl 					(void) ldap_unbind(ld);
24282830Sdjl 					MKERROR(LOG_WARNING, *errorp, rc,
24294522Schinlong 					    strdup(errstr), NULL);
24302830Sdjl 					return (rc);
24312830Sdjl 				}
24322830Sdjl 			}
24332830Sdjl 			(void) memset(&sasl_param, 0,
24344522Schinlong 			    sizeof (ns_sasl_cb_param_t));
24352830Sdjl 			sasl_param.authid = NULL;
24362830Sdjl 			sasl_param.authzid = "";
24372830Sdjl 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
24384522Schinlong 			    (void *)&min_ssf);
24392830Sdjl 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
24404522Schinlong 			    (void *)&max_ssf);
24412830Sdjl 
24422830Sdjl 			rc = ldap_sasl_interactive_bind_s(
24434522Schinlong 			    ld, NULL, "GSSAPI",
24444522Schinlong 			    NULL, NULL, LDAP_SASL_INTERACTIVE,
24454522Schinlong 			    __s_api_sasl_bind_callback,
24464522Schinlong 			    &sasl_param);
24472830Sdjl 
24482830Sdjl 			if (rc != LDAP_SUCCESS) {
24492830Sdjl 				(void) snprintf(errstr, sizeof (errstr),
24504522Schinlong 				    gettext("openConnection: "
24514522Schinlong 				    "GSSAPI bind failed "
24524522Schinlong 				    "- %d %s"), rc, ldap_err2string(rc));
24532830Sdjl 				(void) ldap_unbind(ld);
24542830Sdjl 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
24554522Schinlong 				    strdup(errstr), NULL);
24562830Sdjl 				return (NS_LDAP_INTERNAL);
24572830Sdjl 			}
24582830Sdjl 
24592830Sdjl 			break;
24600Sstevel@tonic-gate 		default:
24610Sstevel@tonic-gate 			(void) ldap_unbind(ld);
24620Sstevel@tonic-gate 			(void) sprintf(errstr,
24634522Schinlong 			    gettext("openConnection: unsupported SASL "
24644522Schinlong 			    "mechanism (%d)"), auth->auth.saslmech);
24650Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp,
24664522Schinlong 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
24674522Schinlong 			    NULL);
24680Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
24690Sstevel@tonic-gate 		}
24700Sstevel@tonic-gate 	}
24710Sstevel@tonic-gate 
24720Sstevel@tonic-gate 	*ldp = ld;
24730Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
24740Sstevel@tonic-gate }
24750Sstevel@tonic-gate 
24760Sstevel@tonic-gate /*
24770Sstevel@tonic-gate  * FUNCTION:	__s_api_getDefaultAuth
24780Sstevel@tonic-gate  *
24790Sstevel@tonic-gate  *	Constructs a credential for authentication using the config module.
24800Sstevel@tonic-gate  *
24810Sstevel@tonic-gate  * RETURN VALUES:
24820Sstevel@tonic-gate  *
24830Sstevel@tonic-gate  * NS_LDAP_SUCCESS	If successful
24840Sstevel@tonic-gate  * NS_LDAP_CONFIG	If there are any config errors.
24850Sstevel@tonic-gate  * NS_LDAP_MEMORY	Memory errors.
24860Sstevel@tonic-gate  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
24870Sstevel@tonic-gate  *			not build a new authp.
24880Sstevel@tonic-gate  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
24890Sstevel@tonic-gate  *			necessary fields of a cred for a given auth method
24900Sstevel@tonic-gate  *			are not provided.
24910Sstevel@tonic-gate  * INPUT:
24920Sstevel@tonic-gate  *
24930Sstevel@tonic-gate  * cLevel	Currently requested credential level to be tried
24940Sstevel@tonic-gate  *
24950Sstevel@tonic-gate  * aMethod	Currently requested authentication method to be tried
24960Sstevel@tonic-gate  *
24970Sstevel@tonic-gate  * OUTPUT:
24980Sstevel@tonic-gate  *
24990Sstevel@tonic-gate  * authp		authentication method to use.
25000Sstevel@tonic-gate  */
25010Sstevel@tonic-gate static int
25020Sstevel@tonic-gate __s_api_getDefaultAuth(
25030Sstevel@tonic-gate 	int	*cLevel,
25040Sstevel@tonic-gate 	ns_auth_t *aMethod,
25050Sstevel@tonic-gate 	ns_cred_t **authp)
25060Sstevel@tonic-gate {
25070Sstevel@tonic-gate 	void		**paramVal = NULL;
25080Sstevel@tonic-gate 	char		*modparamVal = NULL;
25090Sstevel@tonic-gate 	int		getUid = 0;
25100Sstevel@tonic-gate 	int		getPasswd = 0;
25110Sstevel@tonic-gate 	int		getCertpath = 0;
25120Sstevel@tonic-gate 	int		rc = 0;
25130Sstevel@tonic-gate 	ns_ldap_error_t	*errorp = NULL;
25140Sstevel@tonic-gate 
25150Sstevel@tonic-gate #ifdef DEBUG
25160Sstevel@tonic-gate 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
25170Sstevel@tonic-gate #endif
25180Sstevel@tonic-gate 
25190Sstevel@tonic-gate 	if (aMethod == NULL) {
25200Sstevel@tonic-gate 		/* Require an Auth */
25210Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
25220Sstevel@tonic-gate 
25230Sstevel@tonic-gate 	}
25240Sstevel@tonic-gate 	/*
25252830Sdjl 	 * credential level "self" can work with auth method sasl/GSSAPI only
25260Sstevel@tonic-gate 	 */
25272830Sdjl 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
25284522Schinlong 	    aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
25290Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
25300Sstevel@tonic-gate 
25310Sstevel@tonic-gate 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
25320Sstevel@tonic-gate 	if ((*authp) == NULL)
25330Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
25340Sstevel@tonic-gate 
25350Sstevel@tonic-gate 	(*authp)->auth = *aMethod;
25360Sstevel@tonic-gate 
25370Sstevel@tonic-gate 	switch (aMethod->type) {
25380Sstevel@tonic-gate 		case NS_LDAP_AUTH_NONE:
25390Sstevel@tonic-gate 			return (NS_LDAP_SUCCESS);
25400Sstevel@tonic-gate 		case NS_LDAP_AUTH_SIMPLE:
25410Sstevel@tonic-gate 			getUid++;
25420Sstevel@tonic-gate 			getPasswd++;
25430Sstevel@tonic-gate 			break;
25440Sstevel@tonic-gate 		case NS_LDAP_AUTH_SASL:
25450Sstevel@tonic-gate 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
25460Sstevel@tonic-gate 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
25470Sstevel@tonic-gate 				getUid++;
25480Sstevel@tonic-gate 				getPasswd++;
25492830Sdjl 			} else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
25500Sstevel@tonic-gate 				(void) __ns_ldap_freeCred(authp);
25510Sstevel@tonic-gate 				*authp = NULL;
25520Sstevel@tonic-gate 				return (NS_LDAP_INVALID_PARAM);
25530Sstevel@tonic-gate 			}
25540Sstevel@tonic-gate 			break;
25550Sstevel@tonic-gate 		case NS_LDAP_AUTH_TLS:
25560Sstevel@tonic-gate 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
25570Sstevel@tonic-gate 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
25580Sstevel@tonic-gate 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
25590Sstevel@tonic-gate 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
25600Sstevel@tonic-gate 				getUid++;
25610Sstevel@tonic-gate 				getPasswd++;
25620Sstevel@tonic-gate 				getCertpath++;
25630Sstevel@tonic-gate 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
25640Sstevel@tonic-gate 				getCertpath++;
25650Sstevel@tonic-gate 			} else {
25660Sstevel@tonic-gate 				(void) __ns_ldap_freeCred(authp);
25670Sstevel@tonic-gate 				*authp = NULL;
25680Sstevel@tonic-gate 				return (NS_LDAP_INVALID_PARAM);
25690Sstevel@tonic-gate 			}
25700Sstevel@tonic-gate 			break;
25710Sstevel@tonic-gate 	}
25720Sstevel@tonic-gate 
25730Sstevel@tonic-gate 	if (getUid) {
25740Sstevel@tonic-gate 		paramVal = NULL;
25750Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
25764522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
25770Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
25780Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
25790Sstevel@tonic-gate 			*authp = NULL;
25800Sstevel@tonic-gate 			return (rc);
25810Sstevel@tonic-gate 		}
25820Sstevel@tonic-gate 
25830Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
25840Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
25850Sstevel@tonic-gate 			*authp = NULL;
25860Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
25870Sstevel@tonic-gate 		}
25880Sstevel@tonic-gate 
25890Sstevel@tonic-gate 		(*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
25900Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
25910Sstevel@tonic-gate 		if ((*authp)->cred.unix_cred.userID == NULL) {
25920Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
25930Sstevel@tonic-gate 			*authp = NULL;
25940Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
25950Sstevel@tonic-gate 		}
25960Sstevel@tonic-gate 	}
25970Sstevel@tonic-gate 	if (getPasswd) {
25980Sstevel@tonic-gate 		paramVal = NULL;
25990Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
26004522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
26010Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26020Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
26030Sstevel@tonic-gate 			*authp = NULL;
26040Sstevel@tonic-gate 			return (rc);
26050Sstevel@tonic-gate 		}
26060Sstevel@tonic-gate 
26070Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
26080Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26090Sstevel@tonic-gate 			*authp = NULL;
26100Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
26110Sstevel@tonic-gate 		}
26120Sstevel@tonic-gate 
26130Sstevel@tonic-gate 		modparamVal = dvalue((char *)*paramVal);
26140Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
26150Sstevel@tonic-gate 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
26160Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26170Sstevel@tonic-gate 			if (modparamVal != NULL)
26180Sstevel@tonic-gate 				free(modparamVal);
26190Sstevel@tonic-gate 			*authp = NULL;
26200Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
26210Sstevel@tonic-gate 		}
26220Sstevel@tonic-gate 
26230Sstevel@tonic-gate 		(*authp)->cred.unix_cred.passwd = modparamVal;
26240Sstevel@tonic-gate 	}
26250Sstevel@tonic-gate 	if (getCertpath) {
26260Sstevel@tonic-gate 		paramVal = NULL;
26270Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
26284522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
26290Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26300Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
26310Sstevel@tonic-gate 			*authp = NULL;
26320Sstevel@tonic-gate 			return (rc);
26330Sstevel@tonic-gate 		}
26340Sstevel@tonic-gate 
26350Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
26360Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26370Sstevel@tonic-gate 			*authp = NULL;
26380Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
26390Sstevel@tonic-gate 		}
26400Sstevel@tonic-gate 
26410Sstevel@tonic-gate 		(*authp)->hostcertpath = strdup((char *)*paramVal);
26420Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
26430Sstevel@tonic-gate 		if ((*authp)->hostcertpath == NULL) {
26440Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26450Sstevel@tonic-gate 			*authp = NULL;
26460Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
26470Sstevel@tonic-gate 		}
26480Sstevel@tonic-gate 	}
26490Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
26500Sstevel@tonic-gate }
26510Sstevel@tonic-gate 
26520Sstevel@tonic-gate /*
26530Sstevel@tonic-gate  * FUNCTION:	__s_api_getConnection
26540Sstevel@tonic-gate  *
26550Sstevel@tonic-gate  *	Bind to the specified server or one from the server
26560Sstevel@tonic-gate  *	list and return the pointer.
26570Sstevel@tonic-gate  *
26580Sstevel@tonic-gate  *	This function can rebind or not (NS_LDAP_HARD), it can require a
26590Sstevel@tonic-gate  *	credential or bind anonymously
26600Sstevel@tonic-gate  *
26610Sstevel@tonic-gate  *	This function follows the DUA configuration schema algorithm
26620Sstevel@tonic-gate  *
26630Sstevel@tonic-gate  * RETURN VALUES:
26640Sstevel@tonic-gate  *
26650Sstevel@tonic-gate  * NS_LDAP_SUCCESS	A connection was made successfully.
26660Sstevel@tonic-gate  * NS_LDAP_SUCCESS_WITH_INFO
26670Sstevel@tonic-gate  * 			A connection was made successfully, but with
26680Sstevel@tonic-gate  *			password management info in *errorp
26690Sstevel@tonic-gate  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
26700Sstevel@tonic-gate  * NS_LDAP_CONFIG	If there are any config errors.
26710Sstevel@tonic-gate  * NS_LDAP_MEMORY	Memory errors.
26720Sstevel@tonic-gate  * NS_LDAP_INTERNAL	If there was a ldap error.
26730Sstevel@tonic-gate  *
26740Sstevel@tonic-gate  * INPUT:
26750Sstevel@tonic-gate  *
26760Sstevel@tonic-gate  * server	Bind to this LDAP server only
26770Sstevel@tonic-gate  * flags	If NS_LDAP_HARD is set function will not return until it has
26780Sstevel@tonic-gate  *		a connection unless there is a authentication problem.
26790Sstevel@tonic-gate  *		If NS_LDAP_NEW_CONN is set the function must force a new
26800Sstevel@tonic-gate  *              connection to be created
26810Sstevel@tonic-gate  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
26820Sstevel@tonic-gate  * auth		Credentials for bind. This could be NULL in which case
26830Sstevel@tonic-gate  *		a default cred built from the config module is used.
26840Sstevel@tonic-gate  * sessionId	cookie that points to a previous session
26850Sstevel@tonic-gate  * fail_if_new_pwd_reqd
26860Sstevel@tonic-gate  *		a flag indicating this function should fail if the passwd
26870Sstevel@tonic-gate  *		in auth needs to change immediately
26881179Svv149972  * nopasswd_acct_mgmt
26891179Svv149972  *		a flag indicating that makeConnection should check before
26901179Svv149972  *		binding if server supports LDAP V3 password less
26911179Svv149972  *		account management
26920Sstevel@tonic-gate  *
26930Sstevel@tonic-gate  * OUTPUT:
26940Sstevel@tonic-gate  *
26950Sstevel@tonic-gate  * session	pointer to a session with connection information
26960Sstevel@tonic-gate  * errorp	Set if there are any INTERNAL, or CONFIG error.
26970Sstevel@tonic-gate  */
26980Sstevel@tonic-gate int
26990Sstevel@tonic-gate __s_api_getConnection(
27000Sstevel@tonic-gate 	const char *server,
27010Sstevel@tonic-gate 	const int flags,
27020Sstevel@tonic-gate 	const ns_cred_t *cred,		/* credentials for bind */
27030Sstevel@tonic-gate 	ConnectionID *sessionId,
27040Sstevel@tonic-gate 	Connection **session,
27050Sstevel@tonic-gate 	ns_ldap_error_t **errorp,
27061179Svv149972 	int fail_if_new_pwd_reqd,
27071179Svv149972 	int nopasswd_acct_mgmt)
27080Sstevel@tonic-gate {
27090Sstevel@tonic-gate 	char		errmsg[MAXERROR];
27100Sstevel@tonic-gate 	ns_auth_t	**aMethod = NULL;
27110Sstevel@tonic-gate 	ns_auth_t	**aNext = NULL;
27120Sstevel@tonic-gate 	int		**cLevel = NULL;
27130Sstevel@tonic-gate 	int		**cNext = NULL;
27140Sstevel@tonic-gate 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
27150Sstevel@tonic-gate 	int		rc;
27160Sstevel@tonic-gate 	Connection	*con = NULL;
27170Sstevel@tonic-gate 	int		sec = 1;
27180Sstevel@tonic-gate 	ns_cred_t 	*authp = NULL;
27190Sstevel@tonic-gate 	ns_cred_t	anon;
27202830Sdjl 	int		version = NS_LDAP_V2, self_gssapi_only = 0;
27210Sstevel@tonic-gate 	void		**paramVal = NULL;
27221687Sjanga 	char		**badSrvrs = NULL; /* List of problem hostnames */
27230Sstevel@tonic-gate 
27240Sstevel@tonic-gate 	if ((session == NULL) || (sessionId == NULL)) {
27250Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
27260Sstevel@tonic-gate 	}
27270Sstevel@tonic-gate 	*session = NULL;
27280Sstevel@tonic-gate 
27290Sstevel@tonic-gate 	/* if we already have a session id try to reuse connection */
27300Sstevel@tonic-gate 	if (*sessionId > 0) {
27310Sstevel@tonic-gate 		rc = findConnectionById(flags, cred, *sessionId, &con);
27320Sstevel@tonic-gate 		if (rc == *sessionId && con) {
27330Sstevel@tonic-gate 			*session = con;
27340Sstevel@tonic-gate 			return (NS_LDAP_SUCCESS);
27350Sstevel@tonic-gate 		}
27360Sstevel@tonic-gate 		*sessionId = 0;
27370Sstevel@tonic-gate 	}
27380Sstevel@tonic-gate 
27390Sstevel@tonic-gate 	/* get profile version number */
27400Sstevel@tonic-gate 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
27414522Schinlong 	    &paramVal, errorp)) != NS_LDAP_SUCCESS)
27420Sstevel@tonic-gate 		return (rc);
27430Sstevel@tonic-gate 	if (paramVal == NULL) {
27440Sstevel@tonic-gate 		(void) sprintf(errmsg, gettext("getConnection: no file "
27454522Schinlong 		    "version"));
27460Sstevel@tonic-gate 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
27474522Schinlong 		    NS_LDAP_CONFIG);
27480Sstevel@tonic-gate 		return (NS_LDAP_CONFIG);
27490Sstevel@tonic-gate 	}
27500Sstevel@tonic-gate 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
27510Sstevel@tonic-gate 		version = NS_LDAP_V1;
27520Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&paramVal);
27530Sstevel@tonic-gate 
27540Sstevel@tonic-gate 	/* Get the bind timeout value */
27550Sstevel@tonic-gate 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
27560Sstevel@tonic-gate 	if (paramVal != NULL && *paramVal != NULL) {
27570Sstevel@tonic-gate 		timeoutSec = **((int **)paramVal);
27580Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
27590Sstevel@tonic-gate 	}
27600Sstevel@tonic-gate 	if (*errorp)
27610Sstevel@tonic-gate 		(void) __ns_ldap_freeError(errorp);
27620Sstevel@tonic-gate 
27630Sstevel@tonic-gate 	if (cred == NULL) {
27640Sstevel@tonic-gate 		/* Get the authentication method list */
27650Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
27664522Schinlong 		    (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
27670Sstevel@tonic-gate 			return (rc);
27680Sstevel@tonic-gate 		if (aMethod == NULL) {
27690Sstevel@tonic-gate 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
27700Sstevel@tonic-gate 			if (aMethod == NULL)
27710Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
27720Sstevel@tonic-gate 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
27730Sstevel@tonic-gate 			if (aMethod[0] == NULL) {
27740Sstevel@tonic-gate 				free(aMethod);
27750Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
27760Sstevel@tonic-gate 			}
27770Sstevel@tonic-gate 			if (version == NS_LDAP_V1)
27780Sstevel@tonic-gate 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
27790Sstevel@tonic-gate 			else {
27800Sstevel@tonic-gate 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
27810Sstevel@tonic-gate 				(aMethod[0])->saslmech =
27824522Schinlong 				    NS_LDAP_SASL_DIGEST_MD5;
27830Sstevel@tonic-gate 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
27840Sstevel@tonic-gate 			}
27850Sstevel@tonic-gate 		}
27860Sstevel@tonic-gate 
27870Sstevel@tonic-gate 		/* Get the credential level list */
27880Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
27894522Schinlong 		    (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
27900Sstevel@tonic-gate 			(void) __ns_ldap_freeParam((void ***)&aMethod);
27910Sstevel@tonic-gate 			return (rc);
27920Sstevel@tonic-gate 		}
27930Sstevel@tonic-gate 		if (cLevel == NULL) {
27940Sstevel@tonic-gate 			cLevel = (int **)calloc(2, sizeof (int *));
27950Sstevel@tonic-gate 			if (cLevel == NULL)
27960Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
27970Sstevel@tonic-gate 			cLevel[0] = (int *)calloc(1, sizeof (int));
27980Sstevel@tonic-gate 			if (cLevel[0] == NULL)
27990Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
28000Sstevel@tonic-gate 			if (version == NS_LDAP_V1)
28010Sstevel@tonic-gate 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
28020Sstevel@tonic-gate 			else
28030Sstevel@tonic-gate 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
28040Sstevel@tonic-gate 		}
28050Sstevel@tonic-gate 	}
28060Sstevel@tonic-gate 
28070Sstevel@tonic-gate 	/* setup the anon credential for anonymous connection */
28080Sstevel@tonic-gate 	(void) memset(&anon, 0, sizeof (ns_cred_t));
28090Sstevel@tonic-gate 	anon.auth.type = NS_LDAP_AUTH_NONE;
28100Sstevel@tonic-gate 
28110Sstevel@tonic-gate 	for (; ; ) {
28120Sstevel@tonic-gate 		if (cred != NULL) {
28130Sstevel@tonic-gate 			/* using specified auth method */
28140Sstevel@tonic-gate 			rc = makeConnection(&con, server, cred,
28154522Schinlong 			    sessionId, timeoutSec, errorp,
28164522Schinlong 			    fail_if_new_pwd_reqd,
28174522Schinlong 			    nopasswd_acct_mgmt, flags, &badSrvrs);
28184387Smj162486 			/* not using bad server if credentials were supplied */
28194387Smj162486 			if (badSrvrs && *badSrvrs) {
28204387Smj162486 				__s_api_free2dArray(badSrvrs);
28214387Smj162486 				badSrvrs = NULL;
28224387Smj162486 			}
28230Sstevel@tonic-gate 			if (rc == NS_LDAP_SUCCESS ||
28244522Schinlong 			    rc == NS_LDAP_SUCCESS_WITH_INFO) {
28250Sstevel@tonic-gate 				*session = con;
28260Sstevel@tonic-gate 				break;
28270Sstevel@tonic-gate 			}
28280Sstevel@tonic-gate 		} else {
28292830Sdjl 			self_gssapi_only = __s_api_self_gssapi_only_get();
28300Sstevel@tonic-gate 			/* for every cred level */
28310Sstevel@tonic-gate 			for (cNext = cLevel; *cNext != NULL; cNext++) {
28322830Sdjl 				if (self_gssapi_only &&
28334522Schinlong 				    **cNext != NS_LDAP_CRED_SELF)
28342830Sdjl 					continue;
28350Sstevel@tonic-gate 				if (**cNext == NS_LDAP_CRED_ANON) {
28361687Sjanga 					/*
28371687Sjanga 					 * make connection anonymously
28381687Sjanga 					 * Free the down server list before
28391687Sjanga 					 * looping through
28401687Sjanga 					 */
28411687Sjanga 					if (badSrvrs && *badSrvrs) {
28421687Sjanga 						__s_api_free2dArray(badSrvrs);
28431687Sjanga 						badSrvrs = NULL;
28441687Sjanga 					}
28450Sstevel@tonic-gate 					rc = makeConnection(&con, server, &anon,
28464522Schinlong 					    sessionId, timeoutSec, errorp,
28474522Schinlong 					    fail_if_new_pwd_reqd,
28484522Schinlong 					    nopasswd_acct_mgmt, flags,
28494522Schinlong 					    &badSrvrs);
28500Sstevel@tonic-gate 					if (rc == NS_LDAP_SUCCESS ||
28514522Schinlong 					    rc ==
28524522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO) {
28530Sstevel@tonic-gate 						*session = con;
28540Sstevel@tonic-gate 						goto done;
28550Sstevel@tonic-gate 					}
28560Sstevel@tonic-gate 					continue;
28570Sstevel@tonic-gate 				}
28580Sstevel@tonic-gate 				/* for each cred level */
28590Sstevel@tonic-gate 				for (aNext = aMethod; *aNext != NULL; aNext++) {
28602830Sdjl 					if (self_gssapi_only &&
28614522Schinlong 					    (*aNext)->saslmech !=
28624522Schinlong 					    NS_LDAP_SASL_GSSAPI)
28632830Sdjl 						continue;
28642830Sdjl 					/*
28652830Sdjl 					 * self coexists with sasl/GSSAPI only
28662830Sdjl 					 * and non-self coexists with non-gssapi
28672830Sdjl 					 * only
28682830Sdjl 					 */
28692830Sdjl 					if ((**cNext == NS_LDAP_CRED_SELF &&
28704522Schinlong 					    (*aNext)->saslmech !=
28714522Schinlong 					    NS_LDAP_SASL_GSSAPI) ||
28724522Schinlong 					    (**cNext != NS_LDAP_CRED_SELF &&
28734522Schinlong 					    (*aNext)->saslmech ==
28744522Schinlong 					    NS_LDAP_SASL_GSSAPI))
28752830Sdjl 						continue;
28760Sstevel@tonic-gate 					/* make connection and authenticate */
28770Sstevel@tonic-gate 					/* with default credentials */
28780Sstevel@tonic-gate 					authp = NULL;
28790Sstevel@tonic-gate 					rc = __s_api_getDefaultAuth(*cNext,
28804522Schinlong 					    *aNext, &authp);
28810Sstevel@tonic-gate 					if (rc != NS_LDAP_SUCCESS) {
28820Sstevel@tonic-gate 						continue;
28830Sstevel@tonic-gate 					}
28841687Sjanga 					/*
28851687Sjanga 					 * Free the down server list before
28861687Sjanga 					 * looping through
28871687Sjanga 					 */
28881687Sjanga 					if (badSrvrs && *badSrvrs) {
28891687Sjanga 						__s_api_free2dArray(badSrvrs);
28901687Sjanga 						badSrvrs = NULL;
28911687Sjanga 					}
28920Sstevel@tonic-gate 					rc = makeConnection(&con, server, authp,
28934522Schinlong 					    sessionId, timeoutSec, errorp,
28944522Schinlong 					    fail_if_new_pwd_reqd,
28954522Schinlong 					    nopasswd_acct_mgmt, flags,
28964522Schinlong 					    &badSrvrs);
28970Sstevel@tonic-gate 					(void) __ns_ldap_freeCred(&authp);
28980Sstevel@tonic-gate 					if (rc == NS_LDAP_SUCCESS ||
28994522Schinlong 					    rc ==
29004522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO) {
29010Sstevel@tonic-gate 						*session = con;
29020Sstevel@tonic-gate 						goto done;
29030Sstevel@tonic-gate 					}
29040Sstevel@tonic-gate 				}
29050Sstevel@tonic-gate 			}
29060Sstevel@tonic-gate 		}
29070Sstevel@tonic-gate 		if (flags & NS_LDAP_HARD) {
29080Sstevel@tonic-gate 			if (sec < LDAPMAXHARDLOOKUPTIME)
29090Sstevel@tonic-gate 				sec *= 2;
29100Sstevel@tonic-gate 			_sleep(sec);
29110Sstevel@tonic-gate 		} else {
29120Sstevel@tonic-gate 			break;
29130Sstevel@tonic-gate 		}
29140Sstevel@tonic-gate 	}
29150Sstevel@tonic-gate 
29160Sstevel@tonic-gate done:
29172830Sdjl 	/*
29182830Sdjl 	 * If unable to get a connection, and this is
29192830Sdjl 	 * the thread opening the shared connection,
29202830Sdjl 	 * unlock the session mutex and let other
29212830Sdjl 	 * threads try to get their own connection.
29222830Sdjl 	 */
29232830Sdjl 	if (wait4session != 0 && sessionTid == thr_self()) {
29242830Sdjl 		wait4session = 0;
29252830Sdjl 		sessionTid = 0;
29262830Sdjl #ifdef DEBUG
29272830Sdjl 		(void) fprintf(stderr, "tid= %d: __s_api_getConnection: "
29284522Schinlong 		    "unlocking sessionLock \n", thr_self());
29292830Sdjl 		fflush(stderr);
29302830Sdjl #endif /* DEBUG */
29312830Sdjl 		(void) mutex_unlock(&sessionLock);
29322830Sdjl 	}
29332830Sdjl 	if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
29342830Sdjl 		/*
29352830Sdjl 		 * self_gssapi_only is true but no self/sasl/gssapi is
29362830Sdjl 		 * configured
29372830Sdjl 		 */
29382830Sdjl 		rc = NS_LDAP_CONFIG;
29392830Sdjl 	}
29402830Sdjl 
29410Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&aMethod);
29420Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&cLevel);
29431687Sjanga 
29441687Sjanga 	if (badSrvrs && *badSrvrs) {
29451687Sjanga 		/*
29461687Sjanga 		 * At this point, either we have a successful
29471687Sjanga 		 * connection or exhausted all the possible auths.
29481687Sjanga 		 * and creds. Mark the problem servers as down
29491687Sjanga 		 * so that the problem servers are not contacted
29501687Sjanga 		 * again until the refresh_ttl expires.
29511687Sjanga 		 */
29521687Sjanga 		(void) __s_api_removeBadServers(badSrvrs);
29531687Sjanga 		__s_api_free2dArray(badSrvrs);
29541687Sjanga 	}
29550Sstevel@tonic-gate 	return (rc);
29560Sstevel@tonic-gate }
29570Sstevel@tonic-gate 
29580Sstevel@tonic-gate #pragma fini(_free_sessionPool)
29590Sstevel@tonic-gate static void
29600Sstevel@tonic-gate _free_sessionPool()
29610Sstevel@tonic-gate {
29620Sstevel@tonic-gate 	int id;
29630Sstevel@tonic-gate 
29642830Sdjl 	(void) rw_wrlock(&sessionPoolLock);
29650Sstevel@tonic-gate 	if (sessionPool != NULL) {
29660Sstevel@tonic-gate 		for (id = 0; id < sessionPoolSize; id++)
29670Sstevel@tonic-gate 			_DropConnection(id + CONID_OFFSET, 0, 1);
29680Sstevel@tonic-gate 		free(sessionPool);
29690Sstevel@tonic-gate 		sessionPool = NULL;
29700Sstevel@tonic-gate 		sessionPoolSize = 0;
29710Sstevel@tonic-gate 	}
29722830Sdjl 	(void) rw_unlock(&sessionPoolLock);
29730Sstevel@tonic-gate }
2974