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 /*
225840Smj162486  * 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 
806072Smj162486 static int check_nscd_proc(pid_t pid, boolean_t check_uid);
810Sstevel@tonic-gate 
822830Sdjl /*
832830Sdjl  * SSF values are for SASL integrity & privacy.
842830Sdjl  * JES DS5.2 does not support this feature but DS6 does.
852830Sdjl  * The values between 0 and 65535 can work with both server versions.
862830Sdjl  */
872830Sdjl #define	MAX_SASL_SSF	65535
882830Sdjl #define	MIN_SASL_SSF	0
890Sstevel@tonic-gate 
901687Sjanga /* Number of hostnames to allocate memory for */
911687Sjanga #define	NUMTOMALLOC	32
922830Sdjl /*
932830Sdjl  * ns_mtckey is for sharing a ldap connection among multiple
942830Sdjl  * threads; created by ns_ldap_init() in ns_init.c
952830Sdjl  */
962830Sdjl extern thread_key_t ns_mtckey;
971687Sjanga 
982830Sdjl /* Per thread LDAP error resides in thread-specific data. */
992830Sdjl struct ldap_error {
1002830Sdjl 	int	le_errno;
1012830Sdjl 	char	*le_matched;
1022830Sdjl 	char	*le_errmsg;
1032830Sdjl };
1042830Sdjl 
1053428Smichen static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
1063428Smichen 
1072830Sdjl /* destructor */
1082830Sdjl void
1092830Sdjl ns_tsd_cleanup(void *key) {
1102830Sdjl 	struct ldap_error *le = (struct ldap_error *)key;
1112830Sdjl 
1122830Sdjl 	if (le == NULL)
1132830Sdjl 		return;
1142830Sdjl 	if (le->le_matched != NULL) {
1152830Sdjl 		ldap_memfree(le->le_matched);
1162830Sdjl 	}
1172830Sdjl 	if (le->le_errmsg != NULL) {
1182830Sdjl 		ldap_memfree(le->le_errmsg);
1192830Sdjl 	}
1202830Sdjl 	free(le);
1212830Sdjl }
1222830Sdjl 
1232830Sdjl /* Callback function for allocating a mutex */
1242830Sdjl static void *
1252830Sdjl ns_mutex_alloc(void)
1262830Sdjl {
1272830Sdjl 	mutex_t *mutexp = NULL;
1282830Sdjl 
1292830Sdjl 	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
1302830Sdjl 		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
1312830Sdjl 			free(mutexp);
1322830Sdjl 			mutexp = NULL;
1332830Sdjl 		}
1342830Sdjl 	}
1352830Sdjl 	return (mutexp);
1362830Sdjl }
1372830Sdjl 
1382830Sdjl /* Callback function for freeing a mutex */
1392830Sdjl static void
1402830Sdjl ns_mutex_free(void *mutexp)
1412830Sdjl {
1422830Sdjl 	(void) mutex_destroy((mutex_t *)mutexp);
1432830Sdjl 	free(mutexp);
1442830Sdjl }
1452830Sdjl 
1462830Sdjl /*
1472830Sdjl  * Function for setting up thread-specific data
1482830Sdjl  * where per thread LDAP error is stored
1492830Sdjl  */
1502830Sdjl static int
1512830Sdjl tsd_setup()
1522830Sdjl {
1532830Sdjl 	void	*tsd;
1542830Sdjl 	int	rc;
1552830Sdjl 
1562830Sdjl 	/* return success if TSD already set */
1572830Sdjl 	rc = thr_getspecific(ns_mtckey, &tsd);
1582830Sdjl 	if (rc == 0 && tsd != NULL)
1592830Sdjl 		return (0);
1602830Sdjl 
1612830Sdjl 	/* allocate and set TSD */
1622830Sdjl 	tsd = (void *) calloc(1, sizeof (struct ldap_error));
1632830Sdjl 	if (tsd == NULL)
1642830Sdjl 		return (-1);
1652830Sdjl 	rc = thr_setspecific(ns_mtckey, tsd);
1662830Sdjl 	if (rc != 0) { /* must be ENOMEM */
1672830Sdjl 		free(tsd);
1682830Sdjl 		return (-1);
1692830Sdjl 	}
1702830Sdjl 	return (0);
1712830Sdjl 
1722830Sdjl 
1732830Sdjl }
1742830Sdjl 
1752830Sdjl /* Callback function for setting the per thread LDAP error */
1762830Sdjl /*ARGSUSED*/
1772830Sdjl static void
1782830Sdjl set_ld_error(int err, char *matched, char *errmsg, void *dummy)
1792830Sdjl {
1802830Sdjl 	struct ldap_error	*le;
1812830Sdjl 
1822830Sdjl 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
1832830Sdjl 		syslog(LOG_ERR, "set_ld_error: thr_getspecific failed. errno"
1844522Schinlong 		    " %d", errno);
1852830Sdjl 		return;
1862830Sdjl 	}
1873428Smichen 
1883428Smichen 	/* play safe, do nothing if TSD pointer is NULL */
1893428Smichen 	if (le == NULL)
1903428Smichen 		return;
1913428Smichen 
1922830Sdjl 	le->le_errno = err;
1932830Sdjl 	if (le->le_matched != NULL) {
1942830Sdjl 		ldap_memfree(le->le_matched);
1952830Sdjl 	}
1962830Sdjl 	le->le_matched = matched;
1972830Sdjl 	if (le->le_errmsg != NULL) {
1982830Sdjl 		ldap_memfree(le->le_errmsg);
1992830Sdjl 	}
2002830Sdjl 	le->le_errmsg = errmsg;
2012830Sdjl }
2022830Sdjl 
2033387Schinlong int
2043387Schinlong /* check and allocate the thread-specific data for using a shared connection */
2053387Schinlong __s_api_check_MTC_tsd()
2063387Schinlong {
2073387Schinlong 	if (tsd_setup() != 0)
2083387Schinlong 		return (NS_LDAP_MEMORY);
2093387Schinlong 
2103387Schinlong 	return (NS_LDAP_SUCCESS);
2113387Schinlong }
2123387Schinlong 
2132830Sdjl /* Callback function for getting the per thread LDAP error */
2142830Sdjl /*ARGSUSED*/
2152830Sdjl static int
2162830Sdjl get_ld_error(char **matched, char **errmsg, void *dummy)
2172830Sdjl {
2182830Sdjl 	struct ldap_error	*le;
2192830Sdjl 
2202830Sdjl 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
2212830Sdjl 		syslog(LOG_ERR, "get_ld_error: thr_getspecific failed. errno"
2224522Schinlong 		    " %d", errno);
2232830Sdjl 		return (errno);
2242830Sdjl 	}
2253428Smichen 
2263428Smichen 	/* play safe, return NULL error data, if TSD pointer is NULL */
2273428Smichen 	if (le == NULL)
2283428Smichen 		le = &ldap_error_NULL;
2293428Smichen 
2302830Sdjl 	if (matched != NULL) {
2312830Sdjl 		*matched = le->le_matched;
2322830Sdjl 	}
2332830Sdjl 	if (errmsg != NULL) {
2342830Sdjl 		*errmsg = le->le_errmsg;
2352830Sdjl 	}
2362830Sdjl 	return (le->le_errno);
2372830Sdjl }
2382830Sdjl 
2392830Sdjl /* Callback function for setting per thread errno */
2402830Sdjl static void
2412830Sdjl set_errno(int err)
2422830Sdjl {
2432830Sdjl 	errno = err;
2442830Sdjl }
2452830Sdjl 
2462830Sdjl /* Callback function for getting per thread errno */
2472830Sdjl static int
2482830Sdjl get_errno(void)
2492830Sdjl {
2502830Sdjl 	return (errno);
2512830Sdjl }
2522830Sdjl 
2532830Sdjl /*
2542830Sdjl  * set up to allow multiple threads to use the same ldap connection
2552830Sdjl  */
2562830Sdjl static int
2572830Sdjl setup_mt_conn(LDAP *ld)
2582830Sdjl {
2592830Sdjl 
2602830Sdjl 	struct ldap_thread_fns		tfns;
2612830Sdjl 	struct ldap_extra_thread_fns	extrafns;
2622830Sdjl 	int				rc;
2632830Sdjl 
2642830Sdjl 	/*
2652830Sdjl 	 * Set the function pointers for dealing with mutexes
2662830Sdjl 	 * and error information
2672830Sdjl 	 */
2682830Sdjl 	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
2692830Sdjl 	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
2702830Sdjl 	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
2712830Sdjl 	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
2722830Sdjl 	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
2732830Sdjl 	tfns.ltf_get_errno = get_errno;
2742830Sdjl 	tfns.ltf_set_errno = set_errno;
2752830Sdjl 	tfns.ltf_get_lderrno = get_ld_error;
2762830Sdjl 	tfns.ltf_set_lderrno = set_ld_error;
2772830Sdjl 	tfns.ltf_lderrno_arg = NULL;
2782830Sdjl 
2792830Sdjl 	/*
2802830Sdjl 	 * Set up this session to use those function pointers
2812830Sdjl 	 */
2822830Sdjl 	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
2834522Schinlong 	    (void *) &tfns);
2842830Sdjl 	if (rc < 0) {
2852830Sdjl 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
2862830Sdjl 		"(LDAP_OPT_THREAD_FN_PTRS)");
2872830Sdjl 		return (-1);
2882830Sdjl 	}
2892830Sdjl 
2902830Sdjl 	/*
2912830Sdjl 	 * Set the function pointers for working with semaphores
2922830Sdjl 	 */
2932830Sdjl 	(void) memset(&extrafns, '\0',
2944522Schinlong 	    sizeof (struct ldap_extra_thread_fns));
2952830Sdjl 	extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
2962830Sdjl 	extrafns.ltf_mutex_trylock = NULL;
2972830Sdjl 	extrafns.ltf_sema_alloc = NULL;
2982830Sdjl 	extrafns.ltf_sema_free = NULL;
2992830Sdjl 	extrafns.ltf_sema_wait = NULL;
3002830Sdjl 	extrafns.ltf_sema_post = NULL;
3012830Sdjl 
3022830Sdjl 	/* Set up this session to use those function pointers */
3032830Sdjl 	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
3044522Schinlong 	    (void *) &extrafns);
3052830Sdjl 	if (rc < 0) {
3062830Sdjl 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
3072830Sdjl 		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)");
3082830Sdjl 		return (-1);
3092830Sdjl 	}
3102830Sdjl 
3112830Sdjl 	return (0);
3122830Sdjl }
3132830Sdjl 
3142830Sdjl static void
3152830Sdjl ns_setup_mt_conn_and_tsd(LDAP *ld) {
3162830Sdjl 	thread_t t = thr_self();
3172830Sdjl 	void *tsd;
3182830Sdjl 	/* set up to share this connection among threads */
3192830Sdjl 	if (MTperConn == 1) {
3202830Sdjl 		if (tsd_setup() == -1) {
3212830Sdjl 			syslog(LOG_ERR, "tid= %d: unable "
3222830Sdjl 				"to set up TSD\n", t);
3232830Sdjl 		} else {
3242830Sdjl 			if (setup_mt_conn(ld) == -1) {
3252830Sdjl 			/* multiple threads per connection not supported */
3262830Sdjl 				syslog(LOG_ERR, "tid= %d: multiple "
3272830Sdjl 					"threads per connection not "
3282830Sdjl 					"supported\n", t);
3292830Sdjl 				(void) thr_getspecific(ns_mtckey, &tsd);
3302830Sdjl 				ns_tsd_cleanup(tsd);
3312830Sdjl 				(void) thr_setspecific(ns_mtckey, NULL);
3322830Sdjl 				MTperConn = 0;
3332830Sdjl 			}
3342830Sdjl 		}
3352830Sdjl 	}
3362830Sdjl }
3370Sstevel@tonic-gate 
3380Sstevel@tonic-gate /*
3396072Smj162486  * Check name and UID of process, if it is nscd.
3406072Smj162486  *
3416072Smj162486  * Input:
3426072Smj162486  *   pid	: PID of checked process
3436072Smj162486  *   check_uid	: check if UID == 0
3446072Smj162486  * Output:
3456072Smj162486  *   1	: nscd detected
3466072Smj162486  *   0	: nscd not confirmed
3476072Smj162486  */
3486072Smj162486 static int
3496072Smj162486 check_nscd_proc(pid_t pid, boolean_t check_uid)
3506072Smj162486 {
3516072Smj162486 	psinfo_t	pinfo;
3526072Smj162486 	char		fname[MAXPATHLEN];
3536072Smj162486 	ssize_t		ret;
3546072Smj162486 	int		fd;
3556072Smj162486 
3566072Smj162486 	if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
3576072Smj162486 		if ((fd = open(fname,  O_RDONLY)) >= 0) {
3586072Smj162486 			ret = read(fd, &pinfo, sizeof (psinfo_t));
3596072Smj162486 			(void) close(fd);
3606072Smj162486 			if ((ret == sizeof (psinfo_t)) &&
3616072Smj162486 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
3626072Smj162486 				if (check_uid && (pinfo.pr_uid != 0))
3636072Smj162486 					return (0);
3646072Smj162486 				return (1);
3656072Smj162486 			}
3666072Smj162486 		}
3676072Smj162486 	}
3686072Smj162486 	return (0);
3696072Smj162486 }
3706072Smj162486 
3716072Smj162486 /*
3726072Smj162486  * Check if this process is peruser nscd.
3736072Smj162486  */
3746072Smj162486 int
3756072Smj162486 __s_api_peruser_proc(void)
3766072Smj162486 {
3776072Smj162486 	pid_t		my_ppid;
3786072Smj162486 	static mutex_t	nscdLock = DEFAULTMUTEX;
3796072Smj162486 	static pid_t	checkedPpid = (pid_t)-1;
3806072Smj162486 	static int	isPeruserNscd = 0;
3816072Smj162486 
3826072Smj162486 	my_ppid = getppid();
3836072Smj162486 
3846072Smj162486 	/*
3856072Smj162486 	 * Already checked before for this process? If yes, return cached
3866072Smj162486 	 * response.
3876072Smj162486 	 */
3886072Smj162486 	if (my_ppid == checkedPpid) {
3896072Smj162486 		return (isPeruserNscd);
3906072Smj162486 	}
3916072Smj162486 
3926072Smj162486 	(void) mutex_lock(&nscdLock);
3936072Smj162486 
3946072Smj162486 	/* Check once more incase another thread has just complete this. */
3956072Smj162486 	if (my_ppid == checkedPpid) {
3966072Smj162486 		(void) mutex_unlock(&nscdLock);
3976072Smj162486 		return (isPeruserNscd);
3986072Smj162486 	}
3996072Smj162486 
4006072Smj162486 	/* Reinitialize to be sure there is no residue after fork. */
4016072Smj162486 	isPeruserNscd = 0;
4026072Smj162486 
4036072Smj162486 	/* Am I the nscd process? */
4046072Smj162486 	if (check_nscd_proc(getpid(), B_FALSE)) {
4056072Smj162486 		/* Is my parent the nscd process with UID == 0. */
4066072Smj162486 		isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
4076072Smj162486 	}
4086072Smj162486 
4096072Smj162486 	/* Remeber for whom isPeruserNscd is. */
4106072Smj162486 	checkedPpid = my_ppid;
4116072Smj162486 
4126072Smj162486 	(void) mutex_unlock(&nscdLock);
4136072Smj162486 	return (isPeruserNscd);
4146072Smj162486 }
4156072Smj162486 
4166072Smj162486 /*
4176072Smj162486  * Check if this process is main nscd.
4180Sstevel@tonic-gate  */
4195399Schinlong int
4205399Schinlong __s_api_nscd_proc(void)
4210Sstevel@tonic-gate {
4220Sstevel@tonic-gate 	pid_t		my_pid;
4236072Smj162486 	static mutex_t	nscdLock = DEFAULTMUTEX;
4246072Smj162486 	static pid_t	checkedPid = (pid_t)-1;
4256072Smj162486 	static int	isMainNscd = 0;
4260Sstevel@tonic-gate 
4276072Smj162486 	/*
4286072Smj162486 	 * Don't bother checking if this process isn't root, this cannot
4296072Smj162486 	 * be main nscd.
4306072Smj162486 	 */
4310Sstevel@tonic-gate 	if (getuid() != 0)
4320Sstevel@tonic-gate 		return (0);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	my_pid = getpid();
4356072Smj162486 
4366072Smj162486 	/*
4376072Smj162486 	 * Already checked before for this process? If yes, return cached
4386072Smj162486 	 * response.
4396072Smj162486 	 */
4406072Smj162486 	if (my_pid == checkedPid) {
4416072Smj162486 		return (isMainNscd);
4420Sstevel@tonic-gate 	}
4436072Smj162486 
4440Sstevel@tonic-gate 	(void) mutex_lock(&nscdLock);
4456072Smj162486 
4466072Smj162486 	/* Check once more incase another thread has just done this. */
4476072Smj162486 	if (my_pid == checkedPid) {
4480Sstevel@tonic-gate 		(void) mutex_unlock(&nscdLock);
4496072Smj162486 		return (isMainNscd);
4500Sstevel@tonic-gate 	}
4516072Smj162486 
4526072Smj162486 	/*
4536072Smj162486 	 * Am I the nscd process? UID is already checked, not needed from
4546072Smj162486 	 * psinfo.
4556072Smj162486 	 */
4566072Smj162486 	isMainNscd = check_nscd_proc(my_pid, B_FALSE);
4576072Smj162486 
4586072Smj162486 	/* Remeber for whom isMainNscd is. */
4590Sstevel@tonic-gate 	checkedPid = my_pid;
4606072Smj162486 
4610Sstevel@tonic-gate 	(void) mutex_unlock(&nscdLock);
4626072Smj162486 	return (isMainNscd);
4630Sstevel@tonic-gate }
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate /*
4660Sstevel@tonic-gate  * This function requests a server from the cache manager through
4670Sstevel@tonic-gate  * the door functionality
4680Sstevel@tonic-gate  */
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate static int
4710Sstevel@tonic-gate __s_api_requestServer(const char *request, const char *server,
4722830Sdjl 	ns_server_info_t *ret, ns_ldap_error_t **error,  const char *addrType)
4730Sstevel@tonic-gate {
4740Sstevel@tonic-gate 	union {
4750Sstevel@tonic-gate 		ldap_data_t	s_d;
4760Sstevel@tonic-gate 		char		s_b[DOORBUFFERSIZE];
4770Sstevel@tonic-gate 	} space;
4780Sstevel@tonic-gate 	ldap_data_t	*sptr;
4790Sstevel@tonic-gate 	int		ndata;
4800Sstevel@tonic-gate 	int		adata;
4810Sstevel@tonic-gate 	char		errstr[MAXERROR];
4820Sstevel@tonic-gate 	const char	*ireq;
4830Sstevel@tonic-gate 	char		*rbuf, *ptr, *rest;
4840Sstevel@tonic-gate 	char		*dptr;
4850Sstevel@tonic-gate 	char		**mptr, **mptr1, **cptr, **cptr1;
4860Sstevel@tonic-gate 	int		mcnt, ccnt;
4870Sstevel@tonic-gate 	char		**servers;
4882830Sdjl 	int		rc, len;
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	if (ret == NULL || error == NULL) {
4910Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
4920Sstevel@tonic-gate 	}
4930Sstevel@tonic-gate 	(void) memset(ret, 0, sizeof (ns_server_info_t));
4940Sstevel@tonic-gate 	*error = NULL;
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate 	if (request == NULL)
4990Sstevel@tonic-gate 		ireq = NS_CACHE_NEW;
5000Sstevel@tonic-gate 	else
5010Sstevel@tonic-gate 		ireq = request;
5020Sstevel@tonic-gate 
5032830Sdjl 	adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1);
5040Sstevel@tonic-gate 	if (server != NULL) {
5050Sstevel@tonic-gate 		adata += strlen(DOORLINESEP) + 1;
5060Sstevel@tonic-gate 		adata += strlen(server) + 1;
5070Sstevel@tonic-gate 	}
5080Sstevel@tonic-gate 	ndata = sizeof (space);
5092830Sdjl 	len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
5100Sstevel@tonic-gate 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
5112830Sdjl 	if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
5122830Sdjl 		return (NS_LDAP_MEMORY);
5132830Sdjl 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >=
5144522Schinlong 	    len)
5152830Sdjl 		return (NS_LDAP_MEMORY);
5160Sstevel@tonic-gate 	if (server != NULL) {
5172830Sdjl 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
5184522Schinlong 		    DOORLINESEP, len) >= len)
5192830Sdjl 			return (NS_LDAP_MEMORY);
5202830Sdjl 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server,
5214522Schinlong 		    len) >= len)
5222830Sdjl 			return (NS_LDAP_MEMORY);
5230Sstevel@tonic-gate 	}
5240Sstevel@tonic-gate 	sptr = &space.s_d;
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
5270Sstevel@tonic-gate 	case SUCCESS:
5280Sstevel@tonic-gate 		break;
5290Sstevel@tonic-gate 	/* this case is for when the $mgr is not running, but ldapclient */
5300Sstevel@tonic-gate 	/* is trying to initialize things */
5310Sstevel@tonic-gate 	case NOSERVER:
5320Sstevel@tonic-gate 		/* get first server from config list unavailable otherwise */
5330Sstevel@tonic-gate 		servers = NULL;
5340Sstevel@tonic-gate 		rc = __s_api_getServers(&servers, error);
5350Sstevel@tonic-gate 		if (rc != NS_LDAP_SUCCESS) {
5360Sstevel@tonic-gate 			if (servers != NULL) {
5370Sstevel@tonic-gate 				__s_api_free2dArray(servers);
5380Sstevel@tonic-gate 				servers = NULL;
5390Sstevel@tonic-gate 			}
5400Sstevel@tonic-gate 			return (rc);
5410Sstevel@tonic-gate 		}
5420Sstevel@tonic-gate 		if (servers == NULL || servers[0] == NULL) {
5430Sstevel@tonic-gate 			__s_api_free2dArray(servers);
5440Sstevel@tonic-gate 			servers = NULL;
5450Sstevel@tonic-gate 			(void) sprintf(errstr,
5464522Schinlong 			    gettext("No server found in configuration"));
5470Sstevel@tonic-gate 			MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
5484522Schinlong 			    strdup(errstr), NULL);
5490Sstevel@tonic-gate 			return (NS_LDAP_CONFIG);
5500Sstevel@tonic-gate 		}
5510Sstevel@tonic-gate 		ret->server = strdup(servers[0]);
5520Sstevel@tonic-gate 		if (ret->server == NULL) {
5530Sstevel@tonic-gate 			__s_api_free2dArray(servers);
5540Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
5550Sstevel@tonic-gate 		}
5560Sstevel@tonic-gate 		ret->saslMechanisms = NULL;
5570Sstevel@tonic-gate 		ret->controls = NULL;
5580Sstevel@tonic-gate 		__s_api_free2dArray(servers);
5590Sstevel@tonic-gate 		servers = NULL;
5600Sstevel@tonic-gate 		return (NS_LDAP_SUCCESS);
5610Sstevel@tonic-gate 	case NOTFOUND:
5620Sstevel@tonic-gate 	default:
5630Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
5640Sstevel@tonic-gate 	}
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	/* copy info from door call return structure here */
5670Sstevel@tonic-gate 	rbuf =  space.s_d.ldap_ret.ldap_u.config;
5680Sstevel@tonic-gate 
5690Sstevel@tonic-gate 	/* Get the host */
5700Sstevel@tonic-gate 	ptr = strtok_r(rbuf, DOORLINESEP, &rest);
5710Sstevel@tonic-gate 	if (ptr == NULL) {
5720Sstevel@tonic-gate 		(void) sprintf(errstr, gettext("No server returned from "
5734522Schinlong 		    "ldap_cachemgr"));
5740Sstevel@tonic-gate 		MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
5754522Schinlong 		    strdup(errstr), NULL);
5760Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
5770Sstevel@tonic-gate 	}
5780Sstevel@tonic-gate 	ret->server = strdup(ptr);
5790Sstevel@tonic-gate 	if (ret->server == NULL) {
5800Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
5810Sstevel@tonic-gate 	}
5824522Schinlong 	/* Get the host FQDN format */
5834522Schinlong 	if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
5844522Schinlong 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
5854522Schinlong 		if (ptr == NULL) {
5864522Schinlong 			(void) sprintf(errstr, gettext("No server FQDN format "
5874522Schinlong 			    "returned from ldap_cachemgr"));
5884522Schinlong 			MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
5894522Schinlong 			    strdup(errstr), NULL);
5904522Schinlong 			free(ret->server);
5914522Schinlong 			ret->server = NULL;
5924522Schinlong 			return (NS_LDAP_OP_FAILED);
5934522Schinlong 		}
5944522Schinlong 		ret->serverFQDN = strdup(ptr);
5954522Schinlong 		if (ret->serverFQDN == NULL) {
5964522Schinlong 			free(ret->server);
5974522Schinlong 			ret->server = NULL;
5984522Schinlong 			return (NS_LDAP_MEMORY);
5994522Schinlong 		}
6004522Schinlong 	}
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	/* get the Supported Controls/SASL mechs */
6030Sstevel@tonic-gate 	mptr = NULL;
6040Sstevel@tonic-gate 	mcnt = 0;
6050Sstevel@tonic-gate 	cptr = NULL;
6060Sstevel@tonic-gate 	ccnt = 0;
6070Sstevel@tonic-gate 	for (; ; ) {
6080Sstevel@tonic-gate 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
6090Sstevel@tonic-gate 		if (ptr == NULL)
6100Sstevel@tonic-gate 			break;
6110Sstevel@tonic-gate 		if (strncasecmp(ptr, _SASLMECHANISM,
6124522Schinlong 		    _SASLMECHANISM_LEN) == 0) {
6130Sstevel@tonic-gate 			dptr = strchr(ptr, '=');
6140Sstevel@tonic-gate 			if (dptr == NULL)
6150Sstevel@tonic-gate 				continue;
6160Sstevel@tonic-gate 			dptr++;
6170Sstevel@tonic-gate 			mptr1 = (char **)realloc((void *)mptr,
6184522Schinlong 			    sizeof (char *) * (mcnt+2));
6190Sstevel@tonic-gate 			if (mptr1 == NULL) {
6200Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
6210Sstevel@tonic-gate 				if (sptr != &space.s_d) {
6224522Schinlong 					(void) munmap((char *)sptr, ndata);
6230Sstevel@tonic-gate 				}
6240Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
6254522Schinlong 				__s_api_free_server_info(ret);
6260Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
6270Sstevel@tonic-gate 			}
6280Sstevel@tonic-gate 			mptr = mptr1;
6290Sstevel@tonic-gate 			mptr[mcnt] = strdup(dptr);
6300Sstevel@tonic-gate 			if (mptr[mcnt] == NULL) {
6310Sstevel@tonic-gate 				if (sptr != &space.s_d) {
6324522Schinlong 					(void) munmap((char *)sptr, ndata);
6330Sstevel@tonic-gate 				}
6340Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
6350Sstevel@tonic-gate 				cptr = NULL;
6360Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
6370Sstevel@tonic-gate 				mptr = NULL;
6384522Schinlong 				__s_api_free_server_info(ret);
6390Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
6400Sstevel@tonic-gate 			}
6410Sstevel@tonic-gate 			mcnt++;
6420Sstevel@tonic-gate 			mptr[mcnt] = NULL;
6430Sstevel@tonic-gate 		}
6440Sstevel@tonic-gate 		if (strncasecmp(ptr, _SUPPORTEDCONTROL,
6454522Schinlong 		    _SUPPORTEDCONTROL_LEN) == 0) {
6460Sstevel@tonic-gate 			dptr = strchr(ptr, '=');
6470Sstevel@tonic-gate 			if (dptr == NULL)
6480Sstevel@tonic-gate 				continue;
6490Sstevel@tonic-gate 			dptr++;
6500Sstevel@tonic-gate 			cptr1 = (char **)realloc((void *)cptr,
6514522Schinlong 			    sizeof (char *) * (ccnt+2));
6520Sstevel@tonic-gate 			if (cptr1 == NULL) {
6530Sstevel@tonic-gate 				if (sptr != &space.s_d) {
6544522Schinlong 					(void) munmap((char *)sptr, ndata);
6550Sstevel@tonic-gate 				}
6560Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
6570Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
6580Sstevel@tonic-gate 				mptr = NULL;
6594522Schinlong 				__s_api_free_server_info(ret);
6600Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
6610Sstevel@tonic-gate 			}
6620Sstevel@tonic-gate 			cptr = cptr1;
6630Sstevel@tonic-gate 			cptr[ccnt] = strdup(dptr);
6640Sstevel@tonic-gate 			if (cptr[ccnt] == NULL) {
6650Sstevel@tonic-gate 				if (sptr != &space.s_d) {
6664522Schinlong 					(void) munmap((char *)sptr, ndata);
6670Sstevel@tonic-gate 				}
6680Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
6690Sstevel@tonic-gate 				cptr = NULL;
6700Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
6710Sstevel@tonic-gate 				mptr = NULL;
6724522Schinlong 				__s_api_free_server_info(ret);
6730Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
6740Sstevel@tonic-gate 			}
6750Sstevel@tonic-gate 			ccnt++;
6760Sstevel@tonic-gate 			cptr[ccnt] = NULL;
6770Sstevel@tonic-gate 		}
6780Sstevel@tonic-gate 	}
6790Sstevel@tonic-gate 	if (mptr != NULL) {
6800Sstevel@tonic-gate 		ret->saslMechanisms = mptr;
6810Sstevel@tonic-gate 	}
6820Sstevel@tonic-gate 	if (cptr != NULL) {
6830Sstevel@tonic-gate 		ret->controls = cptr;
6840Sstevel@tonic-gate 	}
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 
6870Sstevel@tonic-gate 	/* clean up door call */
6880Sstevel@tonic-gate 	if (sptr != &space.s_d) {
6890Sstevel@tonic-gate 		(void) munmap((char *)sptr, ndata);
6900Sstevel@tonic-gate 	}
6910Sstevel@tonic-gate 	*error = NULL;
6920Sstevel@tonic-gate 
6930Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
6940Sstevel@tonic-gate }
6950Sstevel@tonic-gate 
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate /*
6980Sstevel@tonic-gate  * printCred(): prints the credential structure
6990Sstevel@tonic-gate  */
7000Sstevel@tonic-gate static void
7012830Sdjl printCred(int pri, const ns_cred_t *cred)
7020Sstevel@tonic-gate {
7032830Sdjl 	thread_t	t = thr_self();
7042830Sdjl 
7050Sstevel@tonic-gate 	if (cred == NULL) {
7062830Sdjl 		syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t);
7070Sstevel@tonic-gate 		return;
7080Sstevel@tonic-gate 	}
7090Sstevel@tonic-gate 
7102830Sdjl 	syslog(pri, "tid= %d: AuthType=%d", t, cred->auth.type);
7112830Sdjl 	syslog(pri, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
7122830Sdjl 	syslog(pri, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
7132830Sdjl 	syslog(pri, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
7140Sstevel@tonic-gate 	if (cred->hostcertpath)
7152830Sdjl 		syslog(pri, "tid= %d: hostCertPath=%s\n",
7164522Schinlong 		    t, cred->hostcertpath);
7170Sstevel@tonic-gate 	if (cred->cred.unix_cred.userID)
7182830Sdjl 		syslog(pri, "tid= %d: userID=%s\n",
7194522Schinlong 		    t, cred->cred.unix_cred.userID);
7203387Schinlong #ifdef DEBUG
7210Sstevel@tonic-gate 	if (cred->cred.unix_cred.passwd)
7222830Sdjl 		syslog(pri, "tid= %d: passwd=%s\n",
7234522Schinlong 		    t, cred->cred.unix_cred.passwd);
7243387Schinlong #endif
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate /*
7280Sstevel@tonic-gate  * printConnection(): prints the connection structure
7290Sstevel@tonic-gate  */
7300Sstevel@tonic-gate static void
7312830Sdjl printConnection(int pri, Connection *con)
7320Sstevel@tonic-gate {
7332830Sdjl 	thread_t	t = thr_self();
7342830Sdjl 
7352830Sdjl 	if (con == NULL)
7360Sstevel@tonic-gate 		return;
7370Sstevel@tonic-gate 
7382830Sdjl 	syslog(pri, "tid= %d: connectionID=%d\n", t, con->connectionId);
7392830Sdjl 	syslog(pri, "tid= %d: shared=%d\n", t, con->shared);
7402830Sdjl 	syslog(pri, "tid= %d: usedBit=%d\n", t, con->usedBit);
7412830Sdjl 	syslog(pri, "tid= %d: threadID=%d\n", t, con->threadID);
7420Sstevel@tonic-gate 	if (con->serverAddr) {
7432830Sdjl 		syslog(pri, "tid= %d: serverAddr=%s\n",
7444522Schinlong 		    t, con->serverAddr);
7450Sstevel@tonic-gate 	}
7462830Sdjl 	printCred(pri, con->auth);
7470Sstevel@tonic-gate }
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate /*
7522830Sdjl  * addConnection(): set up a connection so that it can be shared
7532830Sdjl  * among multiple threads and then insert the connection in the
7542830Sdjl  * connection list.
7550Sstevel@tonic-gate  * Returns: -1 = failure, new Connection ID = success
7562830Sdjl  *
7572830Sdjl  * This function could exit with sessionLock locked. It will be
7582830Sdjl  * be unlocked in __s_api_getConnection() when it exits without getting a
7592830Sdjl  * connection.
7600Sstevel@tonic-gate  */
7610Sstevel@tonic-gate static int
7620Sstevel@tonic-gate addConnection(Connection *con)
7630Sstevel@tonic-gate {
7642830Sdjl 	int i, noMTperC = 0;
7652830Sdjl 	thread_t t = thr_self();
7662830Sdjl 	struct ldap_thread_fns tfns;
7672830Sdjl 	void *tsd;
7680Sstevel@tonic-gate 
7690Sstevel@tonic-gate 	if (!con)
7700Sstevel@tonic-gate 		return (-1);
7712830Sdjl 
7722830Sdjl 	syslog(LOG_DEBUG, "tid= %d: Adding connection (serverAddr=%s)",
7734522Schinlong 	    t, con->serverAddr);
7742830Sdjl 
7752830Sdjl 	if (MTperConn == 1) {
7762830Sdjl 		/*
7772830Sdjl 		 * Make sure ld has proper thread functions and tsd
7782830Sdjl 		 * is set up.
7792830Sdjl 		 */
7802830Sdjl 		(void) memset(&tfns, 0, sizeof (struct ldap_thread_fns));
7812830Sdjl 		/*
7822830Sdjl 		 * ldap_init sets ltf_get_lderrno and ltf_set_lderrno to NULLs.
7832830Sdjl 		 * It's supposed to be overwritten by ns_setup_mt_conn_and_tsd.
7842830Sdjl 		 */
7852830Sdjl 		if (ldap_get_option(con->ld, LDAP_OPT_THREAD_FN_PTRS,
7864522Schinlong 		    (void *)&tfns) != 0 ||
7874522Schinlong 		    tfns.ltf_get_lderrno != get_ld_error ||
7884522Schinlong 		    tfns.ltf_set_lderrno != set_ld_error) {
7892830Sdjl 			MTperConn = 0;
7902830Sdjl 			noMTperC = 1;
7912830Sdjl 		} else {
7922830Sdjl 			if (thr_getspecific(ns_mtckey, &tsd) != 0 ||
7934522Schinlong 			    tsd == NULL)
7942830Sdjl 				noMTperC = 1;
7952830Sdjl 		}
7962830Sdjl 
7972830Sdjl 	} else {
7982830Sdjl 		noMTperC = 1;
7992830Sdjl 	}
8002830Sdjl 
8012830Sdjl 	(void) rw_wrlock(&sessionPoolLock);
8020Sstevel@tonic-gate 	if (sessionPool == NULL) {
8030Sstevel@tonic-gate 		sessionPoolSize = SESSION_CACHE_INC;
8040Sstevel@tonic-gate 		sessionPool = calloc(sessionPoolSize,
8054522Schinlong 		    sizeof (struct connection **));
8060Sstevel@tonic-gate 		if (!sessionPool) {
8072830Sdjl 			(void) rw_unlock(&sessionPoolLock);
8080Sstevel@tonic-gate 			return (-1);
8090Sstevel@tonic-gate 		}
8102830Sdjl 
8112830Sdjl 		syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t);
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 	for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
8140Sstevel@tonic-gate 		;
8150Sstevel@tonic-gate 	if (i == sessionPoolSize) {
8160Sstevel@tonic-gate 		/* run out of array, need to increase sessionPool */
8170Sstevel@tonic-gate 		Connection **cl;
8180Sstevel@tonic-gate 		cl = (Connection **) realloc(sessionPool,
8194522Schinlong 		    (sessionPoolSize + SESSION_CACHE_INC) *
8204522Schinlong 		    sizeof (Connection *));
8210Sstevel@tonic-gate 		if (!cl) {
8222830Sdjl 			(void) rw_unlock(&sessionPoolLock);
8230Sstevel@tonic-gate 			return (-1);
8240Sstevel@tonic-gate 		}
8250Sstevel@tonic-gate 		(void) memset(cl + sessionPoolSize, 0,
8264522Schinlong 		    SESSION_CACHE_INC * sizeof (struct connection *));
8270Sstevel@tonic-gate 		sessionPool = cl;
8280Sstevel@tonic-gate 		sessionPoolSize += SESSION_CACHE_INC;
8292830Sdjl 		syslog(LOG_DEBUG, "tid: %d: Increased "
8304522Schinlong 		    "sessionPoolSize to: %d\n",
8314522Schinlong 		    t, sessionPoolSize);
8320Sstevel@tonic-gate 	}
8330Sstevel@tonic-gate 	sessionPool[i] = con;
8343387Schinlong 	if (noMTperC == 0) {
8352830Sdjl 		con->shared++;
8363387Schinlong 		con->pid = getpid();
8373387Schinlong 		(void) mutex_lock(&sharedConnNumberLock);
8383387Schinlong 		sharedConnNumber++;
8393387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
8403387Schinlong 	} else
8412830Sdjl 		con->usedBit = B_TRUE;
8422830Sdjl 
8432830Sdjl 	(void) rw_unlock(&sessionPoolLock);
8442830Sdjl 
8450Sstevel@tonic-gate 	con->connectionId = i + CONID_OFFSET;
8462830Sdjl 
8472830Sdjl 	syslog(LOG_DEBUG, "tid= %d: Connection added [%d]\n",
8484522Schinlong 	    t, i);
8492830Sdjl 	printConnection(LOG_DEBUG, con);
8502830Sdjl 
8512830Sdjl 	/*
8522830Sdjl 	 * A connection can be shared now, unlock
8532830Sdjl 	 * the session mutex and let other
8542830Sdjl 	 * threads try to use this connection or
8552830Sdjl 	 * get their own.
8562830Sdjl 	 */
8572830Sdjl 	if (wait4session != 0 && sessionTid == thr_self()) {
8582830Sdjl 		wait4session = 0;
8592830Sdjl 		sessionTid = 0;
8602830Sdjl 		syslog(LOG_DEBUG, "tid= %d: unlocking sessionLock\n", t);
8612830Sdjl 		(void) mutex_unlock(&sessionLock);
8622830Sdjl 	}
8632830Sdjl 
8640Sstevel@tonic-gate 	return (i + CONID_OFFSET);
8650Sstevel@tonic-gate }
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate /*
8680Sstevel@tonic-gate  * See if the specified session matches a currently available
8690Sstevel@tonic-gate  */
8700Sstevel@tonic-gate 
8710Sstevel@tonic-gate static int
8720Sstevel@tonic-gate findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
8730Sstevel@tonic-gate 	Connection **conp)
8740Sstevel@tonic-gate {
8750Sstevel@tonic-gate 	Connection *cp;
8760Sstevel@tonic-gate 	int id;
8770Sstevel@tonic-gate 
8780Sstevel@tonic-gate 	if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
8790Sstevel@tonic-gate 		return (-1);
8802830Sdjl 
8815532Smj162486 	/*
8825532Smj162486 	 * If a new connection is requested, no need to continue.
8835532Smj162486 	 * If the process is not nscd and is not requesting keep connections
8845532Smj162486 	 * alive, no need to continue.
8855532Smj162486 	 */
8865532Smj162486 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
8876072Smj162486 	    !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
8882830Sdjl 		return (-1);
8892830Sdjl 
8900Sstevel@tonic-gate 	*conp = NULL;
8910Sstevel@tonic-gate 	if (sessionPool == NULL)
8920Sstevel@tonic-gate 		return (-1);
8930Sstevel@tonic-gate 	id = cID - CONID_OFFSET;
8940Sstevel@tonic-gate 	if (id < 0 || id >= sessionPoolSize)
8950Sstevel@tonic-gate 		return (-1);
8960Sstevel@tonic-gate 
8972830Sdjl 	(void) rw_rdlock(&sessionPoolLock);
8980Sstevel@tonic-gate 	if (sessionPool[id] == NULL) {
8992830Sdjl 		(void) rw_unlock(&sessionPoolLock);
9000Sstevel@tonic-gate 		return (-1);
9010Sstevel@tonic-gate 	}
9020Sstevel@tonic-gate 	cp = sessionPool[id];
9030Sstevel@tonic-gate 
9040Sstevel@tonic-gate 	/*
9050Sstevel@tonic-gate 	 * Make sure the connection has the same type of authentication method
9060Sstevel@tonic-gate 	 */
9070Sstevel@tonic-gate 	if ((cp->usedBit) ||
9082830Sdjl 	    (cp->notAvail) ||
9090Sstevel@tonic-gate 	    (cp->auth->auth.type != auth->auth.type) ||
9100Sstevel@tonic-gate 	    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
9110Sstevel@tonic-gate 	    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
9120Sstevel@tonic-gate 	    (cp->auth->auth.saslopt != auth->auth.saslopt)) {
9132830Sdjl 		(void) rw_unlock(&sessionPoolLock);
9140Sstevel@tonic-gate 		return (-1);
9150Sstevel@tonic-gate 	}
9160Sstevel@tonic-gate 	if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
9174522Schinlong 	    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
9184522Schinlong 	    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
9194522Schinlong 	    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
9204522Schinlong 	    ((cp->auth->cred.unix_cred.userID == NULL) ||
9214522Schinlong 	    (strcasecmp(cp->auth->cred.unix_cred.userID,
9224522Schinlong 	    auth->cred.unix_cred.userID) != 0))) {
9232830Sdjl 		(void) rw_unlock(&sessionPoolLock);
9240Sstevel@tonic-gate 		return (-1);
9250Sstevel@tonic-gate 	}
9262830Sdjl 
9270Sstevel@tonic-gate 	/* An existing connection is found but it needs to be reset */
9280Sstevel@tonic-gate 	if (flags & NS_LDAP_NEW_CONN) {
9292830Sdjl 		(void) rw_unlock(&sessionPoolLock);
9300Sstevel@tonic-gate 		DropConnection(cID, 0);
9310Sstevel@tonic-gate 		return (-1);
9320Sstevel@tonic-gate 	}
9330Sstevel@tonic-gate 	/* found an available connection */
9340Sstevel@tonic-gate 	cp->usedBit = B_TRUE;
9352830Sdjl 	(void) rw_unlock(&sessionPoolLock);
9360Sstevel@tonic-gate 	cp->threadID = thr_self();
9370Sstevel@tonic-gate 	*conp = cp;
9380Sstevel@tonic-gate 	return (cID);
9390Sstevel@tonic-gate }
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate /*
9420Sstevel@tonic-gate  * findConnection(): find an available connection from the list
9430Sstevel@tonic-gate  * that matches the criteria specified in Connection structure.
9440Sstevel@tonic-gate  * If serverAddr is NULL, then find a connection to any server
9450Sstevel@tonic-gate  * as long as it matches the rest of the parameters.
9460Sstevel@tonic-gate  * Returns: -1 = failure, the Connection ID found = success.
9472830Sdjl  *
9482830Sdjl  * This function could exit with sessionLock locked. It will be
9492830Sdjl  * be unlocked in addConnection() when this thread adds the connection
9502830Sdjl  * to the pool or in __s_api_getConnection() when it exits without getting a
9512830Sdjl  * connection.
9520Sstevel@tonic-gate  */
9532830Sdjl #define	TRY_TIMES	10
9540Sstevel@tonic-gate static int
9552830Sdjl findConnection(int flags, const char *serverAddr,
9562830Sdjl 	const ns_cred_t *auth, Connection **conp)
9570Sstevel@tonic-gate {
9580Sstevel@tonic-gate 	Connection *cp;
9594048Schinlong 	int i, j, conn_server_index, up_server_index, drop_conn;
9602830Sdjl 	int rc;
9612830Sdjl 	int try;
9624048Schinlong 	ns_server_info_t sinfo;
9634048Schinlong 	ns_ldap_error_t *errorp = NULL;
9644522Schinlong 	char **servers;
9654048Schinlong 	void **paramVal = NULL;
9662830Sdjl #ifdef DEBUG
9672830Sdjl 	thread_t t = thr_self();
9682830Sdjl #endif /* DEBUG */
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	if (auth == NULL || conp == NULL)
9710Sstevel@tonic-gate 		return (-1);
9720Sstevel@tonic-gate 	*conp = NULL;
9730Sstevel@tonic-gate 
9745840Smj162486 	/*
9755840Smj162486 	 * If a new connection is requested, no need to continue.
9765840Smj162486 	 * If the process is not nscd and is not requesting keep connections
9775840Smj162486 	 * alive, no need to continue.
9785840Smj162486 	 */
9795840Smj162486 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
9806072Smj162486 	    !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
9812830Sdjl 		return (-1);
9822830Sdjl 
9830Sstevel@tonic-gate #ifdef DEBUG
9842830Sdjl 	(void) fprintf(stderr, "tid= %d: Find connection\n", t);
9852830Sdjl 	(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
9860Sstevel@tonic-gate 	if (serverAddr && *serverAddr)
9872830Sdjl 		(void) fprintf(stderr, "tid= %d: serverAddr=%s\n",
9884522Schinlong 		    t, serverAddr);
9890Sstevel@tonic-gate 	else
9902830Sdjl 		(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
9914048Schinlong 	printCred(LOG_DEBUG, auth);
9920Sstevel@tonic-gate 	fflush(stderr);
9930Sstevel@tonic-gate #endif /* DEBUG */
9942830Sdjl 
9952830Sdjl 	/*
9962830Sdjl 	 * If multiple threads per connection not supported,
9972830Sdjl 	 * no sessionPool means no connection
9982830Sdjl 	 */
9992830Sdjl 	(void) rw_rdlock(&sessionPoolLock);
10002830Sdjl 	if (MTperConn == 0 && sessionPool == NULL) {
10012830Sdjl 		(void) rw_unlock(&sessionPoolLock);
10020Sstevel@tonic-gate 		return (-1);
10032830Sdjl 	}
10042830Sdjl 
10052830Sdjl 	/*
10063387Schinlong 	 * If no sharable connections in cache, then serialize the opening
10072830Sdjl 	 * of connections. Make sure only one is being opened
10082830Sdjl 	 * at a time. Otherwise, we may end up with more
10092830Sdjl 	 * connections than we want (if multiple threads get
10102830Sdjl 	 * here at the same time)
10112830Sdjl 	 */
10123387Schinlong 	(void) mutex_lock(&sharedConnNumberLock);
10133387Schinlong 	if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) {
10143387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
10152830Sdjl 		(void) rw_unlock(&sessionPoolLock);
10162830Sdjl 		(void) mutex_lock(&sessionLock);
10173387Schinlong 		(void) mutex_lock(&sharedConnNumberLock);
10183387Schinlong 		if (sessionPool == NULL || (sharedConnNumber == 0 &&
10194522Schinlong 		    MTperConn == 1)) {
10203387Schinlong 			(void) mutex_unlock(&sharedConnNumberLock);
10212830Sdjl 			wait4session = 1;
10222830Sdjl 			sessionTid = thr_self();
10232830Sdjl #ifdef DEBUG
10242830Sdjl 			(void) fprintf(stderr, "tid= %d: get "
10254522Schinlong 			    "connection ... \n", t);
10262830Sdjl 			fflush(stderr);
10272830Sdjl #endif /* DEBUG */
10282830Sdjl 			/*
10292830Sdjl 			 * Exit with sessionLock locked. It will be
10302830Sdjl 			 * be unlocked in addConnection() when this
10312830Sdjl 			 * thread adds the connection to the pool or
10322830Sdjl 			 * in __s_api_getConnection() when it exits
10332830Sdjl 			 * without getting a connection.
10342830Sdjl 			 */
10352830Sdjl 			return (-1);
10362830Sdjl 		}
10372830Sdjl 
10382830Sdjl #ifdef DEBUG
10393387Schinlong 		(void) fprintf(stderr, "tid= %d: shareable connections "
10404522Schinlong 		    "exist\n", t);
10412830Sdjl 		fflush(stderr);
10422830Sdjl #endif /* DEBUG */
10433387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
10442830Sdjl 		/*
10453387Schinlong 		 * There are sharable connections, check to see if
10462830Sdjl 		 * one can be shared.
10472830Sdjl 		 */
10482830Sdjl 		(void) mutex_unlock(&sessionLock);
10492830Sdjl 		(void) rw_rdlock(&sessionPoolLock);
10503387Schinlong 	} else
10513387Schinlong 		(void) mutex_unlock(&sharedConnNumberLock);
10523387Schinlong 
10532830Sdjl 	try = 0;
10542830Sdjl 	check_again:
10552830Sdjl 
10560Sstevel@tonic-gate 	for (i = 0; i < sessionPoolSize; ++i) {
10570Sstevel@tonic-gate 		if (sessionPool[i] == NULL)
10580Sstevel@tonic-gate 			continue;
10590Sstevel@tonic-gate 		cp = sessionPool[i];
10600Sstevel@tonic-gate #ifdef DEBUG
10612830Sdjl 		(void) fprintf(stderr, "tid= %d: checking connection "
10624522Schinlong 		    "[%d] ....\n", t, i);
10634048Schinlong 		printConnection(LOG_DEBUG, cp);
10640Sstevel@tonic-gate #endif /* DEBUG */
10652830Sdjl 		if ((cp->usedBit) || (cp->notAvail) ||
10660Sstevel@tonic-gate 		    (cp->auth->auth.type != auth->auth.type) ||
10670Sstevel@tonic-gate 		    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
10680Sstevel@tonic-gate 		    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
10690Sstevel@tonic-gate 		    (cp->auth->auth.saslopt != auth->auth.saslopt) ||
10700Sstevel@tonic-gate 		    (serverAddr && *serverAddr &&
10710Sstevel@tonic-gate 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
10720Sstevel@tonic-gate 			continue;
10730Sstevel@tonic-gate 		if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
10740Sstevel@tonic-gate 		    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
10750Sstevel@tonic-gate 		    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
10760Sstevel@tonic-gate 		    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
10770Sstevel@tonic-gate 		    ((cp->auth->cred.unix_cred.userID == NULL) ||
10780Sstevel@tonic-gate 		    (cp->auth->cred.unix_cred.passwd == NULL) ||
10790Sstevel@tonic-gate 		    ((strcasecmp(cp->auth->cred.unix_cred.userID,
10804522Schinlong 		    auth->cred.unix_cred.userID) != 0)) ||
10810Sstevel@tonic-gate 		    ((strcmp(cp->auth->cred.unix_cred.passwd,
10824522Schinlong 		    auth->cred.unix_cred.passwd) != 0))))
10830Sstevel@tonic-gate 				continue;
10844048Schinlong 		if (!(serverAddr && *serverAddr)) {
10854048Schinlong 			/*
10864048Schinlong 			 * Get preferred server list.
10874048Schinlong 			 * When preferred servers are merged with default
10884048Schinlong 			 * servers (S_LDAP_SERVER_P) by __s_api_getServer,
10894048Schinlong 			 * preferred servers are copied sequencially.
10904048Schinlong 			 * The order should be the same as the order retrieved
10914048Schinlong 			 * by __ns_ldap_getParam.
10924048Schinlong 			 */
10934048Schinlong 			if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
10944522Schinlong 			    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
10954048Schinlong 				(void) __ns_ldap_freeError(&errorp);
10964048Schinlong 				(void) __ns_ldap_freeParam(&paramVal);
10974048Schinlong 				(void) rw_unlock(&sessionPoolLock);
10984048Schinlong 				return (-1);
10994048Schinlong 			}
11004048Schinlong 			servers = (char **)paramVal;
11014048Schinlong 			/*
11024048Schinlong 			 * Do fallback only if preferred servers are defined.
11034048Schinlong 			 */
11044048Schinlong 			if (servers != NULL) {
11054048Schinlong 				/*
11064048Schinlong 				 * Find the 1st available server
11074048Schinlong 				 */
11084048Schinlong 				rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
11094522Schinlong 				    &sinfo, &errorp, NS_CACHE_ADDR_IP);
11104048Schinlong 				if (rc != NS_LDAP_SUCCESS) {
11114048Schinlong 					/*
11124048Schinlong 					 * Drop the connection.
11134048Schinlong 					 * Pass 1 to fini so it won't be locked
11144048Schinlong 					 * inside _DropConnection
11154048Schinlong 					 */
11164048Schinlong 					_DropConnection(
11174522Schinlong 					    cp->connectionId,
11184522Schinlong 					    NS_LDAP_NEW_CONN, 1);
11194048Schinlong 					(void) rw_unlock(
11204048Schinlong 					    &sessionPoolLock);
11214522Schinlong 					(void) __ns_ldap_freeError(&errorp);
11224048Schinlong 					(void) __ns_ldap_freeParam(
11234522Schinlong 					    (void ***)&servers);
11244048Schinlong 					return (-1);
11254048Schinlong 				}
11264048Schinlong 
11274048Schinlong 				if (sinfo.server) {
11284048Schinlong 					/*
11294048Schinlong 					 * Test if cp->serverAddr is a
11304048Schinlong 					 * preferred server.
11314048Schinlong 					 */
11324048Schinlong 					conn_server_index = -1;
11334048Schinlong 					for (j = 0; servers[j] != NULL; j++) {
11344048Schinlong 						if (strcasecmp(servers[j],
11354522Schinlong 						    cp->serverAddr) == 0) {
11364048Schinlong 							conn_server_index = j;
11374048Schinlong 							break;
11384048Schinlong 						}
11394048Schinlong 					}
11404048Schinlong 					/*
11414048Schinlong 					 * Test if sinfo.server is a preferred
11424048Schinlong 					 * server.
11434048Schinlong 					 */
11444048Schinlong 					up_server_index = -1;
11454048Schinlong 					for (j = 0; servers[j] != NULL; j++) {
11464048Schinlong 						if (strcasecmp(sinfo.server,
11474522Schinlong 						    servers[j]) == 0) {
11484048Schinlong 							up_server_index = j;
11494048Schinlong 							break;
11504048Schinlong 						}
11514048Schinlong 					}
11524048Schinlong 
11534048Schinlong 					/*
11544048Schinlong 					 * The following code is to fall back
11554048Schinlong 					 * to preferred servers if servers
11564048Schinlong 					 * are previously down but are up now.
11574048Schinlong 					 * If cp->serverAddr is a preferred
11584048Schinlong 					 * server, it falls back to the servers
11594048Schinlong 					 * ahead of it. If cp->serverAddr is
11604048Schinlong 					 * not a preferred server, it falls
11614048Schinlong 					 * back to any of preferred servers
11624048Schinlong 					 * returned by ldap_cachemgr.
11634048Schinlong 					 */
11644048Schinlong 					if (conn_server_index >= 0 &&
11654522Schinlong 					    up_server_index >= 0) {
11664048Schinlong 						/*
11674048Schinlong 						 * cp->serverAddr and
11684048Schinlong 						 * sinfo.server are preferred
11694048Schinlong 						 * servers.
11704048Schinlong 						 */
11714048Schinlong 						if (up_server_index ==
11724522Schinlong 						    conn_server_index)
11734048Schinlong 							/*
11744048Schinlong 							 * sinfo.server is the
11754048Schinlong 							 * same as
11764048Schinlong 							 * cp->serverAddr.
11774048Schinlong 							 * Keep the connection.
11784048Schinlong 							 */
11794048Schinlong 							drop_conn = 0;
11804048Schinlong 						else
11814048Schinlong 							/*
11824048Schinlong 							 * 1.
11834048Schinlong 							 * up_server_index <
11844048Schinlong 							 * conn_server_index
11854048Schinlong 							 *
11864048Schinlong 							 * sinfo is ahead of
11874048Schinlong 							 * cp->serverAddr in
11884048Schinlong 							 * Need to fall back.
11894048Schinlong 							 * 2.
11904048Schinlong 							 * up_server_index >
11914048Schinlong 							 * conn_server_index
11924048Schinlong 							 * cp->serverAddr is
11934048Schinlong 							 * down. Drop it.
11944048Schinlong 							 */
11954048Schinlong 							drop_conn = 1;
11964048Schinlong 					} else if (conn_server_index >= 0 &&
11974522Schinlong 					    up_server_index == -1) {
11984048Schinlong 						/*
11994048Schinlong 						 * cp->serverAddr is a preferred
12004048Schinlong 						 * server but sinfo.server is
12014048Schinlong 						 * not. Preferred servers are
12024048Schinlong 						 * ahead of default servers.
12034048Schinlong 						 * This means cp->serverAddr is
12044048Schinlong 						 * down. Drop it.
12054048Schinlong 						 */
12064048Schinlong 						drop_conn = 1;
12074048Schinlong 					} else if (conn_server_index == -1 &&
12084522Schinlong 					    up_server_index >= 0) {
12094048Schinlong 						/*
12104048Schinlong 						 * cp->serverAddr is not a
12114048Schinlong 						 * preferred server but
12124048Schinlong 						 * sinfo.server is.
12134048Schinlong 						 * Fall back.
12144048Schinlong 						 */
12154048Schinlong 						drop_conn = 1;
12164048Schinlong 					} else {
12174048Schinlong 						/*
12184048Schinlong 						 * Both index are -1
12194048Schinlong 						 * cp->serverAddr and
12204048Schinlong 						 * sinfo.server are not
12214048Schinlong 						 * preferred servers.
12224048Schinlong 						 * No fallback.
12234048Schinlong 						 */
12244048Schinlong 						drop_conn = 0;
12254048Schinlong 					}
12264048Schinlong 					if (drop_conn) {
12274048Schinlong 						/*
12284048Schinlong 						 * Drop the connection so the
12294048Schinlong 						 * new conection can fall back
12304048Schinlong 						 * to a new server later.
12314048Schinlong 						 * Pass 1 to fini so it won't
12324048Schinlong 						 * be locked inside
12334048Schinlong 						 * _DropConnection
12344048Schinlong 						 */
12354048Schinlong 						_DropConnection(
12364522Schinlong 						    cp->connectionId,
12374522Schinlong 						    NS_LDAP_NEW_CONN, 1);
12384048Schinlong 						(void) rw_unlock(
12394048Schinlong 						    &sessionPoolLock);
12404048Schinlong 						(void) __ns_ldap_freeParam(
12414522Schinlong 						    (void ***)&servers);
12424522Schinlong 						__s_api_free_server_info(
12434522Schinlong 						    &sinfo);
12444048Schinlong 						return (-1);
12454048Schinlong 					} else {
12464048Schinlong 						/*
12474048Schinlong 						 * Keep the connection
12484048Schinlong 						 */
12494048Schinlong 						(void) __ns_ldap_freeParam(
12504522Schinlong 						    (void ***)&servers);
12514522Schinlong 						__s_api_free_server_info(
12524522Schinlong 						    &sinfo);
12534048Schinlong 					}
12544048Schinlong 				} else {
12554048Schinlong 					(void) rw_unlock(&sessionPoolLock);
12564048Schinlong 					syslog(LOG_WARNING, "libsldap: Null "
12574522Schinlong 					    "sinfo.server from "
12584522Schinlong 					    "__s_api_requestServer");
12594048Schinlong 					(void) __ns_ldap_freeParam(
12604522Schinlong 					    (void ***)&servers);
12614048Schinlong 					return (-1);
12624048Schinlong 				}
12634048Schinlong 			}
12644048Schinlong 		}
12654048Schinlong 
12660Sstevel@tonic-gate 		/* found an available connection */
12672830Sdjl 		if (MTperConn == 0)
12682830Sdjl 			cp->usedBit = B_TRUE;
12692830Sdjl 		else {
12703387Schinlong 			/*
12713387Schinlong 			 * if connection was established in a different
12723387Schinlong 			 * process, drop it and get a new one
12733387Schinlong 			 */
12743387Schinlong 			if (cp->pid != getpid()) {
12753387Schinlong 				(void) rw_unlock(&sessionPoolLock);
12763387Schinlong 				DropConnection(cp->connectionId,
12774522Schinlong 				    NS_LDAP_NEW_CONN);
12783387Schinlong 
12793387Schinlong 				goto get_conn;
12803387Schinlong 			}
12812830Sdjl 			/* allocate TSD for per thread ldap error */
12822830Sdjl 			rc = tsd_setup();
12832830Sdjl 
12842830Sdjl 			/* if we got TSD, this connection is shared */
12852830Sdjl 			if (rc != -1)
12862830Sdjl 				cp->shared++;
12872830Sdjl 			else if (cp->shared == 0) {
12882830Sdjl 				cp->usedBit = B_TRUE;
12892830Sdjl 				cp->threadID = thr_self();
12902830Sdjl 				(void) rw_unlock(&sessionPoolLock);
12912830Sdjl 				return (-1);
12922830Sdjl 			}
12932830Sdjl 		}
12942830Sdjl 		(void) rw_unlock(&sessionPoolLock);
12952830Sdjl 
12960Sstevel@tonic-gate 		*conp = cp;
12970Sstevel@tonic-gate #ifdef DEBUG
12982830Sdjl 		(void) fprintf(stderr, "tid= %d: Connection found "
12994522Schinlong 		    "cID=%d, shared =%d\n", t, i, cp->shared);
13000Sstevel@tonic-gate 		fflush(stderr);
13010Sstevel@tonic-gate #endif /* DEBUG */
13020Sstevel@tonic-gate 		return (i + CONID_OFFSET);
13030Sstevel@tonic-gate 	}
13043387Schinlong 
13053387Schinlong 	get_conn:
13063387Schinlong 
13072830Sdjl 	(void) rw_unlock(&sessionPoolLock);
13082830Sdjl 
13092830Sdjl 	/*
13102830Sdjl 	 * If multiple threads per connection not supported,
13112830Sdjl 	 * we are done, just return -1 to tell the caller to
13122830Sdjl 	 * proceed with opening a connection
13132830Sdjl 	 */
13142830Sdjl 	if (MTperConn == 0)
13152830Sdjl 		return (-1);
13162830Sdjl 
13172830Sdjl 	/*
13182830Sdjl 	 * No connection can be shared, test to see if
13192830Sdjl 	 * one is being opened. If trylock returns
13202830Sdjl 	 * EBUSY then it is, so wait until the opening
13212830Sdjl 	 * is done and try to see if the new connection
13222830Sdjl 	 * can be shared.
13232830Sdjl 	 */
13242830Sdjl 	rc = mutex_trylock(&sessionLock);
13252830Sdjl 	if (rc == EBUSY) {
13262830Sdjl 		(void) mutex_lock(&sessionLock);
13272830Sdjl 		(void) mutex_unlock(&sessionLock);
13282830Sdjl 		(void) rw_rdlock(&sessionPoolLock);
13292830Sdjl #ifdef DEBUG
13302830Sdjl 		(void) fprintf(stderr, "tid= %d: check session "
13314522Schinlong 		    "pool again\n", t);
13322830Sdjl 		fflush(stderr);
13332830Sdjl #endif /* DEBUG */
13342830Sdjl 		if (try < TRY_TIMES) {
13352830Sdjl 			try++;
13362830Sdjl 			goto check_again;
13372830Sdjl 		} else {
13382830Sdjl 			syslog(LOG_WARNING, "libsldap: mutex_trylock "
13394522Schinlong 			    "%d times. Stop.", TRY_TIMES);
13403387Schinlong 			(void) rw_unlock(&sessionPoolLock);
13412830Sdjl 			return (-1);
13422830Sdjl 		}
13432830Sdjl 	} else if (rc == 0) {
13442830Sdjl 		/*
13452830Sdjl 		 * No connection can be shared, none being opened,
13462830Sdjl 		 * exit with sessionLock locked to open one. The
13472830Sdjl 		 * mutex will be unlocked in addConnection() when
13482830Sdjl 		 * this thread adds the new connection to the pool
13492830Sdjl 		 * or in __s_api_getConnection() when it exits
13502830Sdjl 		 * without getting a connection.
13512830Sdjl 		 */
13522830Sdjl 		wait4session = 1;
13532830Sdjl 		sessionTid = thr_self();
13542830Sdjl #ifdef DEBUG
13552830Sdjl 		(void) fprintf(stderr, "tid= %d: no connection found, "
13564522Schinlong 		    "none being opened, get connection ...\n", t);
13572830Sdjl 		fflush(stderr);
13582830Sdjl #endif /* DEBUG */
13592830Sdjl 		return (-1);
13602830Sdjl 	} else {
13612830Sdjl 		syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
13624522Schinlong 		    "error %d", rc);
13632830Sdjl 		return (-1);
13642830Sdjl 	}
13650Sstevel@tonic-gate }
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate /*
13680Sstevel@tonic-gate  * Free a Connection structure
13690Sstevel@tonic-gate  */
13700Sstevel@tonic-gate static void
13710Sstevel@tonic-gate freeConnection(Connection *con)
13720Sstevel@tonic-gate {
13730Sstevel@tonic-gate 	if (con == NULL)
13740Sstevel@tonic-gate 		return;
13750Sstevel@tonic-gate 	if (con->serverAddr)
13760Sstevel@tonic-gate 		free(con->serverAddr);
13770Sstevel@tonic-gate 	if (con->auth)
13780Sstevel@tonic-gate 		(void) __ns_ldap_freeCred(&(con->auth));
13790Sstevel@tonic-gate 	if (con->saslMechanisms) {
13800Sstevel@tonic-gate 		__s_api_free2dArray(con->saslMechanisms);
13810Sstevel@tonic-gate 	}
13820Sstevel@tonic-gate 	if (con->controls) {
13830Sstevel@tonic-gate 		__s_api_free2dArray(con->controls);
13840Sstevel@tonic-gate 	}
13850Sstevel@tonic-gate 	free(con);
13860Sstevel@tonic-gate }
13870Sstevel@tonic-gate 
13880Sstevel@tonic-gate /*
13890Sstevel@tonic-gate  * Find a connection matching the passed in criteria.  If an open
13900Sstevel@tonic-gate  * connection with that criteria exists use it, otherwise open a
13910Sstevel@tonic-gate  * new connection.
13920Sstevel@tonic-gate  * Success: returns the pointer to the Connection structure
13930Sstevel@tonic-gate  * Failure: returns NULL, error code and message should be in errorp
13940Sstevel@tonic-gate  */
13951687Sjanga 
13960Sstevel@tonic-gate static int
13970Sstevel@tonic-gate makeConnection(Connection **conp, const char *serverAddr,
13980Sstevel@tonic-gate 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
13991179Svv149972 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
14002830Sdjl 	int nopasswd_acct_mgmt, int flags, char ***badsrvrs)
14010Sstevel@tonic-gate {
14020Sstevel@tonic-gate 	Connection *con = NULL;
14030Sstevel@tonic-gate 	ConnectionID id;
14040Sstevel@tonic-gate 	char errmsg[MAXERROR];
14050Sstevel@tonic-gate 	int rc, exit_rc = NS_LDAP_SUCCESS;
14060Sstevel@tonic-gate 	ns_server_info_t sinfo;
14070Sstevel@tonic-gate 	char *hReq, *host = NULL;
14080Sstevel@tonic-gate 	LDAP *ld = NULL;
14090Sstevel@tonic-gate 	int passwd_mgmt = 0;
14101687Sjanga 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
14112830Sdjl 	short	memerr = 0; /* Variable for tracking memory allocation */
14124522Schinlong 	char *serverAddrType = NULL, **bindHost = NULL;
14132830Sdjl 
14140Sstevel@tonic-gate 
14150Sstevel@tonic-gate 	if (conp == NULL || errorp == NULL || auth == NULL)
14160Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
14170Sstevel@tonic-gate 	*errorp = NULL;
14180Sstevel@tonic-gate 	*conp = NULL;
14194522Schinlong 	(void) memset(&sinfo, 0, sizeof (sinfo));
14200Sstevel@tonic-gate 
14214048Schinlong 	if ((wait4session == 0 || sessionTid != thr_self()) &&
14224522Schinlong 	    (id = findConnection(flags, serverAddr, auth, &con)) != -1) {
14230Sstevel@tonic-gate 		/* connection found in cache */
14240Sstevel@tonic-gate #ifdef DEBUG
14252830Sdjl 		(void) fprintf(stderr, "tid= %d: connection found in "
14264522Schinlong 		    "cache %d\n", thr_self(), id);
14270Sstevel@tonic-gate 		fflush(stderr);
14280Sstevel@tonic-gate #endif /* DEBUG */
14290Sstevel@tonic-gate 		*cID = id;
14300Sstevel@tonic-gate 		*conp = con;
14310Sstevel@tonic-gate 		return (NS_LDAP_SUCCESS);
14320Sstevel@tonic-gate 	}
14330Sstevel@tonic-gate 
14344522Schinlong 	if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
14352830Sdjl 		serverAddrType = NS_CACHE_ADDR_HOSTNAME;
14364522Schinlong 		bindHost = &sinfo.serverFQDN;
14374522Schinlong 	} else {
14382830Sdjl 		serverAddrType = NS_CACHE_ADDR_IP;
14394522Schinlong 		bindHost = &sinfo.server;
14404522Schinlong 	}
14412830Sdjl 
14420Sstevel@tonic-gate 	if (serverAddr) {
14435559Ssdussud 		/*
14445559Ssdussud 		 * We're given the server address, just use it.
14455559Ssdussud 		 * In case of sasl/GSSAPI, serverAddr would need to be a FQDN.
14465559Ssdussud 		 * We assume this is the case for now.
14475559Ssdussud 		 *
14485559Ssdussud 		 * Only the server address fields of sinfo structure are filled
14495559Ssdussud 		 * in since these are the only relevant data that we have. Other
14505559Ssdussud 		 * fields of this structure (controls, saslMechanisms) are
14515559Ssdussud 		 * kept to NULL.
14525559Ssdussud 		 */
14535559Ssdussud 		sinfo.server = strdup(serverAddr);
14545559Ssdussud 		if (sinfo.server == NULL)  {
14555559Ssdussud 			return (NS_LDAP_MEMORY);
14565559Ssdussud 		}
14575559Ssdussud 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
14585559Ssdussud 			sinfo.serverFQDN = strdup(serverAddr);
14595559Ssdussud 			if (sinfo.serverFQDN == NULL) {
14605559Ssdussud 				free(sinfo.server);
14615559Ssdussud 				return (NS_LDAP_MEMORY);
14625559Ssdussud 			}
14632830Sdjl 		}
14644522Schinlong 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
14654522Schinlong 		    fail_if_new_pwd_reqd, passwd_mgmt);
14660Sstevel@tonic-gate 		if (rc == NS_LDAP_SUCCESS || rc ==
14674522Schinlong 		    NS_LDAP_SUCCESS_WITH_INFO) {
14680Sstevel@tonic-gate 			exit_rc = rc;
14690Sstevel@tonic-gate 			goto create_con;
14700Sstevel@tonic-gate 		} else {
14715559Ssdussud 			if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
14725559Ssdussud 				(void) snprintf(errmsg, sizeof (errmsg),
14735559Ssdussud 				    "%s %s", gettext("makeConnection: "
14745559Ssdussud 				    "failed to open connection using "
14755559Ssdussud 				    "sasl/GSSAPI to"), *bindHost);
14765559Ssdussud 			} else {
14775559Ssdussud 				(void) snprintf(errmsg, sizeof (errmsg),
14785559Ssdussud 				    "%s %s", gettext("makeConnection: "
14795559Ssdussud 				    "failed to open connection to"),
14805559Ssdussud 				    *bindHost);
14815559Ssdussud 			}
14825559Ssdussud 			syslog(LOG_ERR, "libsldap: %s", errmsg);
14835559Ssdussud 			__s_api_free_server_info(&sinfo);
14840Sstevel@tonic-gate 			return (rc);
14850Sstevel@tonic-gate 		}
14860Sstevel@tonic-gate 	}
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	/* No cached connection, create one */
14890Sstevel@tonic-gate 	for (; ; ) {
14900Sstevel@tonic-gate 		if (host == NULL)
14910Sstevel@tonic-gate 			hReq = NS_CACHE_NEW;
14920Sstevel@tonic-gate 		else
14930Sstevel@tonic-gate 			hReq = NS_CACHE_NEXT;
14942830Sdjl 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
14954522Schinlong 		    serverAddrType);
14960Sstevel@tonic-gate 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
14974522Schinlong 		    (host && (strcasecmp(host, sinfo.server) == 0))) {
14980Sstevel@tonic-gate 			/* Log the error */
14990Sstevel@tonic-gate 			if (*errorp) {
15000Sstevel@tonic-gate 				(void) snprintf(errmsg, sizeof (errmsg),
15010Sstevel@tonic-gate 				"%s: (%s)", gettext("makeConnection: "
15020Sstevel@tonic-gate 				"unable to make LDAP connection, "
15030Sstevel@tonic-gate 				"request for a server failed"),
15040Sstevel@tonic-gate 				    (*errorp)->message);
15050Sstevel@tonic-gate 				syslog(LOG_ERR, "libsldap: %s", errmsg);
15060Sstevel@tonic-gate 			}
15070Sstevel@tonic-gate 
15084522Schinlong 			__s_api_free_server_info(&sinfo);
15090Sstevel@tonic-gate 			if (host)
15100Sstevel@tonic-gate 				free(host);
15110Sstevel@tonic-gate 			return (NS_LDAP_OP_FAILED);
15120Sstevel@tonic-gate 		}
15130Sstevel@tonic-gate 		if (host)
15140Sstevel@tonic-gate 			free(host);
15150Sstevel@tonic-gate 		host = strdup(sinfo.server);
15160Sstevel@tonic-gate 		if (host == NULL) {
15174522Schinlong 			__s_api_free_server_info(&sinfo);
15180Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
15190Sstevel@tonic-gate 		}
15200Sstevel@tonic-gate 
15210Sstevel@tonic-gate 		/* check if server supports password management */
15220Sstevel@tonic-gate 		passwd_mgmt = __s_api_contain_passwd_control_oid(
15234522Schinlong 		    sinfo.controls);
15241179Svv149972 		/* check if server supports password less account mgmt */
15251179Svv149972 		if (nopasswd_acct_mgmt &&
15264522Schinlong 		    !__s_api_contain_account_usable_control_oid(
15274522Schinlong 		    sinfo.controls)) {
15281179Svv149972 			syslog(LOG_WARNING, "libsldap: server %s does not "
15294522Schinlong 			    "provide account information without password",
15304522Schinlong 			    host);
15311179Svv149972 			free(host);
15324522Schinlong 			__s_api_free_server_info(&sinfo);
15331179Svv149972 			return (NS_LDAP_OP_FAILED);
15341179Svv149972 		}
15350Sstevel@tonic-gate 		/* make the connection */
15364522Schinlong 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
15374522Schinlong 		    fail_if_new_pwd_reqd, passwd_mgmt);
15380Sstevel@tonic-gate 		/* if success, go to create connection structure */
15390Sstevel@tonic-gate 		if (rc == NS_LDAP_SUCCESS ||
15404522Schinlong 		    rc == NS_LDAP_SUCCESS_WITH_INFO) {
15410Sstevel@tonic-gate 			exit_rc = rc;
15420Sstevel@tonic-gate 			break;
15430Sstevel@tonic-gate 		}
15440Sstevel@tonic-gate 
15450Sstevel@tonic-gate 		/*
15460Sstevel@tonic-gate 		 * If not able to reach the server, inform the ldap
15470Sstevel@tonic-gate 		 * cache manager that the server should be removed
15480Sstevel@tonic-gate 		 * from its server list. Thus, the manager will not
15490Sstevel@tonic-gate 		 * return this server on the next get-server request
15500Sstevel@tonic-gate 		 * and will also reduce the server list refresh TTL,
15510Sstevel@tonic-gate 		 * so that it will find out sooner when the server
15520Sstevel@tonic-gate 		 * is up again.
15530Sstevel@tonic-gate 		 */
15540Sstevel@tonic-gate 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
15550Sstevel@tonic-gate 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
15564522Schinlong 			    (*errorp)->status == LDAP_SERVER_DOWN) {
15571687Sjanga 				/* Reset memory allocation error */
15581687Sjanga 				memerr = 0;
15591687Sjanga 				/*
15601687Sjanga 				 * We contacted a server that we could
15611687Sjanga 				 * not either authenticate to or contact.
15621687Sjanga 				 * If it is due to authentication, then
15631687Sjanga 				 * we need to try the server again. So,
15641687Sjanga 				 * do not remove the server yet, but
15651687Sjanga 				 * add it to the bad server list.
15661687Sjanga 				 * The caller routine will remove
15671687Sjanga 				 * the servers if:
15681687Sjanga 				 *	a). A good server is found or
15691687Sjanga 				 *	b). All the possible methods
15701687Sjanga 				 *	    are tried without finding
15711687Sjanga 				 *	    a good server
15721687Sjanga 				 */
15731687Sjanga 				if (*badsrvrs == NULL) {
15744522Schinlong 					if (!(*badsrvrs = (char **)malloc
15754522Schinlong 					    (sizeof (char *) * NUMTOMALLOC))) {
15764522Schinlong 						memerr = 1;
15774522Schinlong 					}
15781687Sjanga 				/* Allocate memory in chunks of NUMTOMALLOC */
15791687Sjanga 				} else if ((totalbad % NUMTOMALLOC) ==
15804522Schinlong 				    NUMTOMALLOC - 1) {
15814522Schinlong 					char **tmpptr;
15824522Schinlong 					if (!(tmpptr = (char **)realloc(
15834522Schinlong 					    *badsrvrs,
15841687Sjanga 					    (sizeof (char *) * NUMTOMALLOC *
15851687Sjanga 					    ((totalbad/NUMTOMALLOC) + 2))))) {
15864522Schinlong 						memerr = 1;
15874522Schinlong 					} else {
15884522Schinlong 						*badsrvrs = tmpptr;
15894522Schinlong 					}
1590493Ssdussud 				}
15911687Sjanga 				/*
15921687Sjanga 				 * Store host only if there were no unsuccessful
15931687Sjanga 				 * memory allocations above
15941687Sjanga 				 */
15951687Sjanga 				if (!memerr &&
15961687Sjanga 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
15971687Sjanga 					memerr = 1;
15981687Sjanga 					totalbad--;
15991687Sjanga 				}
16001687Sjanga 				(*badsrvrs)[totalbad] = NULL;
1601493Ssdussud 			}
16020Sstevel@tonic-gate 		}
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 		/* else, cleanup and go for the next server */
16054522Schinlong 		__s_api_free_server_info(&sinfo);
16064522Schinlong 
16071687Sjanga 		/* Return if we had memory allocation errors */
16081687Sjanga 		if (memerr)
16091687Sjanga 			return (NS_LDAP_MEMORY);
16100Sstevel@tonic-gate 		if (*errorp) {
16110Sstevel@tonic-gate 			/*
16120Sstevel@tonic-gate 			 * If openConnection() failed due to
16130Sstevel@tonic-gate 			 * password policy, or invalid credential,
16140Sstevel@tonic-gate 			 * keep *errorp and exit
16150Sstevel@tonic-gate 			 */
16160Sstevel@tonic-gate 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
16170Sstevel@tonic-gate 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
16180Sstevel@tonic-gate 				free(host);
16190Sstevel@tonic-gate 				return (rc);
16200Sstevel@tonic-gate 			} else {
16210Sstevel@tonic-gate 				(void) __ns_ldap_freeError(errorp);
16220Sstevel@tonic-gate 				*errorp = NULL;
16230Sstevel@tonic-gate 			}
16240Sstevel@tonic-gate 		}
16250Sstevel@tonic-gate 	}
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate create_con:
16280Sstevel@tonic-gate 	/* we have created ld, setup con structure */
16290Sstevel@tonic-gate 	if (host)
16300Sstevel@tonic-gate 		free(host);
16310Sstevel@tonic-gate 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
16324522Schinlong 		__s_api_free_server_info(&sinfo);
16330Sstevel@tonic-gate 		/*
16340Sstevel@tonic-gate 		 * If password control attached in **errorp,
16350Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
16360Sstevel@tonic-gate 		 * free the error structure
16370Sstevel@tonic-gate 		 */
16380Sstevel@tonic-gate 		if (*errorp) {
16390Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
16400Sstevel@tonic-gate 			*errorp = NULL;
16410Sstevel@tonic-gate 		}
16425559Ssdussud 		(void) ldap_unbind(ld);
16430Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
16440Sstevel@tonic-gate 	}
16450Sstevel@tonic-gate 
16464522Schinlong 	con->serverAddr = sinfo.server; /* Store original format */
16474522Schinlong 	if (sinfo.serverFQDN != NULL) {
16484522Schinlong 		free(sinfo.serverFQDN);
16494522Schinlong 		sinfo.serverFQDN = NULL;
16504522Schinlong 	}
16510Sstevel@tonic-gate 	con->saslMechanisms = sinfo.saslMechanisms;
16520Sstevel@tonic-gate 	con->controls = sinfo.controls;
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate 	con->auth = __ns_ldap_dupAuth(auth);
16550Sstevel@tonic-gate 	if (con->auth == NULL) {
16565559Ssdussud 		(void) ldap_unbind(ld);
16575559Ssdussud 		freeConnection(con);
16580Sstevel@tonic-gate 		/*
16590Sstevel@tonic-gate 		 * If password control attached in **errorp,
16600Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
16610Sstevel@tonic-gate 		 * free the error structure
16620Sstevel@tonic-gate 		 */
16630Sstevel@tonic-gate 		if (*errorp) {
16640Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
16650Sstevel@tonic-gate 			*errorp = NULL;
16660Sstevel@tonic-gate 		}
16670Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
16680Sstevel@tonic-gate 	}
16690Sstevel@tonic-gate 
16700Sstevel@tonic-gate 	con->threadID = thr_self();
16713387Schinlong 	con->pid = getpid();
16722830Sdjl 
16730Sstevel@tonic-gate 	con->ld = ld;
16740Sstevel@tonic-gate 	if ((id = addConnection(con)) == -1) {
16755559Ssdussud 		(void) ldap_unbind(ld);
16760Sstevel@tonic-gate 		freeConnection(con);
16770Sstevel@tonic-gate 		/*
16780Sstevel@tonic-gate 		 * If password control attached in **errorp,
16790Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
16800Sstevel@tonic-gate 		 * free the error structure
16810Sstevel@tonic-gate 		 */
16820Sstevel@tonic-gate 		if (*errorp) {
16830Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
16840Sstevel@tonic-gate 			*errorp = NULL;
16850Sstevel@tonic-gate 		}
16860Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
16870Sstevel@tonic-gate 	}
16880Sstevel@tonic-gate #ifdef DEBUG
16892830Sdjl 	(void) fprintf(stderr, "tid= %d: connection added into "
16904522Schinlong 	    "cache %d\n", thr_self(), id);
16910Sstevel@tonic-gate 	fflush(stderr);
16920Sstevel@tonic-gate #endif /* DEBUG */
16930Sstevel@tonic-gate 	*cID = id;
16940Sstevel@tonic-gate 	*conp = con;
16950Sstevel@tonic-gate 	return (exit_rc);
16960Sstevel@tonic-gate }
16970Sstevel@tonic-gate 
16980Sstevel@tonic-gate /*
16990Sstevel@tonic-gate  * Return the specified connection to the pool.  If necessary
17000Sstevel@tonic-gate  * delete the connection.
17010Sstevel@tonic-gate  */
17020Sstevel@tonic-gate 
17030Sstevel@tonic-gate static void
17040Sstevel@tonic-gate _DropConnection(ConnectionID cID, int flag, int fini)
17050Sstevel@tonic-gate {
17060Sstevel@tonic-gate 	Connection *cp;
17070Sstevel@tonic-gate 	int id;
17082830Sdjl 	int use_lock = !fini;
1709*6722Smj162486 	struct timeval	zerotime;
1710*6722Smj162486 	LDAPMessage	*res;
1711*6722Smj162486 
1712*6722Smj162486 	zerotime.tv_sec = zerotime.tv_usec = 0L;
1713*6722Smj162486 
17142830Sdjl #ifdef DEBUG
17152830Sdjl 	thread_t t = thr_self();
17162830Sdjl #endif /* DEBUG */
17170Sstevel@tonic-gate 
17180Sstevel@tonic-gate 	id = cID - CONID_OFFSET;
17190Sstevel@tonic-gate 	if (id < 0 || id >= sessionPoolSize)
17200Sstevel@tonic-gate 		return;
17210Sstevel@tonic-gate #ifdef DEBUG
17222830Sdjl 	(void) fprintf(stderr, "tid= %d: "
17234522Schinlong 	    "Dropping connection cID=%d flag=0x%x, fini = %d\n",
17244522Schinlong 	    t, cID, flag, fini);
17250Sstevel@tonic-gate 	fflush(stderr);
17260Sstevel@tonic-gate #endif /* DEBUG */
17272830Sdjl 	if (use_lock)
17282830Sdjl 		(void) rw_wrlock(&sessionPoolLock);
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 	cp = sessionPool[id];
17310Sstevel@tonic-gate 	/* sanity check before removing */
17324048Schinlong 	if (!cp || (!fini && !cp->shared && !cp->usedBit)) {
17332830Sdjl #ifdef DEBUG
17342830Sdjl 		if (cp == NULL)
17352830Sdjl 			(void) fprintf(stderr, "tid= %d: no "
17364522Schinlong 			    "need to remove (fini = %d, cp = %p)\n", t,
17374522Schinlong 			    fini, cp);
17382830Sdjl 		else
17392830Sdjl 			(void) fprintf(stderr, "tid= %d: no "
17404522Schinlong 			    "need to remove (fini = %d, cp = %p, shared = %d)"
17414522Schinlong 			    "\n", t, fini, cp, cp->shared);
17422830Sdjl 		fflush(stderr);
17432830Sdjl #endif /* DEBUG */
17442830Sdjl 		if (use_lock)
17452830Sdjl 			(void) rw_unlock(&sessionPoolLock);
17460Sstevel@tonic-gate 		return;
17470Sstevel@tonic-gate 	}
17480Sstevel@tonic-gate 
17490Sstevel@tonic-gate 	if (!fini &&
17504522Schinlong 	    ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail &&
17516072Smj162486 	    ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc() ||
17526072Smj162486 	    __s_api_peruser_proc())) {
17532830Sdjl #ifdef DEBUG
17542830Sdjl 		(void) fprintf(stderr, "tid= %d: keep alive (fini = %d "
17554522Schinlong 		    "shared = %d)\n", t, fini, cp->shared);
17562830Sdjl #endif /* DEBUG */
17570Sstevel@tonic-gate 		/* release Connection (keep alive) */
17582830Sdjl 		if (cp->shared)
17592830Sdjl 			cp->shared--;
17600Sstevel@tonic-gate 		cp->usedBit = B_FALSE;
17610Sstevel@tonic-gate 		cp->threadID = 0;	/* unmark the threadID */
1762*6722Smj162486 		/*
1763*6722Smj162486 		 * Do sanity cleanup of remaining results if connection is not
1764*6722Smj162486 		 * shared by any requests.
1765*6722Smj162486 		 */
1766*6722Smj162486 		if (cp->shared <= 0) {
1767*6722Smj162486 			while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL,
1768*6722Smj162486 			    &zerotime, &res) > 0) {
1769*6722Smj162486 				if (res != NULL)
1770*6722Smj162486 					(void) ldap_msgfree(res);
1771*6722Smj162486 			}
1772*6722Smj162486 		}
17732830Sdjl 		if (use_lock)
17742830Sdjl 			(void) rw_unlock(&sessionPoolLock);
17750Sstevel@tonic-gate 	} else {
17760Sstevel@tonic-gate 		/* delete Connection (disconnect) */
17772830Sdjl 		if (cp->shared > 0) {
17782830Sdjl #ifdef DEBUG
17792830Sdjl 		(void) fprintf(stderr, "tid= %d: Connection no "
17804522Schinlong 		    "longer available (fini = %d, shared = %d)\n",
17814522Schinlong 		    t, fini, cp->shared);
17822830Sdjl 		fflush(stderr);
17832830Sdjl #endif /* DEBUG */
17842830Sdjl 			cp->shared--;
17853387Schinlong 			/*
17863387Schinlong 			 * Mark this connection not available and decrement
17873387Schinlong 			 * sharedConnNumber. There could be multiple threads
17883387Schinlong 			 * sharing this connection so decrement
17893387Schinlong 			 * sharedConnNumber only once per connection.
17903387Schinlong 			 */
17913387Schinlong 			if (cp->notAvail == 0) {
17923387Schinlong 				cp->notAvail = 1;
17933387Schinlong 				(void) mutex_lock(&sharedConnNumberLock);
17943387Schinlong 				sharedConnNumber--;
17953387Schinlong 				(void) mutex_unlock(&sharedConnNumberLock);
17963387Schinlong 			}
17972830Sdjl 		}
17982830Sdjl 
17992830Sdjl 		if (cp->shared <= 0) {
18002830Sdjl #ifdef DEBUG
18012830Sdjl 			(void) fprintf(stderr, "tid= %d: unbind "
18024522Schinlong 			    "(fini = %d, shared = %d)\n",
18034522Schinlong 			    t, fini, cp->shared);
18042830Sdjl 			fflush(stderr);
18052830Sdjl #endif /* DEBUG */
18062830Sdjl 			sessionPool[id] = NULL;
18072830Sdjl 			(void) ldap_unbind(cp->ld);
18082830Sdjl 			freeConnection(cp);
18092830Sdjl 		}
18102830Sdjl 
18112830Sdjl 		if (use_lock)
18122830Sdjl 			(void) rw_unlock(&sessionPoolLock);
18130Sstevel@tonic-gate 	}
18140Sstevel@tonic-gate }
18150Sstevel@tonic-gate 
18160Sstevel@tonic-gate void
18170Sstevel@tonic-gate DropConnection(ConnectionID cID, int flag)
18180Sstevel@tonic-gate {
18190Sstevel@tonic-gate 	_DropConnection(cID, flag, 0);
18200Sstevel@tonic-gate }
18210Sstevel@tonic-gate 
18220Sstevel@tonic-gate /*
18230Sstevel@tonic-gate  * This routine is called after a bind operation is
18240Sstevel@tonic-gate  * done in openConnection() to process the password
18250Sstevel@tonic-gate  * management information, if any.
18260Sstevel@tonic-gate  *
18270Sstevel@tonic-gate  * Input:
18280Sstevel@tonic-gate  *   bind_type: "simple" or "sasl/DIGEST-MD5"
18290Sstevel@tonic-gate  *   ldaprc   : ldap rc from the ldap bind operation
18300Sstevel@tonic-gate  *   controls : controls returned by the server
18310Sstevel@tonic-gate  *   errmsg   : error message from the server
18320Sstevel@tonic-gate  *   fail_if_new_pwd_reqd:
18330Sstevel@tonic-gate  *              flag indicating if connection should be open
18340Sstevel@tonic-gate  *              when password needs to change immediately
18350Sstevel@tonic-gate  *   passwd_mgmt:
18360Sstevel@tonic-gate  *              flag indicating if server supports password
18370Sstevel@tonic-gate  *              policy/management
18380Sstevel@tonic-gate  *
18390Sstevel@tonic-gate  * Output     : ns_ldap_error structure, which may contain
18400Sstevel@tonic-gate  *              password status and number of seconds until
18410Sstevel@tonic-gate  *              expired
18420Sstevel@tonic-gate  *
18430Sstevel@tonic-gate  * return rc:
18440Sstevel@tonic-gate  * NS_LDAP_EXTERNAL: error, connection should not open
18450Sstevel@tonic-gate  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
18460Sstevel@tonic-gate  * NS_LDAP_SUCCESS: OK to open connection
18470Sstevel@tonic-gate  *
18480Sstevel@tonic-gate  */
18490Sstevel@tonic-gate 
18500Sstevel@tonic-gate static int
18510Sstevel@tonic-gate process_pwd_mgmt(char *bind_type, int ldaprc,
18520Sstevel@tonic-gate 		LDAPControl **controls,
18530Sstevel@tonic-gate 		char *errmsg, ns_ldap_error_t **errorp,
18540Sstevel@tonic-gate 		int fail_if_new_pwd_reqd,
18550Sstevel@tonic-gate 		int passwd_mgmt)
18560Sstevel@tonic-gate {
18570Sstevel@tonic-gate 	char		errstr[MAXERROR];
18580Sstevel@tonic-gate 	LDAPControl	**ctrl = NULL;
18590Sstevel@tonic-gate 	int		exit_rc;
18600Sstevel@tonic-gate 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
18610Sstevel@tonic-gate 	int		sec_until_exp = 0;
18620Sstevel@tonic-gate 
18630Sstevel@tonic-gate 	/*
18640Sstevel@tonic-gate 	 * errmsg may be an empty string,
18650Sstevel@tonic-gate 	 * even if ldaprc is LDAP_SUCCESS,
18660Sstevel@tonic-gate 	 * free the empty string if that's the case
18670Sstevel@tonic-gate 	 */
18680Sstevel@tonic-gate 	if (errmsg &&
18694522Schinlong 	    (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
18700Sstevel@tonic-gate 		ldap_memfree(errmsg);
18710Sstevel@tonic-gate 		errmsg = NULL;
18720Sstevel@tonic-gate 	}
18730Sstevel@tonic-gate 
18740Sstevel@tonic-gate 	if (ldaprc != LDAP_SUCCESS) {
18750Sstevel@tonic-gate 		/*
18760Sstevel@tonic-gate 		 * try to map ldap rc and error message to
18770Sstevel@tonic-gate 		 * a password status
18780Sstevel@tonic-gate 		 */
18790Sstevel@tonic-gate 		if (errmsg) {
18800Sstevel@tonic-gate 			if (passwd_mgmt)
18810Sstevel@tonic-gate 				pwd_status =
18824522Schinlong 				    __s_api_set_passwd_status(
18834522Schinlong 				    ldaprc, errmsg);
18840Sstevel@tonic-gate 			ldap_memfree(errmsg);
18850Sstevel@tonic-gate 		}
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate 		(void) snprintf(errstr, sizeof (errstr),
18884522Schinlong 		    gettext("openConnection: "
18894522Schinlong 		    "%s bind failed "
18904522Schinlong 		    "- %s"), bind_type, ldap_err2string(ldaprc));
18910Sstevel@tonic-gate 
18920Sstevel@tonic-gate 		if (pwd_status != NS_PASSWD_GOOD) {
18930Sstevel@tonic-gate 			MKERROR_PWD_MGMT(*errorp,
18944522Schinlong 			    ldaprc, strdup(errstr),
18954522Schinlong 			    pwd_status, 0, NULL);
18960Sstevel@tonic-gate 		} else {
18970Sstevel@tonic-gate 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
18984522Schinlong 			    NULL);
18990Sstevel@tonic-gate 		}
19000Sstevel@tonic-gate 		if (controls)
19010Sstevel@tonic-gate 			ldap_controls_free(controls);
19020Sstevel@tonic-gate 
19030Sstevel@tonic-gate 		return (NS_LDAP_INTERNAL);
19040Sstevel@tonic-gate 	}
19050Sstevel@tonic-gate 
19060Sstevel@tonic-gate 	/*
19070Sstevel@tonic-gate 	 * ldaprc is LDAP_SUCCESS,
19080Sstevel@tonic-gate 	 * process the password management controls, if any
19090Sstevel@tonic-gate 	 */
19100Sstevel@tonic-gate 	exit_rc = NS_LDAP_SUCCESS;
19110Sstevel@tonic-gate 	if (controls && passwd_mgmt) {
19120Sstevel@tonic-gate 		/*
19130Sstevel@tonic-gate 		 * The control with the OID
19140Sstevel@tonic-gate 		 * 2.16.840.1.113730.3.4.4 (or
19150Sstevel@tonic-gate 		 * LDAP_CONTROL_PWEXPIRED, as defined
19160Sstevel@tonic-gate 		 * in the ldap.h header file) is the
19170Sstevel@tonic-gate 		 * expired password control.
19180Sstevel@tonic-gate 		 *
19190Sstevel@tonic-gate 		 * This control is used if the server
19200Sstevel@tonic-gate 		 * is configured to require users to
19210Sstevel@tonic-gate 		 * change their passwords when first
19220Sstevel@tonic-gate 		 * logging in and whenever the
19230Sstevel@tonic-gate 		 * passwords are reset.
19240Sstevel@tonic-gate 		 *
19250Sstevel@tonic-gate 		 * If the user is logging in for the
19260Sstevel@tonic-gate 		 * first time or if the user's
19270Sstevel@tonic-gate 		 * password has been reset, the
19280Sstevel@tonic-gate 		 * server sends this control to
19290Sstevel@tonic-gate 		 * indicate that the client needs to
19300Sstevel@tonic-gate 		 * change the password immediately.
19310Sstevel@tonic-gate 		 *
19320Sstevel@tonic-gate 		 * At this point, the only operation
19330Sstevel@tonic-gate 		 * that the client can perform is to
19340Sstevel@tonic-gate 		 * change the user's password. If the
19350Sstevel@tonic-gate 		 * client requests any other LDAP
19360Sstevel@tonic-gate 		 * operation, the server sends back
19370Sstevel@tonic-gate 		 * an LDAP_UNWILLING_TO_PERFORM
19380Sstevel@tonic-gate 		 * result code with an expired
19390Sstevel@tonic-gate 		 * password control.
19400Sstevel@tonic-gate 		 *
19410Sstevel@tonic-gate 		 * The control with the OID
19420Sstevel@tonic-gate 		 * 2.16.840.1.113730.3.4.5 (or
19430Sstevel@tonic-gate 		 * LDAP_CONTROL_PWEXPIRING, as
19440Sstevel@tonic-gate 		 * defined in the ldap.h header file)
19450Sstevel@tonic-gate 		 * is the password expiration warning
19460Sstevel@tonic-gate 		 * control.
19470Sstevel@tonic-gate 		 *
19480Sstevel@tonic-gate 		 * This control is used if the server
19490Sstevel@tonic-gate 		 * is configured to expire user
19500Sstevel@tonic-gate 		 * passwords after a certain amount
19510Sstevel@tonic-gate 		 * of time.
19520Sstevel@tonic-gate 		 *
19530Sstevel@tonic-gate 		 * The server sends this control back
19540Sstevel@tonic-gate 		 * to the client if the client binds
19550Sstevel@tonic-gate 		 * using a password that will soon
19560Sstevel@tonic-gate 		 * expire.  The ldctl_value field of
19570Sstevel@tonic-gate 		 * the LDAPControl structure
19580Sstevel@tonic-gate 		 * specifies the number of seconds
19590Sstevel@tonic-gate 		 * before the password will expire.
19600Sstevel@tonic-gate 		 */
19610Sstevel@tonic-gate 		for (ctrl = controls; *ctrl; ctrl++) {
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate 			if (strcmp((*ctrl)->ldctl_oid,
19644522Schinlong 			    LDAP_CONTROL_PWEXPIRED) == 0) {
19650Sstevel@tonic-gate 				/*
19660Sstevel@tonic-gate 				 * if the caller wants this bind
19670Sstevel@tonic-gate 				 * to fail, set up the error info.
19680Sstevel@tonic-gate 				 * If call to this function is
19690Sstevel@tonic-gate 				 * for searching the LDAP directory,
19700Sstevel@tonic-gate 				 * e.g., __ns_ldap_list(),
19710Sstevel@tonic-gate 				 * there's really no sense to
19720Sstevel@tonic-gate 				 * let a connection open and
19730Sstevel@tonic-gate 				 * then fail immediately afterward
19740Sstevel@tonic-gate 				 * on the LDAP search operation with
19750Sstevel@tonic-gate 				 * the LDAP_UNWILLING_TO_PERFORM rc
19760Sstevel@tonic-gate 				 */
19770Sstevel@tonic-gate 				pwd_status =
19784522Schinlong 				    NS_PASSWD_CHANGE_NEEDED;
19790Sstevel@tonic-gate 				if (fail_if_new_pwd_reqd) {
19800Sstevel@tonic-gate 					(void) snprintf(errstr,
19814522Schinlong 					    sizeof (errstr),
19824522Schinlong 					    gettext(
19834522Schinlong 					    "openConnection: "
19844522Schinlong 					    "%s bind "
19854522Schinlong 					    "failed "
19864522Schinlong 					    "- password "
19874522Schinlong 					    "expired. It "
19884522Schinlong 					    " needs to change "
19894522Schinlong 					    "immediately!"),
19904522Schinlong 					    bind_type);
19910Sstevel@tonic-gate 					MKERROR_PWD_MGMT(*errorp,
19924522Schinlong 					    LDAP_SUCCESS,
19934522Schinlong 					    strdup(errstr),
19944522Schinlong 					    pwd_status,
19954522Schinlong 					    0,
19964522Schinlong 					    NULL);
19970Sstevel@tonic-gate 					exit_rc = NS_LDAP_INTERNAL;
19980Sstevel@tonic-gate 				} else {
19990Sstevel@tonic-gate 					MKERROR_PWD_MGMT(*errorp,
20004522Schinlong 					    LDAP_SUCCESS,
20014522Schinlong 					    NULL,
20024522Schinlong 					    pwd_status,
20034522Schinlong 					    0,
20044522Schinlong 					    NULL);
20050Sstevel@tonic-gate 					exit_rc =
20064522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO;
20070Sstevel@tonic-gate 				}
20080Sstevel@tonic-gate 				break;
20090Sstevel@tonic-gate 			} else if (strcmp((*ctrl)->ldctl_oid,
20104522Schinlong 			    LDAP_CONTROL_PWEXPIRING) == 0) {
20110Sstevel@tonic-gate 				pwd_status =
20124522Schinlong 				    NS_PASSWD_ABOUT_TO_EXPIRE;
20130Sstevel@tonic-gate 				if ((*ctrl)->
20144522Schinlong 				    ldctl_value.bv_len > 0 &&
20154522Schinlong 				    (*ctrl)->
20164522Schinlong 				    ldctl_value.bv_val)
20170Sstevel@tonic-gate 					sec_until_exp =
20184522Schinlong 					    atoi((*ctrl)->
20194522Schinlong 					    ldctl_value.bv_val);
20200Sstevel@tonic-gate 				MKERROR_PWD_MGMT(*errorp,
20214522Schinlong 				    LDAP_SUCCESS,
20224522Schinlong 				    NULL,
20234522Schinlong 				    pwd_status,
20244522Schinlong 				    sec_until_exp,
20254522Schinlong 				    NULL);
20260Sstevel@tonic-gate 				exit_rc =
20274522Schinlong 				    NS_LDAP_SUCCESS_WITH_INFO;
20280Sstevel@tonic-gate 				break;
20290Sstevel@tonic-gate 			}
20300Sstevel@tonic-gate 		}
20310Sstevel@tonic-gate 	}
20320Sstevel@tonic-gate 
20330Sstevel@tonic-gate 	if (controls)
20340Sstevel@tonic-gate 		ldap_controls_free(controls);
20350Sstevel@tonic-gate 
20360Sstevel@tonic-gate 	return (exit_rc);
20370Sstevel@tonic-gate }
20380Sstevel@tonic-gate 
20390Sstevel@tonic-gate static int
20400Sstevel@tonic-gate ldap_in_hosts_switch()
20410Sstevel@tonic-gate {
20420Sstevel@tonic-gate 	enum __nsw_parse_err		pserr;
20430Sstevel@tonic-gate 	struct __nsw_switchconfig	*conf;
20440Sstevel@tonic-gate 	struct __nsw_lookup		*lkp;
20450Sstevel@tonic-gate 	const char			*name;
20460Sstevel@tonic-gate 	int				found = 0;
20470Sstevel@tonic-gate 
20480Sstevel@tonic-gate 	conf = __nsw_getconfig("hosts", &pserr);
20490Sstevel@tonic-gate 	if (conf == NULL) {
20500Sstevel@tonic-gate 		return (-1);
20510Sstevel@tonic-gate 	}
20520Sstevel@tonic-gate 
20530Sstevel@tonic-gate 	/* check for skip and count other backends */
20540Sstevel@tonic-gate 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
20550Sstevel@tonic-gate 		name = lkp->service_name;
20560Sstevel@tonic-gate 		if (strcmp(name, "ldap") == 0) {
20570Sstevel@tonic-gate 			found = 1;
20580Sstevel@tonic-gate 			break;
20590Sstevel@tonic-gate 		}
20600Sstevel@tonic-gate 	}
20610Sstevel@tonic-gate 	__nsw_freeconfig(conf);
20620Sstevel@tonic-gate 	return (found);
20630Sstevel@tonic-gate }
20640Sstevel@tonic-gate 
20650Sstevel@tonic-gate static int
20660Sstevel@tonic-gate openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
20670Sstevel@tonic-gate 	int timeoutSec, ns_ldap_error_t **errorp,
20680Sstevel@tonic-gate 	int fail_if_new_pwd_reqd, int passwd_mgmt)
20690Sstevel@tonic-gate {
20700Sstevel@tonic-gate 	LDAP		*ld = NULL;
20710Sstevel@tonic-gate 	char		*binddn, *passwd;
20720Sstevel@tonic-gate 	char		*digest_md5_name;
20730Sstevel@tonic-gate 	const char	*s;
20740Sstevel@tonic-gate 	int		ldapVersion = LDAP_VERSION3;
20750Sstevel@tonic-gate 	int		derefOption = LDAP_DEREF_ALWAYS;
20760Sstevel@tonic-gate 	int		zero = 0;
20770Sstevel@tonic-gate 	int		rc;
20780Sstevel@tonic-gate 	char		errstr[MAXERROR];
20790Sstevel@tonic-gate 	int		errnum = 0;
20800Sstevel@tonic-gate 	LDAPMessage	*resultMsg;
20810Sstevel@tonic-gate 	int		msgId;
20822830Sdjl 	int		useSSL = 0, port = 0;
20830Sstevel@tonic-gate 	struct timeval	tv;
20840Sstevel@tonic-gate 	AuthType_t	bindType;
20850Sstevel@tonic-gate 	int		timeoutMilliSec = timeoutSec * 1000;
20860Sstevel@tonic-gate 	struct berval	cred;
20870Sstevel@tonic-gate 	char		*sslServerAddr;
20880Sstevel@tonic-gate 	char		*s1;
20892830Sdjl 	char		*errmsg, *end = NULL;
20900Sstevel@tonic-gate 	LDAPControl	**controls;
20912830Sdjl 	int		pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
20922830Sdjl 	ns_sasl_cb_param_t	sasl_param;
20930Sstevel@tonic-gate 
20940Sstevel@tonic-gate 	*errorp = NULL;
20950Sstevel@tonic-gate 	*ldp = NULL;
20960Sstevel@tonic-gate 
20970Sstevel@tonic-gate 	switch (auth->auth.type) {
20980Sstevel@tonic-gate 		case NS_LDAP_AUTH_NONE:
20990Sstevel@tonic-gate 		case NS_LDAP_AUTH_SIMPLE:
21000Sstevel@tonic-gate 		case NS_LDAP_AUTH_SASL:
21010Sstevel@tonic-gate 			bindType = auth->auth.type;
21020Sstevel@tonic-gate 			break;
21030Sstevel@tonic-gate 		case NS_LDAP_AUTH_TLS:
21040Sstevel@tonic-gate 			useSSL = 1;
21050Sstevel@tonic-gate 			switch (auth->auth.tlstype) {
21060Sstevel@tonic-gate 				case NS_LDAP_TLS_NONE:
21070Sstevel@tonic-gate 					bindType = NS_LDAP_AUTH_NONE;
21080Sstevel@tonic-gate 					break;
21090Sstevel@tonic-gate 				case NS_LDAP_TLS_SIMPLE:
21100Sstevel@tonic-gate 					bindType = NS_LDAP_AUTH_SIMPLE;
21110Sstevel@tonic-gate 					break;
21120Sstevel@tonic-gate 				case NS_LDAP_TLS_SASL:
21130Sstevel@tonic-gate 					bindType = NS_LDAP_AUTH_SASL;
21140Sstevel@tonic-gate 					break;
21150Sstevel@tonic-gate 				default:
21160Sstevel@tonic-gate 					(void) sprintf(errstr,
21170Sstevel@tonic-gate 					gettext("openConnection: unsupported "
21184522Schinlong 					    "TLS authentication method "
21194522Schinlong 					    "(%d)"), auth->auth.tlstype);
21200Sstevel@tonic-gate 					MKERROR(LOG_WARNING, *errorp,
21214522Schinlong 					    LDAP_AUTH_METHOD_NOT_SUPPORTED,
21224522Schinlong 					    strdup(errstr), NULL);
21230Sstevel@tonic-gate 					return (NS_LDAP_INTERNAL);
21240Sstevel@tonic-gate 			}
21250Sstevel@tonic-gate 			break;
21260Sstevel@tonic-gate 		default:
21270Sstevel@tonic-gate 			(void) sprintf(errstr,
21284522Schinlong 			    gettext("openConnection: unsupported "
21294522Schinlong 			    "authentication method (%d)"), auth->auth.type);
21300Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp,
21314522Schinlong 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
21324522Schinlong 			    NULL);
21330Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
21340Sstevel@tonic-gate 	}
21350Sstevel@tonic-gate 
21360Sstevel@tonic-gate 	if (useSSL) {
21370Sstevel@tonic-gate 		const char	*hostcertpath;
21380Sstevel@tonic-gate 		char		*alloc_hcp = NULL;
21390Sstevel@tonic-gate #ifdef DEBUG
21402830Sdjl 		(void) fprintf(stderr, "tid= %d: +++TLS transport\n",
21414522Schinlong 		    thr_self());
21420Sstevel@tonic-gate #endif /* DEBUG */
21432333Sjanga 
21442333Sjanga 		if (prldap_set_session_option(NULL, NULL,
21452333Sjanga 		    PRLDAP_OPT_IO_MAX_TIMEOUT,
21462333Sjanga 		    timeoutMilliSec) != LDAP_SUCCESS) {
21472333Sjanga 			(void) snprintf(errstr, sizeof (errstr),
21484522Schinlong 			    gettext("openConnection: failed to initialize "
21494522Schinlong 			    "TLS security"));
21502333Sjanga 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
21514522Schinlong 			    strdup(errstr), NULL);
21522333Sjanga 			return (NS_LDAP_INTERNAL);
21532333Sjanga 		}
21542333Sjanga 
21550Sstevel@tonic-gate 		hostcertpath = auth->hostcertpath;
21560Sstevel@tonic-gate 		if (hostcertpath == NULL) {
21570Sstevel@tonic-gate 			alloc_hcp = __s_get_hostcertpath();
21580Sstevel@tonic-gate 			hostcertpath = alloc_hcp;
21590Sstevel@tonic-gate 		}
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 		if (hostcertpath == NULL)
21620Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
21630Sstevel@tonic-gate 
21640Sstevel@tonic-gate 		if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
21650Sstevel@tonic-gate 			if (alloc_hcp)
21660Sstevel@tonic-gate 				free(alloc_hcp);
21670Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
21684522Schinlong 			    gettext("openConnection: failed to initialize "
21694522Schinlong 			    "TLS security (%s)"),
21704522Schinlong 			    ldapssl_err2string(rc));
21710Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
21724522Schinlong 			    strdup(errstr), NULL);
21730Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
21740Sstevel@tonic-gate 		}
21750Sstevel@tonic-gate 		if (alloc_hcp)
21760Sstevel@tonic-gate 			free(alloc_hcp);
21770Sstevel@tonic-gate 
21780Sstevel@tonic-gate 		/* determine if the host name contains a port number */
21790Sstevel@tonic-gate 		s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
21800Sstevel@tonic-gate 		if (s == NULL)
21810Sstevel@tonic-gate 			s = serverAddr;
21820Sstevel@tonic-gate 		s = strchr(s, ':');
21830Sstevel@tonic-gate 		if (s != NULL) {
21840Sstevel@tonic-gate 			/*
21850Sstevel@tonic-gate 			 * If we do get a port number, we will try stripping
21860Sstevel@tonic-gate 			 * it. At present, referrals will always have a
21870Sstevel@tonic-gate 			 * port number.
21880Sstevel@tonic-gate 			 */
21890Sstevel@tonic-gate 			sslServerAddr = strdup(serverAddr);
21900Sstevel@tonic-gate 			if (sslServerAddr == NULL)
21910Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
21920Sstevel@tonic-gate 			s1 = strrchr(sslServerAddr, ':');
21930Sstevel@tonic-gate 			if (s1 != NULL)
21940Sstevel@tonic-gate 				*s1 = '\0';
21950Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
21960Sstevel@tonic-gate 			    gettext("openConnection: cannot use tls with %s. "
21974522Schinlong 			    "Trying %s"),
21984522Schinlong 			    serverAddr, sslServerAddr);
21990Sstevel@tonic-gate 			syslog(LOG_ERR, "libsldap: %s", errstr);
22000Sstevel@tonic-gate 		} else
22010Sstevel@tonic-gate 			sslServerAddr = (char *)serverAddr;
22020Sstevel@tonic-gate 
22030Sstevel@tonic-gate 		ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
22040Sstevel@tonic-gate 
22050Sstevel@tonic-gate 		if (sslServerAddr != serverAddr)
22060Sstevel@tonic-gate 			free(sslServerAddr);
22070Sstevel@tonic-gate 
22080Sstevel@tonic-gate 		if (ld == NULL ||
22090Sstevel@tonic-gate 		    ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
22100Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
22114522Schinlong 			    gettext("openConnection: failed to connect "
22124522Schinlong 			    "using TLS (%s)"), strerror(errno));
22130Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
22144522Schinlong 			    strdup(errstr), NULL);
22150Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
22160Sstevel@tonic-gate 		}
22170Sstevel@tonic-gate 	} else {
22180Sstevel@tonic-gate #ifdef DEBUG
22192830Sdjl 		(void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
22204522Schinlong 		    thr_self());
22210Sstevel@tonic-gate #endif /* DEBUG */
22222830Sdjl 		port = LDAP_PORT;
22232830Sdjl 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI &&
22244522Schinlong 		    (end = strchr(serverAddr, ':')) != NULL) {
22252830Sdjl 			/*
22262830Sdjl 			 * The IP is converted to hostname so it's a
22272830Sdjl 			 * hostname:port up to this point.
22282830Sdjl 			 *
22292830Sdjl 			 * libldap passes hostname:port to the sasl layer.
22302830Sdjl 			 * The ldap service principal is constructed as
22312830Sdjl 			 * ldap/hostname:port@REALM. Kerberos authentication
22322830Sdjl 			 * will fail. So it needs to be parsed to construct
22332830Sdjl 			 * a valid principal ldap/hostname@REALM.
22342830Sdjl 			 *
22352830Sdjl 			 * For useSSL case above, it already parses port so
22362830Sdjl 			 * no need to parse serverAddr
22372830Sdjl 			 */
22382830Sdjl 			*end = '\0';
22392830Sdjl 			port = atoi(end + 1);
22402830Sdjl 		}
22412830Sdjl 
22422830Sdjl 		/* Warning message IF cannot connect to host(s) */
22432830Sdjl 		if ((ld = ldap_init((char *)serverAddr, port)) == NULL) {
22440Sstevel@tonic-gate 			char *p = strerror(errno);
22450Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
22464522Schinlong 			    strdup(p), NULL);
22472830Sdjl 			if (end)
22482830Sdjl 				*end = ':';
22490Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
22500Sstevel@tonic-gate 		} else {
22512830Sdjl 			if (end)
22522830Sdjl 				*end = ':';
22530Sstevel@tonic-gate 			/* check and avoid gethostname recursion */
22540Sstevel@tonic-gate 			if (ldap_in_hosts_switch() > 0 &&
22554522Schinlong 			    ! __s_api_isipv4((char *)serverAddr) &&
22564522Schinlong 			    ! __s_api_isipv6((char *)serverAddr)) {
22570Sstevel@tonic-gate 				/* host: ldap - found, attempt to recover */
22580Sstevel@tonic-gate 				if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
22594522Schinlong 				    "ldap") != 0) {
22604522Schinlong 					(void) snprintf(errstr, sizeof (errstr),
22612830Sdjl 					    gettext("openConnection: "
22622830Sdjl 					    "unrecoverable gethostname "
22632830Sdjl 					    "recursion detected "
22642830Sdjl 					    "in /etc/nsswitch.conf"));
22654522Schinlong 					MKERROR(LOG_WARNING, *errorp,
22662830Sdjl 					    LDAP_CONNECT_ERROR,
22672830Sdjl 					    strdup(errstr), NULL);
22684522Schinlong 					(void) ldap_unbind(ld);
22694522Schinlong 					return (NS_LDAP_INTERNAL);
22700Sstevel@tonic-gate 				}
22710Sstevel@tonic-gate 			}
22720Sstevel@tonic-gate 		}
22730Sstevel@tonic-gate 	}
22740Sstevel@tonic-gate 
22752830Sdjl 	ns_setup_mt_conn_and_tsd(ld);
22760Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
22770Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
22780Sstevel@tonic-gate 	/*
22790Sstevel@tonic-gate 	 * set LDAP_OPT_REFERRALS to OFF.
22800Sstevel@tonic-gate 	 * This library will handle the referral itself
22810Sstevel@tonic-gate 	 * based on API flags or configuration file
22820Sstevel@tonic-gate 	 * specification. If this option is not set
22830Sstevel@tonic-gate 	 * to OFF, libldap will never pass the
22840Sstevel@tonic-gate 	 * referral info up to this library
22850Sstevel@tonic-gate 	 */
22860Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
22870Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
22880Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
22890Sstevel@tonic-gate 	/* setup TCP/IP connect timeout */
22900Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
22914522Schinlong 	    &timeoutMilliSec);
22920Sstevel@tonic-gate 	/* retry if LDAP I/O was interrupted */
22930Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
22940Sstevel@tonic-gate 
22950Sstevel@tonic-gate 	switch (bindType) {
22960Sstevel@tonic-gate 	case NS_LDAP_AUTH_NONE:
22970Sstevel@tonic-gate #ifdef DEBUG
22982830Sdjl 		(void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
22994522Schinlong 		    thr_self());
23000Sstevel@tonic-gate #endif /* DEBUG */
23010Sstevel@tonic-gate 		break;
23020Sstevel@tonic-gate 	case NS_LDAP_AUTH_SIMPLE:
23030Sstevel@tonic-gate 		binddn = auth->cred.unix_cred.userID;
23040Sstevel@tonic-gate 		passwd = auth->cred.unix_cred.passwd;
23050Sstevel@tonic-gate 		if (passwd == NULL || *passwd == '\0' ||
23060Sstevel@tonic-gate 		    binddn == NULL || *binddn == '\0') {
23070Sstevel@tonic-gate 			(void) sprintf(errstr, gettext("openConnection: "
23084522Schinlong 			    "missing credentials for Simple bind"));
23090Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
23104522Schinlong 			    strdup(errstr), NULL);
23110Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23120Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
23130Sstevel@tonic-gate 		}
23140Sstevel@tonic-gate 
23150Sstevel@tonic-gate #ifdef DEBUG
23162830Sdjl 		(void) fprintf(stderr, "tid= %d: +++Simple bind\n",
23174522Schinlong 		    thr_self());
23180Sstevel@tonic-gate #endif /* DEBUG */
23190Sstevel@tonic-gate 		msgId = ldap_simple_bind(ld, binddn, passwd);
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate 		if (msgId == -1) {
23220Sstevel@tonic-gate 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
23234522Schinlong 			    (void *)&errnum);
23240Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
23254522Schinlong 			    gettext("openConnection: simple bind failed "
23264522Schinlong 			    "- %s"), ldap_err2string(errnum));
23270Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23280Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
23294522Schinlong 			    NULL);
23300Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
23310Sstevel@tonic-gate 		}
23320Sstevel@tonic-gate 
23330Sstevel@tonic-gate 		tv.tv_sec = timeoutSec;
23340Sstevel@tonic-gate 		tv.tv_usec = 0;
23350Sstevel@tonic-gate 		rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
23360Sstevel@tonic-gate 
23370Sstevel@tonic-gate 		if ((rc == -1) || (rc == 0)) {
23380Sstevel@tonic-gate 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
23394522Schinlong 			    (void *)&errnum);
23400Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
23414522Schinlong 			    gettext("openConnection: simple bind failed "
23424522Schinlong 			    "- %s"), ldap_err2string(errnum));
23430Sstevel@tonic-gate 			(void) ldap_msgfree(resultMsg);
23440Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23450Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
23464522Schinlong 			    NULL);
23470Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
23480Sstevel@tonic-gate 		}
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 		/*
23510Sstevel@tonic-gate 		 * get ldaprc, controls, and error msg
23520Sstevel@tonic-gate 		 */
23530Sstevel@tonic-gate 		rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
23544522Schinlong 		    &errmsg, NULL, &controls, 1);
23550Sstevel@tonic-gate 
23560Sstevel@tonic-gate 		if (rc != LDAP_SUCCESS) {
23570Sstevel@tonic-gate 			(void) snprintf(errstr, sizeof (errstr),
23584522Schinlong 			    gettext("openConnection: simple bind failed "
23594522Schinlong 			    "- unable to parse result"));
23600Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23610Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
23624522Schinlong 			    strdup(errstr), NULL);
23630Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
23640Sstevel@tonic-gate 		}
23650Sstevel@tonic-gate 
23660Sstevel@tonic-gate 		/* process the password management info, if any */
23670Sstevel@tonic-gate 		pwd_rc = process_pwd_mgmt("simple",
23684522Schinlong 		    errnum, controls, errmsg,
23694522Schinlong 		    errorp,
23704522Schinlong 		    fail_if_new_pwd_reqd,
23714522Schinlong 		    passwd_mgmt);
23720Sstevel@tonic-gate 
23730Sstevel@tonic-gate 		if (pwd_rc == NS_LDAP_INTERNAL) {
23740Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23750Sstevel@tonic-gate 			return (pwd_rc);
23760Sstevel@tonic-gate 		}
23770Sstevel@tonic-gate 
23780Sstevel@tonic-gate 		if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
23790Sstevel@tonic-gate 			*ldp = ld;
23800Sstevel@tonic-gate 			return (pwd_rc);
23810Sstevel@tonic-gate 		}
23820Sstevel@tonic-gate 
23830Sstevel@tonic-gate 		break;
23840Sstevel@tonic-gate 	case NS_LDAP_AUTH_SASL:
23852830Sdjl 		if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
23864522Schinlong 		    auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
23870Sstevel@tonic-gate 			(void) sprintf(errstr,
23884522Schinlong 			    gettext("openConnection: SASL options are "
23894522Schinlong 			    "not supported (%d) for non-GSSAPI sasl bind"),
23904522Schinlong 			    auth->auth.saslopt);
23910Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp,
23924522Schinlong 			    LDAP_AUTH_METHOD_NOT_SUPPORTED,
23934522Schinlong 			    strdup(errstr), NULL);
23940Sstevel@tonic-gate 			(void) ldap_unbind(ld);
23950Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
23960Sstevel@tonic-gate 		}
23972830Sdjl 		if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
23982830Sdjl 			binddn = auth->cred.unix_cred.userID;
23992830Sdjl 			passwd = auth->cred.unix_cred.passwd;
24002830Sdjl 			if (passwd == NULL || *passwd == '\0' ||
24014522Schinlong 			    binddn == NULL || *binddn == '\0') {
24022830Sdjl 				(void) sprintf(errstr,
24034522Schinlong 				    gettext("openConnection: missing "
24044522Schinlong 				    "credentials for SASL bind"));
24052830Sdjl 				MKERROR(LOG_WARNING, *errorp,
24064522Schinlong 				    LDAP_INVALID_CREDENTIALS,
24074522Schinlong 				    strdup(errstr), NULL);
24082830Sdjl 				(void) ldap_unbind(ld);
24092830Sdjl 				return (NS_LDAP_INTERNAL);
24102830Sdjl 			}
24112830Sdjl 			cred.bv_val = passwd;
24122830Sdjl 			cred.bv_len = strlen(passwd);
24130Sstevel@tonic-gate 		}
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate 		switch (auth->auth.saslmech) {
24160Sstevel@tonic-gate 		case NS_LDAP_SASL_CRAM_MD5:
24170Sstevel@tonic-gate 			/*
24180Sstevel@tonic-gate 			 * NOTE: if iDS changes to support cram_md5,
24190Sstevel@tonic-gate 			 * please add password management code here.
24200Sstevel@tonic-gate 			 * Since ldap_sasl_cram_md5_bind_s does not
24210Sstevel@tonic-gate 			 * return anything that could be used to
24220Sstevel@tonic-gate 			 * extract the ldap rc/errmsg/control to
24230Sstevel@tonic-gate 			 * determine if bind failed due to password
24240Sstevel@tonic-gate 			 * policy, a new cram_md5_bind API will need
24250Sstevel@tonic-gate 			 * to be introduced. See
24260Sstevel@tonic-gate 			 * ldap_x_sasl_digest_md5_bind() and case
24270Sstevel@tonic-gate 			 * NS_LDAP_SASL_DIGEST_MD5 below for details.
24280Sstevel@tonic-gate 			 */
24290Sstevel@tonic-gate 			if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
24304522Schinlong 			    &cred, NULL, NULL)) != LDAP_SUCCESS) {
24310Sstevel@tonic-gate 				(void) ldap_get_option(ld,
24324522Schinlong 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
24330Sstevel@tonic-gate 				(void) snprintf(errstr, sizeof (errstr),
24344522Schinlong 				    gettext("openConnection: "
24354522Schinlong 				    "sasl/CRAM-MD5 bind failed - %s"),
24364522Schinlong 				    ldap_err2string(errnum));
24370Sstevel@tonic-gate 				MKERROR(LOG_WARNING, *errorp, errnum,
24384522Schinlong 				    strdup(errstr), NULL);
24390Sstevel@tonic-gate 				(void) ldap_unbind(ld);
24400Sstevel@tonic-gate 				return (NS_LDAP_INTERNAL);
24410Sstevel@tonic-gate 			}
24420Sstevel@tonic-gate 			break;
24430Sstevel@tonic-gate 		case NS_LDAP_SASL_DIGEST_MD5:
24440Sstevel@tonic-gate 			digest_md5_name = malloc(strlen(binddn) + 5);
24450Sstevel@tonic-gate 			/* 5 = strlen("dn: ") + 1 */
24460Sstevel@tonic-gate 			if (digest_md5_name == NULL) {
24470Sstevel@tonic-gate 				(void) ldap_unbind(ld);
24480Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
24490Sstevel@tonic-gate 			}
24500Sstevel@tonic-gate 			(void) strcpy(digest_md5_name, "dn: ");
24510Sstevel@tonic-gate 			(void) strcat(digest_md5_name, binddn);
24520Sstevel@tonic-gate 
24530Sstevel@tonic-gate 			tv.tv_sec = timeoutSec;
24540Sstevel@tonic-gate 			tv.tv_usec = 0;
24550Sstevel@tonic-gate 			rc = ldap_x_sasl_digest_md5_bind(ld,
24564522Schinlong 			    digest_md5_name, &cred, NULL, NULL,
24574522Schinlong 			    &tv, &resultMsg);
24580Sstevel@tonic-gate 
24590Sstevel@tonic-gate 			if (resultMsg == NULL) {
24600Sstevel@tonic-gate 				free(digest_md5_name);
24610Sstevel@tonic-gate 				(void) ldap_get_option(ld,
24624522Schinlong 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
24630Sstevel@tonic-gate 				(void) snprintf(errstr, sizeof (errstr),
24644522Schinlong 				    gettext("openConnection: "
24654522Schinlong 				    "DIGEST-MD5 bind failed - %s"),
24664522Schinlong 				    ldap_err2string(errnum));
24670Sstevel@tonic-gate 				(void) ldap_unbind(ld);
24680Sstevel@tonic-gate 				MKERROR(LOG_WARNING, *errorp, errnum,
24694522Schinlong 				    strdup(errstr), NULL);
24700Sstevel@tonic-gate 				return (NS_LDAP_INTERNAL);
24710Sstevel@tonic-gate 			}
24720Sstevel@tonic-gate 
24730Sstevel@tonic-gate 			/*
24740Sstevel@tonic-gate 			 * get ldaprc, controls, and error msg
24750Sstevel@tonic-gate 			 */
24760Sstevel@tonic-gate 			rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
24774522Schinlong 			    &errmsg, NULL, &controls, 1);
24780Sstevel@tonic-gate 
24790Sstevel@tonic-gate 			if (rc != LDAP_SUCCESS) {
24800Sstevel@tonic-gate 				free(digest_md5_name);
24810Sstevel@tonic-gate 				(void) snprintf(errstr, sizeof (errstr),
24824522Schinlong 				    gettext("openConnection: "
24834522Schinlong 				    "DIGEST-MD5 bind failed "
24844522Schinlong 				    "- unable to parse result"));
24850Sstevel@tonic-gate 				(void) ldap_unbind(ld);
24860Sstevel@tonic-gate 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
24874522Schinlong 				    strdup(errstr), NULL);
24880Sstevel@tonic-gate 				return (NS_LDAP_INTERNAL);
24890Sstevel@tonic-gate 			}
24900Sstevel@tonic-gate 
24910Sstevel@tonic-gate 			/* process the password management info, if any */
24920Sstevel@tonic-gate 			pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
24934522Schinlong 			    errnum, controls, errmsg,
24944522Schinlong 			    errorp,
24954522Schinlong 			    fail_if_new_pwd_reqd,
24964522Schinlong 			    passwd_mgmt);
24970Sstevel@tonic-gate 
24980Sstevel@tonic-gate 			if (pwd_rc == NS_LDAP_INTERNAL) {
24990Sstevel@tonic-gate 				free(digest_md5_name);
25000Sstevel@tonic-gate 				(void) ldap_unbind(ld);
25010Sstevel@tonic-gate 				return (pwd_rc);
25020Sstevel@tonic-gate 			}
25030Sstevel@tonic-gate 
25040Sstevel@tonic-gate 			if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
25050Sstevel@tonic-gate 				*ldp = ld;
25060Sstevel@tonic-gate 				return (pwd_rc);
25070Sstevel@tonic-gate 			}
25080Sstevel@tonic-gate 
25090Sstevel@tonic-gate 			free(digest_md5_name);
25100Sstevel@tonic-gate 			break;
25112830Sdjl 		case NS_LDAP_SASL_GSSAPI:
25122830Sdjl 			if (sasl_gssapi_inited == 0) {
25132830Sdjl 				rc = __s_api_sasl_gssapi_init();
25142830Sdjl 				if (rc != NS_LDAP_SUCCESS) {
25152830Sdjl 					(void) snprintf(errstr, sizeof (errstr),
25164522Schinlong 					    gettext("openConnection: "
25174522Schinlong 					    "GSSAPI initialization "
25184522Schinlong 					    "failed"));
25192830Sdjl 					(void) ldap_unbind(ld);
25202830Sdjl 					MKERROR(LOG_WARNING, *errorp, rc,
25214522Schinlong 					    strdup(errstr), NULL);
25222830Sdjl 					return (rc);
25232830Sdjl 				}
25242830Sdjl 			}
25252830Sdjl 			(void) memset(&sasl_param, 0,
25264522Schinlong 			    sizeof (ns_sasl_cb_param_t));
25272830Sdjl 			sasl_param.authid = NULL;
25282830Sdjl 			sasl_param.authzid = "";
25292830Sdjl 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
25304522Schinlong 			    (void *)&min_ssf);
25312830Sdjl 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
25324522Schinlong 			    (void *)&max_ssf);
25332830Sdjl 
25342830Sdjl 			rc = ldap_sasl_interactive_bind_s(
25354522Schinlong 			    ld, NULL, "GSSAPI",
25364522Schinlong 			    NULL, NULL, LDAP_SASL_INTERACTIVE,
25374522Schinlong 			    __s_api_sasl_bind_callback,
25384522Schinlong 			    &sasl_param);
25392830Sdjl 
25402830Sdjl 			if (rc != LDAP_SUCCESS) {
25412830Sdjl 				(void) snprintf(errstr, sizeof (errstr),
25424522Schinlong 				    gettext("openConnection: "
25434522Schinlong 				    "GSSAPI bind failed "
25444522Schinlong 				    "- %d %s"), rc, ldap_err2string(rc));
25452830Sdjl 				(void) ldap_unbind(ld);
25462830Sdjl 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
25474522Schinlong 				    strdup(errstr), NULL);
25482830Sdjl 				return (NS_LDAP_INTERNAL);
25492830Sdjl 			}
25502830Sdjl 
25512830Sdjl 			break;
25520Sstevel@tonic-gate 		default:
25530Sstevel@tonic-gate 			(void) ldap_unbind(ld);
25540Sstevel@tonic-gate 			(void) sprintf(errstr,
25554522Schinlong 			    gettext("openConnection: unsupported SASL "
25564522Schinlong 			    "mechanism (%d)"), auth->auth.saslmech);
25570Sstevel@tonic-gate 			MKERROR(LOG_WARNING, *errorp,
25584522Schinlong 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
25594522Schinlong 			    NULL);
25600Sstevel@tonic-gate 			return (NS_LDAP_INTERNAL);
25610Sstevel@tonic-gate 		}
25620Sstevel@tonic-gate 	}
25630Sstevel@tonic-gate 
25640Sstevel@tonic-gate 	*ldp = ld;
25650Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
25660Sstevel@tonic-gate }
25670Sstevel@tonic-gate 
25680Sstevel@tonic-gate /*
25690Sstevel@tonic-gate  * FUNCTION:	__s_api_getDefaultAuth
25700Sstevel@tonic-gate  *
25710Sstevel@tonic-gate  *	Constructs a credential for authentication using the config module.
25720Sstevel@tonic-gate  *
25730Sstevel@tonic-gate  * RETURN VALUES:
25740Sstevel@tonic-gate  *
25750Sstevel@tonic-gate  * NS_LDAP_SUCCESS	If successful
25760Sstevel@tonic-gate  * NS_LDAP_CONFIG	If there are any config errors.
25770Sstevel@tonic-gate  * NS_LDAP_MEMORY	Memory errors.
25780Sstevel@tonic-gate  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
25790Sstevel@tonic-gate  *			not build a new authp.
25800Sstevel@tonic-gate  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
25810Sstevel@tonic-gate  *			necessary fields of a cred for a given auth method
25820Sstevel@tonic-gate  *			are not provided.
25830Sstevel@tonic-gate  * INPUT:
25840Sstevel@tonic-gate  *
25850Sstevel@tonic-gate  * cLevel	Currently requested credential level to be tried
25860Sstevel@tonic-gate  *
25870Sstevel@tonic-gate  * aMethod	Currently requested authentication method to be tried
25880Sstevel@tonic-gate  *
25890Sstevel@tonic-gate  * OUTPUT:
25900Sstevel@tonic-gate  *
25910Sstevel@tonic-gate  * authp		authentication method to use.
25920Sstevel@tonic-gate  */
25930Sstevel@tonic-gate static int
25940Sstevel@tonic-gate __s_api_getDefaultAuth(
25950Sstevel@tonic-gate 	int	*cLevel,
25960Sstevel@tonic-gate 	ns_auth_t *aMethod,
25970Sstevel@tonic-gate 	ns_cred_t **authp)
25980Sstevel@tonic-gate {
25990Sstevel@tonic-gate 	void		**paramVal = NULL;
26000Sstevel@tonic-gate 	char		*modparamVal = NULL;
26010Sstevel@tonic-gate 	int		getUid = 0;
26020Sstevel@tonic-gate 	int		getPasswd = 0;
26030Sstevel@tonic-gate 	int		getCertpath = 0;
26040Sstevel@tonic-gate 	int		rc = 0;
26050Sstevel@tonic-gate 	ns_ldap_error_t	*errorp = NULL;
26060Sstevel@tonic-gate 
26070Sstevel@tonic-gate #ifdef DEBUG
26080Sstevel@tonic-gate 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
26090Sstevel@tonic-gate #endif
26100Sstevel@tonic-gate 
26110Sstevel@tonic-gate 	if (aMethod == NULL) {
26120Sstevel@tonic-gate 		/* Require an Auth */
26130Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
26140Sstevel@tonic-gate 
26150Sstevel@tonic-gate 	}
26160Sstevel@tonic-gate 	/*
26172830Sdjl 	 * credential level "self" can work with auth method sasl/GSSAPI only
26180Sstevel@tonic-gate 	 */
26192830Sdjl 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
26204522Schinlong 	    aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
26210Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
26220Sstevel@tonic-gate 
26230Sstevel@tonic-gate 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
26240Sstevel@tonic-gate 	if ((*authp) == NULL)
26250Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
26260Sstevel@tonic-gate 
26270Sstevel@tonic-gate 	(*authp)->auth = *aMethod;
26280Sstevel@tonic-gate 
26290Sstevel@tonic-gate 	switch (aMethod->type) {
26300Sstevel@tonic-gate 		case NS_LDAP_AUTH_NONE:
26310Sstevel@tonic-gate 			return (NS_LDAP_SUCCESS);
26320Sstevel@tonic-gate 		case NS_LDAP_AUTH_SIMPLE:
26330Sstevel@tonic-gate 			getUid++;
26340Sstevel@tonic-gate 			getPasswd++;
26350Sstevel@tonic-gate 			break;
26360Sstevel@tonic-gate 		case NS_LDAP_AUTH_SASL:
26370Sstevel@tonic-gate 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
26380Sstevel@tonic-gate 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
26390Sstevel@tonic-gate 				getUid++;
26400Sstevel@tonic-gate 				getPasswd++;
26412830Sdjl 			} else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
26420Sstevel@tonic-gate 				(void) __ns_ldap_freeCred(authp);
26430Sstevel@tonic-gate 				*authp = NULL;
26440Sstevel@tonic-gate 				return (NS_LDAP_INVALID_PARAM);
26450Sstevel@tonic-gate 			}
26460Sstevel@tonic-gate 			break;
26470Sstevel@tonic-gate 		case NS_LDAP_AUTH_TLS:
26480Sstevel@tonic-gate 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
26490Sstevel@tonic-gate 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
26500Sstevel@tonic-gate 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
26510Sstevel@tonic-gate 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
26520Sstevel@tonic-gate 				getUid++;
26530Sstevel@tonic-gate 				getPasswd++;
26540Sstevel@tonic-gate 				getCertpath++;
26550Sstevel@tonic-gate 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
26560Sstevel@tonic-gate 				getCertpath++;
26570Sstevel@tonic-gate 			} else {
26580Sstevel@tonic-gate 				(void) __ns_ldap_freeCred(authp);
26590Sstevel@tonic-gate 				*authp = NULL;
26600Sstevel@tonic-gate 				return (NS_LDAP_INVALID_PARAM);
26610Sstevel@tonic-gate 			}
26620Sstevel@tonic-gate 			break;
26630Sstevel@tonic-gate 	}
26640Sstevel@tonic-gate 
26650Sstevel@tonic-gate 	if (getUid) {
26660Sstevel@tonic-gate 		paramVal = NULL;
26670Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
26684522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
26690Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26700Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
26710Sstevel@tonic-gate 			*authp = NULL;
26720Sstevel@tonic-gate 			return (rc);
26730Sstevel@tonic-gate 		}
26740Sstevel@tonic-gate 
26750Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
26760Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26770Sstevel@tonic-gate 			*authp = NULL;
26780Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
26790Sstevel@tonic-gate 		}
26800Sstevel@tonic-gate 
26810Sstevel@tonic-gate 		(*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
26820Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
26830Sstevel@tonic-gate 		if ((*authp)->cred.unix_cred.userID == NULL) {
26840Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26850Sstevel@tonic-gate 			*authp = NULL;
26860Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
26870Sstevel@tonic-gate 		}
26880Sstevel@tonic-gate 	}
26890Sstevel@tonic-gate 	if (getPasswd) {
26900Sstevel@tonic-gate 		paramVal = NULL;
26910Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
26924522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
26930Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
26940Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
26950Sstevel@tonic-gate 			*authp = NULL;
26960Sstevel@tonic-gate 			return (rc);
26970Sstevel@tonic-gate 		}
26980Sstevel@tonic-gate 
26990Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
27000Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
27010Sstevel@tonic-gate 			*authp = NULL;
27020Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
27030Sstevel@tonic-gate 		}
27040Sstevel@tonic-gate 
27050Sstevel@tonic-gate 		modparamVal = dvalue((char *)*paramVal);
27060Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
27070Sstevel@tonic-gate 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
27080Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
27090Sstevel@tonic-gate 			if (modparamVal != NULL)
27100Sstevel@tonic-gate 				free(modparamVal);
27110Sstevel@tonic-gate 			*authp = NULL;
27120Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
27130Sstevel@tonic-gate 		}
27140Sstevel@tonic-gate 
27150Sstevel@tonic-gate 		(*authp)->cred.unix_cred.passwd = modparamVal;
27160Sstevel@tonic-gate 	}
27170Sstevel@tonic-gate 	if (getCertpath) {
27180Sstevel@tonic-gate 		paramVal = NULL;
27190Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
27204522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
27210Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
27220Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
27230Sstevel@tonic-gate 			*authp = NULL;
27240Sstevel@tonic-gate 			return (rc);
27250Sstevel@tonic-gate 		}
27260Sstevel@tonic-gate 
27270Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
27280Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
27290Sstevel@tonic-gate 			*authp = NULL;
27300Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
27310Sstevel@tonic-gate 		}
27320Sstevel@tonic-gate 
27330Sstevel@tonic-gate 		(*authp)->hostcertpath = strdup((char *)*paramVal);
27340Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
27350Sstevel@tonic-gate 		if ((*authp)->hostcertpath == NULL) {
27360Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
27370Sstevel@tonic-gate 			*authp = NULL;
27380Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
27390Sstevel@tonic-gate 		}
27400Sstevel@tonic-gate 	}
27410Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
27420Sstevel@tonic-gate }
27430Sstevel@tonic-gate 
27440Sstevel@tonic-gate /*
27450Sstevel@tonic-gate  * FUNCTION:	__s_api_getConnection
27460Sstevel@tonic-gate  *
27470Sstevel@tonic-gate  *	Bind to the specified server or one from the server
27480Sstevel@tonic-gate  *	list and return the pointer.
27490Sstevel@tonic-gate  *
27500Sstevel@tonic-gate  *	This function can rebind or not (NS_LDAP_HARD), it can require a
27510Sstevel@tonic-gate  *	credential or bind anonymously
27520Sstevel@tonic-gate  *
27530Sstevel@tonic-gate  *	This function follows the DUA configuration schema algorithm
27540Sstevel@tonic-gate  *
27550Sstevel@tonic-gate  * RETURN VALUES:
27560Sstevel@tonic-gate  *
27570Sstevel@tonic-gate  * NS_LDAP_SUCCESS	A connection was made successfully.
27580Sstevel@tonic-gate  * NS_LDAP_SUCCESS_WITH_INFO
27590Sstevel@tonic-gate  * 			A connection was made successfully, but with
27600Sstevel@tonic-gate  *			password management info in *errorp
27610Sstevel@tonic-gate  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
27620Sstevel@tonic-gate  * NS_LDAP_CONFIG	If there are any config errors.
27630Sstevel@tonic-gate  * NS_LDAP_MEMORY	Memory errors.
27640Sstevel@tonic-gate  * NS_LDAP_INTERNAL	If there was a ldap error.
27650Sstevel@tonic-gate  *
27660Sstevel@tonic-gate  * INPUT:
27670Sstevel@tonic-gate  *
27680Sstevel@tonic-gate  * server	Bind to this LDAP server only
27690Sstevel@tonic-gate  * flags	If NS_LDAP_HARD is set function will not return until it has
27700Sstevel@tonic-gate  *		a connection unless there is a authentication problem.
27710Sstevel@tonic-gate  *		If NS_LDAP_NEW_CONN is set the function must force a new
27720Sstevel@tonic-gate  *              connection to be created
27730Sstevel@tonic-gate  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
27740Sstevel@tonic-gate  * auth		Credentials for bind. This could be NULL in which case
27750Sstevel@tonic-gate  *		a default cred built from the config module is used.
27760Sstevel@tonic-gate  * sessionId	cookie that points to a previous session
27770Sstevel@tonic-gate  * fail_if_new_pwd_reqd
27780Sstevel@tonic-gate  *		a flag indicating this function should fail if the passwd
27790Sstevel@tonic-gate  *		in auth needs to change immediately
27801179Svv149972  * nopasswd_acct_mgmt
27811179Svv149972  *		a flag indicating that makeConnection should check before
27821179Svv149972  *		binding if server supports LDAP V3 password less
27831179Svv149972  *		account management
27840Sstevel@tonic-gate  *
27850Sstevel@tonic-gate  * OUTPUT:
27860Sstevel@tonic-gate  *
27870Sstevel@tonic-gate  * session	pointer to a session with connection information
27880Sstevel@tonic-gate  * errorp	Set if there are any INTERNAL, or CONFIG error.
27890Sstevel@tonic-gate  */
27900Sstevel@tonic-gate int
27910Sstevel@tonic-gate __s_api_getConnection(
27920Sstevel@tonic-gate 	const char *server,
27930Sstevel@tonic-gate 	const int flags,
27940Sstevel@tonic-gate 	const ns_cred_t *cred,		/* credentials for bind */
27950Sstevel@tonic-gate 	ConnectionID *sessionId,
27960Sstevel@tonic-gate 	Connection **session,
27970Sstevel@tonic-gate 	ns_ldap_error_t **errorp,
27981179Svv149972 	int fail_if_new_pwd_reqd,
27991179Svv149972 	int nopasswd_acct_mgmt)
28000Sstevel@tonic-gate {
28010Sstevel@tonic-gate 	char		errmsg[MAXERROR];
28020Sstevel@tonic-gate 	ns_auth_t	**aMethod = NULL;
28030Sstevel@tonic-gate 	ns_auth_t	**aNext = NULL;
28040Sstevel@tonic-gate 	int		**cLevel = NULL;
28050Sstevel@tonic-gate 	int		**cNext = NULL;
28060Sstevel@tonic-gate 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
28070Sstevel@tonic-gate 	int		rc;
28080Sstevel@tonic-gate 	Connection	*con = NULL;
28090Sstevel@tonic-gate 	int		sec = 1;
28100Sstevel@tonic-gate 	ns_cred_t 	*authp = NULL;
28110Sstevel@tonic-gate 	ns_cred_t	anon;
28122830Sdjl 	int		version = NS_LDAP_V2, self_gssapi_only = 0;
28130Sstevel@tonic-gate 	void		**paramVal = NULL;
28141687Sjanga 	char		**badSrvrs = NULL; /* List of problem hostnames */
28150Sstevel@tonic-gate 
28160Sstevel@tonic-gate 	if ((session == NULL) || (sessionId == NULL)) {
28170Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
28180Sstevel@tonic-gate 	}
28190Sstevel@tonic-gate 	*session = NULL;
28200Sstevel@tonic-gate 
28210Sstevel@tonic-gate 	/* if we already have a session id try to reuse connection */
28220Sstevel@tonic-gate 	if (*sessionId > 0) {
28230Sstevel@tonic-gate 		rc = findConnectionById(flags, cred, *sessionId, &con);
28240Sstevel@tonic-gate 		if (rc == *sessionId && con) {
28250Sstevel@tonic-gate 			*session = con;
28260Sstevel@tonic-gate 			return (NS_LDAP_SUCCESS);
28270Sstevel@tonic-gate 		}
28280Sstevel@tonic-gate 		*sessionId = 0;
28290Sstevel@tonic-gate 	}
28300Sstevel@tonic-gate 
28310Sstevel@tonic-gate 	/* get profile version number */
28320Sstevel@tonic-gate 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
28334522Schinlong 	    &paramVal, errorp)) != NS_LDAP_SUCCESS)
28340Sstevel@tonic-gate 		return (rc);
28350Sstevel@tonic-gate 	if (paramVal == NULL) {
28360Sstevel@tonic-gate 		(void) sprintf(errmsg, gettext("getConnection: no file "
28374522Schinlong 		    "version"));
28380Sstevel@tonic-gate 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
28394522Schinlong 		    NS_LDAP_CONFIG);
28400Sstevel@tonic-gate 		return (NS_LDAP_CONFIG);
28410Sstevel@tonic-gate 	}
28420Sstevel@tonic-gate 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
28430Sstevel@tonic-gate 		version = NS_LDAP_V1;
28440Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&paramVal);
28450Sstevel@tonic-gate 
28460Sstevel@tonic-gate 	/* Get the bind timeout value */
28470Sstevel@tonic-gate 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
28480Sstevel@tonic-gate 	if (paramVal != NULL && *paramVal != NULL) {
28490Sstevel@tonic-gate 		timeoutSec = **((int **)paramVal);
28500Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
28510Sstevel@tonic-gate 	}
28520Sstevel@tonic-gate 	if (*errorp)
28530Sstevel@tonic-gate 		(void) __ns_ldap_freeError(errorp);
28540Sstevel@tonic-gate 
28550Sstevel@tonic-gate 	if (cred == NULL) {
28560Sstevel@tonic-gate 		/* Get the authentication method list */
28570Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
28584522Schinlong 		    (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
28590Sstevel@tonic-gate 			return (rc);
28600Sstevel@tonic-gate 		if (aMethod == NULL) {
28610Sstevel@tonic-gate 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
28620Sstevel@tonic-gate 			if (aMethod == NULL)
28630Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
28640Sstevel@tonic-gate 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
28650Sstevel@tonic-gate 			if (aMethod[0] == NULL) {
28660Sstevel@tonic-gate 				free(aMethod);
28670Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
28680Sstevel@tonic-gate 			}
28690Sstevel@tonic-gate 			if (version == NS_LDAP_V1)
28700Sstevel@tonic-gate 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
28710Sstevel@tonic-gate 			else {
28720Sstevel@tonic-gate 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
28730Sstevel@tonic-gate 				(aMethod[0])->saslmech =
28744522Schinlong 				    NS_LDAP_SASL_DIGEST_MD5;
28750Sstevel@tonic-gate 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
28760Sstevel@tonic-gate 			}
28770Sstevel@tonic-gate 		}
28780Sstevel@tonic-gate 
28790Sstevel@tonic-gate 		/* Get the credential level list */
28800Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
28814522Schinlong 		    (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
28820Sstevel@tonic-gate 			(void) __ns_ldap_freeParam((void ***)&aMethod);
28830Sstevel@tonic-gate 			return (rc);
28840Sstevel@tonic-gate 		}
28850Sstevel@tonic-gate 		if (cLevel == NULL) {
28860Sstevel@tonic-gate 			cLevel = (int **)calloc(2, sizeof (int *));
28870Sstevel@tonic-gate 			if (cLevel == NULL)
28880Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
28890Sstevel@tonic-gate 			cLevel[0] = (int *)calloc(1, sizeof (int));
28900Sstevel@tonic-gate 			if (cLevel[0] == NULL)
28910Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
28920Sstevel@tonic-gate 			if (version == NS_LDAP_V1)
28930Sstevel@tonic-gate 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
28940Sstevel@tonic-gate 			else
28950Sstevel@tonic-gate 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
28960Sstevel@tonic-gate 		}
28970Sstevel@tonic-gate 	}
28980Sstevel@tonic-gate 
28990Sstevel@tonic-gate 	/* setup the anon credential for anonymous connection */
29000Sstevel@tonic-gate 	(void) memset(&anon, 0, sizeof (ns_cred_t));
29010Sstevel@tonic-gate 	anon.auth.type = NS_LDAP_AUTH_NONE;
29020Sstevel@tonic-gate 
29030Sstevel@tonic-gate 	for (; ; ) {
29040Sstevel@tonic-gate 		if (cred != NULL) {
29050Sstevel@tonic-gate 			/* using specified auth method */
29060Sstevel@tonic-gate 			rc = makeConnection(&con, server, cred,
29074522Schinlong 			    sessionId, timeoutSec, errorp,
29084522Schinlong 			    fail_if_new_pwd_reqd,
29094522Schinlong 			    nopasswd_acct_mgmt, flags, &badSrvrs);
29104387Smj162486 			/* not using bad server if credentials were supplied */
29114387Smj162486 			if (badSrvrs && *badSrvrs) {
29124387Smj162486 				__s_api_free2dArray(badSrvrs);
29134387Smj162486 				badSrvrs = NULL;
29144387Smj162486 			}
29150Sstevel@tonic-gate 			if (rc == NS_LDAP_SUCCESS ||
29164522Schinlong 			    rc == NS_LDAP_SUCCESS_WITH_INFO) {
29170Sstevel@tonic-gate 				*session = con;
29180Sstevel@tonic-gate 				break;
29190Sstevel@tonic-gate 			}
29200Sstevel@tonic-gate 		} else {
29212830Sdjl 			self_gssapi_only = __s_api_self_gssapi_only_get();
29220Sstevel@tonic-gate 			/* for every cred level */
29230Sstevel@tonic-gate 			for (cNext = cLevel; *cNext != NULL; cNext++) {
29242830Sdjl 				if (self_gssapi_only &&
29254522Schinlong 				    **cNext != NS_LDAP_CRED_SELF)
29262830Sdjl 					continue;
29270Sstevel@tonic-gate 				if (**cNext == NS_LDAP_CRED_ANON) {
29281687Sjanga 					/*
29291687Sjanga 					 * make connection anonymously
29301687Sjanga 					 * Free the down server list before
29311687Sjanga 					 * looping through
29321687Sjanga 					 */
29331687Sjanga 					if (badSrvrs && *badSrvrs) {
29341687Sjanga 						__s_api_free2dArray(badSrvrs);
29351687Sjanga 						badSrvrs = NULL;
29361687Sjanga 					}
29370Sstevel@tonic-gate 					rc = makeConnection(&con, server, &anon,
29384522Schinlong 					    sessionId, timeoutSec, errorp,
29394522Schinlong 					    fail_if_new_pwd_reqd,
29404522Schinlong 					    nopasswd_acct_mgmt, flags,
29414522Schinlong 					    &badSrvrs);
29420Sstevel@tonic-gate 					if (rc == NS_LDAP_SUCCESS ||
29434522Schinlong 					    rc ==
29444522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO) {
29450Sstevel@tonic-gate 						*session = con;
29460Sstevel@tonic-gate 						goto done;
29470Sstevel@tonic-gate 					}
29480Sstevel@tonic-gate 					continue;
29490Sstevel@tonic-gate 				}
29500Sstevel@tonic-gate 				/* for each cred level */
29510Sstevel@tonic-gate 				for (aNext = aMethod; *aNext != NULL; aNext++) {
29522830Sdjl 					if (self_gssapi_only &&
29534522Schinlong 					    (*aNext)->saslmech !=
29544522Schinlong 					    NS_LDAP_SASL_GSSAPI)
29552830Sdjl 						continue;
29562830Sdjl 					/*
29572830Sdjl 					 * self coexists with sasl/GSSAPI only
29582830Sdjl 					 * and non-self coexists with non-gssapi
29592830Sdjl 					 * only
29602830Sdjl 					 */
29612830Sdjl 					if ((**cNext == NS_LDAP_CRED_SELF &&
29624522Schinlong 					    (*aNext)->saslmech !=
29634522Schinlong 					    NS_LDAP_SASL_GSSAPI) ||
29644522Schinlong 					    (**cNext != NS_LDAP_CRED_SELF &&
29654522Schinlong 					    (*aNext)->saslmech ==
29664522Schinlong 					    NS_LDAP_SASL_GSSAPI))
29672830Sdjl 						continue;
29680Sstevel@tonic-gate 					/* make connection and authenticate */
29690Sstevel@tonic-gate 					/* with default credentials */
29700Sstevel@tonic-gate 					authp = NULL;
29710Sstevel@tonic-gate 					rc = __s_api_getDefaultAuth(*cNext,
29724522Schinlong 					    *aNext, &authp);
29730Sstevel@tonic-gate 					if (rc != NS_LDAP_SUCCESS) {
29740Sstevel@tonic-gate 						continue;
29750Sstevel@tonic-gate 					}
29761687Sjanga 					/*
29771687Sjanga 					 * Free the down server list before
29781687Sjanga 					 * looping through
29791687Sjanga 					 */
29801687Sjanga 					if (badSrvrs && *badSrvrs) {
29811687Sjanga 						__s_api_free2dArray(badSrvrs);
29821687Sjanga 						badSrvrs = NULL;
29831687Sjanga 					}
29840Sstevel@tonic-gate 					rc = makeConnection(&con, server, authp,
29854522Schinlong 					    sessionId, timeoutSec, errorp,
29864522Schinlong 					    fail_if_new_pwd_reqd,
29874522Schinlong 					    nopasswd_acct_mgmt, flags,
29884522Schinlong 					    &badSrvrs);
29890Sstevel@tonic-gate 					(void) __ns_ldap_freeCred(&authp);
29900Sstevel@tonic-gate 					if (rc == NS_LDAP_SUCCESS ||
29914522Schinlong 					    rc ==
29924522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO) {
29930Sstevel@tonic-gate 						*session = con;
29940Sstevel@tonic-gate 						goto done;
29950Sstevel@tonic-gate 					}
29960Sstevel@tonic-gate 				}
29970Sstevel@tonic-gate 			}
29980Sstevel@tonic-gate 		}
29990Sstevel@tonic-gate 		if (flags & NS_LDAP_HARD) {
30000Sstevel@tonic-gate 			if (sec < LDAPMAXHARDLOOKUPTIME)
30010Sstevel@tonic-gate 				sec *= 2;
30020Sstevel@tonic-gate 			_sleep(sec);
30030Sstevel@tonic-gate 		} else {
30040Sstevel@tonic-gate 			break;
30050Sstevel@tonic-gate 		}
30060Sstevel@tonic-gate 	}
30070Sstevel@tonic-gate 
30080Sstevel@tonic-gate done:
30092830Sdjl 	/*
30102830Sdjl 	 * If unable to get a connection, and this is
30112830Sdjl 	 * the thread opening the shared connection,
30122830Sdjl 	 * unlock the session mutex and let other
30132830Sdjl 	 * threads try to get their own connection.
30142830Sdjl 	 */
30152830Sdjl 	if (wait4session != 0 && sessionTid == thr_self()) {
30162830Sdjl 		wait4session = 0;
30172830Sdjl 		sessionTid = 0;
30182830Sdjl #ifdef DEBUG
30192830Sdjl 		(void) fprintf(stderr, "tid= %d: __s_api_getConnection: "
30204522Schinlong 		    "unlocking sessionLock \n", thr_self());
30212830Sdjl 		fflush(stderr);
30222830Sdjl #endif /* DEBUG */
30232830Sdjl 		(void) mutex_unlock(&sessionLock);
30242830Sdjl 	}
30252830Sdjl 	if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
30262830Sdjl 		/*
30272830Sdjl 		 * self_gssapi_only is true but no self/sasl/gssapi is
30282830Sdjl 		 * configured
30292830Sdjl 		 */
30302830Sdjl 		rc = NS_LDAP_CONFIG;
30312830Sdjl 	}
30322830Sdjl 
30330Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&aMethod);
30340Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&cLevel);
30351687Sjanga 
30361687Sjanga 	if (badSrvrs && *badSrvrs) {
30371687Sjanga 		/*
30381687Sjanga 		 * At this point, either we have a successful
30391687Sjanga 		 * connection or exhausted all the possible auths.
30401687Sjanga 		 * and creds. Mark the problem servers as down
30411687Sjanga 		 * so that the problem servers are not contacted
30421687Sjanga 		 * again until the refresh_ttl expires.
30431687Sjanga 		 */
30441687Sjanga 		(void) __s_api_removeBadServers(badSrvrs);
30451687Sjanga 		__s_api_free2dArray(badSrvrs);
30461687Sjanga 	}
30470Sstevel@tonic-gate 	return (rc);
30480Sstevel@tonic-gate }
30490Sstevel@tonic-gate 
30500Sstevel@tonic-gate #pragma fini(_free_sessionPool)
30510Sstevel@tonic-gate static void
30520Sstevel@tonic-gate _free_sessionPool()
30530Sstevel@tonic-gate {
30540Sstevel@tonic-gate 	int id;
30550Sstevel@tonic-gate 
30562830Sdjl 	(void) rw_wrlock(&sessionPoolLock);
30570Sstevel@tonic-gate 	if (sessionPool != NULL) {
30580Sstevel@tonic-gate 		for (id = 0; id < sessionPoolSize; id++)
30590Sstevel@tonic-gate 			_DropConnection(id + CONID_OFFSET, 0, 1);
30600Sstevel@tonic-gate 		free(sessionPool);
30610Sstevel@tonic-gate 		sessionPool = NULL;
30620Sstevel@tonic-gate 		sessionPoolSize = 0;
30630Sstevel@tonic-gate 	}
30642830Sdjl 	(void) rw_unlock(&sessionPoolLock);
30650Sstevel@tonic-gate }
3066