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  */
21*11411SSurya.Prakki@Sun.COM 
220Sstevel@tonic-gate /*
239576SJulian.Pullen@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #include <stdlib.h>
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <errno.h>
300Sstevel@tonic-gate #include <string.h>
310Sstevel@tonic-gate #include <synch.h>
320Sstevel@tonic-gate #include <time.h>
330Sstevel@tonic-gate #include <libintl.h>
340Sstevel@tonic-gate #include <thread.h>
350Sstevel@tonic-gate #include <syslog.h>
360Sstevel@tonic-gate #include <sys/mman.h>
370Sstevel@tonic-gate #include <nsswitch.h>
380Sstevel@tonic-gate #include <nss_dbdefs.h>
390Sstevel@tonic-gate #include "solaris-priv.h"
402830Sdjl #include "solaris-int.h"
410Sstevel@tonic-gate #include "ns_sldap.h"
420Sstevel@tonic-gate #include "ns_internal.h"
430Sstevel@tonic-gate #include "ns_cache_door.h"
446842Sth160488 #include "ns_connmgmt.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 
516842Sth160488 #define	USE_DEFAULT_PORT 0
526842Sth160488 
536842Sth160488 static ns_ldap_return_code performBind(const ns_cred_t *,
546842Sth160488 					LDAP *,
556842Sth160488 					int,
566842Sth160488 					ns_ldap_error_t **,
576842Sth160488 					int,
586842Sth160488 					int);
596842Sth160488 static ns_ldap_return_code createSession(const ns_cred_t *,
606842Sth160488 					const char *,
616842Sth160488 					uint16_t,
626842Sth160488 					int,
636842Sth160488 					LDAP **,
646842Sth160488 					ns_ldap_error_t **);
656842Sth160488 
660Sstevel@tonic-gate extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
670Sstevel@tonic-gate 		LDAPControl **, LDAPControl **);
680Sstevel@tonic-gate extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
690Sstevel@tonic-gate 
709576SJulian.Pullen@Sun.COM extern int __door_getconf(char **buffer, int *buflen,
719576SJulian.Pullen@Sun.COM 		ns_ldap_error_t **error, int callnumber);
729576SJulian.Pullen@Sun.COM extern int __ns_ldap_freeUnixCred(UnixCred_t **credp);
739576SJulian.Pullen@Sun.COM extern int SetDoorInfoToUnixCred(char *buffer,
749576SJulian.Pullen@Sun.COM 		ns_ldap_error_t **errorp,
759576SJulian.Pullen@Sun.COM 		UnixCred_t **cred);
769576SJulian.Pullen@Sun.COM 
770Sstevel@tonic-gate static int openConnection(LDAP **, const char *, const ns_cred_t *,
786842Sth160488 		int, ns_ldap_error_t **, int, int, ns_conn_user_t *);
794048Schinlong static void
804048Schinlong _DropConnection(ConnectionID cID, int flag, int fini);
816842Sth160488 
826842Sth160488 static mutex_t sessionPoolLock = DEFAULTMUTEX;
830Sstevel@tonic-gate 
840Sstevel@tonic-gate static Connection **sessionPool = NULL;
850Sstevel@tonic-gate static int sessionPoolSize = 0;
860Sstevel@tonic-gate 
872830Sdjl /*
882830Sdjl  * SSF values are for SASL integrity & privacy.
892830Sdjl  * JES DS5.2 does not support this feature but DS6 does.
902830Sdjl  * The values between 0 and 65535 can work with both server versions.
912830Sdjl  */
922830Sdjl #define	MAX_SASL_SSF	65535
932830Sdjl #define	MIN_SASL_SSF	0
940Sstevel@tonic-gate 
951687Sjanga /* Number of hostnames to allocate memory for */
961687Sjanga #define	NUMTOMALLOC	32
972830Sdjl 
982830Sdjl /*
996842Sth160488  * This function get the servers from the lists and returns
1006842Sth160488  * the first server with the empty lists of server controls and
1016842Sth160488  * SASL mechanisms. It is invoked if it is not possible to obtain a server
1026842Sth160488  * from ldap_cachemgr or the local list.
1032830Sdjl  */
1046842Sth160488 static
1056842Sth160488 ns_ldap_return_code
1066842Sth160488 getFirstFromConfig(ns_server_info_t *ret, ns_ldap_error_t **error)
1072830Sdjl {
1086842Sth160488 	char			**servers = NULL;
1096842Sth160488 	ns_ldap_return_code	ret_code;
1106842Sth160488 	char			errstr[MAXERROR];
1112830Sdjl 
1126842Sth160488 	/* get first server from config list unavailable otherwise */
1136842Sth160488 	ret_code = __s_api_getServers(&servers, error);
1146842Sth160488 	if (ret_code != NS_LDAP_SUCCESS) {
1156842Sth160488 		if (servers != NULL) {
1166842Sth160488 			__s_api_free2dArray(servers);
1176842Sth160488 		}
1186842Sth160488 		return (ret_code);
1192830Sdjl 	}
1202830Sdjl 
1216842Sth160488 	if (servers == NULL || servers[0] == NULL) {
1226842Sth160488 		__s_api_free2dArray(servers);
1236842Sth160488 		(void) sprintf(errstr,
1246842Sth160488 		    gettext("No server found in configuration"));
1256842Sth160488 		MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
1266842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
1276842Sth160488 		return (NS_LDAP_CONFIG);
1286072Smj162486 	}
1296072Smj162486 
1306842Sth160488 	ret->server = strdup(servers[0]);
1316842Sth160488 	if (ret->server == NULL) {
1326842Sth160488 		__s_api_free2dArray(servers);
1336842Sth160488 		return (NS_LDAP_MEMORY);
1346072Smj162486 	}
1356072Smj162486 
1366842Sth160488 	ret->saslMechanisms = NULL;
1376842Sth160488 	ret->controls = NULL;
1386072Smj162486 
1396842Sth160488 	__s_api_free2dArray(servers);
1406072Smj162486 
1416842Sth160488 	return (NS_LDAP_SUCCESS);
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate 
1449576SJulian.Pullen@Sun.COM /* very similar to __door_getldapconfig() in ns_config.c */
1459576SJulian.Pullen@Sun.COM static int
1469576SJulian.Pullen@Sun.COM __door_getadmincred(char **buffer, int *buflen, ns_ldap_error_t **error)
1479576SJulian.Pullen@Sun.COM {
1489576SJulian.Pullen@Sun.COM 	return (__door_getconf(buffer, buflen, error, GETADMINCRED));
1499576SJulian.Pullen@Sun.COM }
1509576SJulian.Pullen@Sun.COM 
1519576SJulian.Pullen@Sun.COM /*
1529576SJulian.Pullen@Sun.COM  * This function requests Admin credentials from the cache manager through
1539576SJulian.Pullen@Sun.COM  * the door functionality
1549576SJulian.Pullen@Sun.COM  */
1559576SJulian.Pullen@Sun.COM 
1569576SJulian.Pullen@Sun.COM static int
1579576SJulian.Pullen@Sun.COM requestAdminCred(UnixCred_t **cred, ns_ldap_error_t **error)
1589576SJulian.Pullen@Sun.COM {
1599576SJulian.Pullen@Sun.COM 	char	*buffer = NULL;
1609576SJulian.Pullen@Sun.COM 	int	buflen = 0;
1619576SJulian.Pullen@Sun.COM 	int	ret;
1629576SJulian.Pullen@Sun.COM 
1639576SJulian.Pullen@Sun.COM 	*error = NULL;
1649576SJulian.Pullen@Sun.COM 	ret = __door_getadmincred(&buffer, &buflen, error);
1659576SJulian.Pullen@Sun.COM 
1669576SJulian.Pullen@Sun.COM 	if (ret != NS_LDAP_SUCCESS) {
1679576SJulian.Pullen@Sun.COM 		if (*error != NULL && (*error)->message != NULL)
1689576SJulian.Pullen@Sun.COM 			syslog(LOG_WARNING, "libsldap: %s", (*error)->message);
1699576SJulian.Pullen@Sun.COM 		return (ret);
1709576SJulian.Pullen@Sun.COM 	}
1719576SJulian.Pullen@Sun.COM 
1729576SJulian.Pullen@Sun.COM 	/* now convert from door format */
1739576SJulian.Pullen@Sun.COM 	ret = SetDoorInfoToUnixCred(buffer, error, cred);
1749576SJulian.Pullen@Sun.COM 	free(buffer);
1759576SJulian.Pullen@Sun.COM 
1769576SJulian.Pullen@Sun.COM 	return (ret);
1779576SJulian.Pullen@Sun.COM }
1789576SJulian.Pullen@Sun.COM 
1790Sstevel@tonic-gate /*
1800Sstevel@tonic-gate  * This function requests a server from the cache manager through
1810Sstevel@tonic-gate  * the door functionality
1820Sstevel@tonic-gate  */
1830Sstevel@tonic-gate 
1846842Sth160488 int
1850Sstevel@tonic-gate __s_api_requestServer(const char *request, const char *server,
1862830Sdjl 	ns_server_info_t *ret, ns_ldap_error_t **error,  const char *addrType)
1870Sstevel@tonic-gate {
1880Sstevel@tonic-gate 	union {
1890Sstevel@tonic-gate 		ldap_data_t	s_d;
1900Sstevel@tonic-gate 		char		s_b[DOORBUFFERSIZE];
1910Sstevel@tonic-gate 	} space;
1926842Sth160488 	ldap_data_t		*sptr;
1936842Sth160488 	int			ndata;
1946842Sth160488 	int			adata;
1956842Sth160488 	char			errstr[MAXERROR];
1966842Sth160488 	const char		*ireq;
1976842Sth160488 	char			*rbuf, *ptr, *rest;
1986842Sth160488 	char			*dptr;
1996842Sth160488 	char			**mptr, **mptr1, **cptr, **cptr1;
2006842Sth160488 	int			mcnt, ccnt;
2016842Sth160488 	int			len;
2026842Sth160488 	ns_ldap_return_code	ret_code;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	if (ret == NULL || error == NULL) {
2050Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
2060Sstevel@tonic-gate 	}
2070Sstevel@tonic-gate 	(void) memset(ret, 0, sizeof (ns_server_info_t));
2080Sstevel@tonic-gate 	*error = NULL;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	if (request == NULL)
2110Sstevel@tonic-gate 		ireq = NS_CACHE_NEW;
2120Sstevel@tonic-gate 	else
2130Sstevel@tonic-gate 		ireq = request;
2140Sstevel@tonic-gate 
2156842Sth160488 	/*
2166842Sth160488 	 * In the 'Standalone' mode a server will be obtained
2176842Sth160488 	 * from the local libsldap's list
2186842Sth160488 	 */
2196842Sth160488 	if (__s_api_isStandalone()) {
2207281Smichen 		if ((ret_code = __s_api_findRootDSE(ireq,
2216842Sth160488 		    server,
2226842Sth160488 		    addrType,
2236842Sth160488 		    ret,
2247281Smichen 		    error)) != NS_LDAP_SUCCESS) {
2257281Smichen 			/*
2267281Smichen 			 * get first server from local list only once
2277281Smichen 			 * to prevent looping
2287281Smichen 			 */
2297281Smichen 			if (strcmp(ireq, NS_CACHE_NEW) != 0)
2307281Smichen 				return (ret_code);
2317281Smichen 
2326842Sth160488 			syslog(LOG_WARNING,
2336842Sth160488 			    "libsldap (\"standalone\" mode): "
2346842Sth160488 			    "can not find any available server. "
2356842Sth160488 			    "Return the first one from the lists");
2366842Sth160488 			if (*error != NULL) {
2376842Sth160488 				(void) __ns_ldap_freeError(error);
2386842Sth160488 			}
2396842Sth160488 
2406842Sth160488 			ret_code = getFirstFromConfig(ret, error);
2416842Sth160488 			if (ret_code != NS_LDAP_SUCCESS) {
2426842Sth160488 				return (ret_code);
2436842Sth160488 			}
2446842Sth160488 
2456842Sth160488 			if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
2466842Sth160488 				ret_code = __s_api_ip2hostname(ret->server,
2476842Sth160488 				    &ret->serverFQDN);
2486842Sth160488 				if (ret_code != NS_LDAP_SUCCESS) {
2496842Sth160488 					(void) snprintf(errstr,
2506842Sth160488 					    sizeof (errstr),
2516842Sth160488 					    gettext("The %s address "
2526842Sth160488 					    "can not be resolved into "
2536842Sth160488 					    "a host name. Returning "
2546842Sth160488 					    "the address as it is."),
2556842Sth160488 					    ret->server);
2566842Sth160488 					MKERROR(LOG_ERR,
2576842Sth160488 					    *error,
2586842Sth160488 					    NS_CONFIG_NOTLOADED,
2596842Sth160488 					    strdup(errstr),
2606842Sth160488 					    NS_LDAP_MEMORY);
2616842Sth160488 					free(ret->server);
2626842Sth160488 					ret->server = NULL;
2636842Sth160488 					return (NS_LDAP_INTERNAL);
2646842Sth160488 				}
2656842Sth160488 			}
2666842Sth160488 		}
2676842Sth160488 
2686842Sth160488 		return (NS_LDAP_SUCCESS);
2696842Sth160488 	}
2706842Sth160488 
2716842Sth160488 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
2726842Sth160488 
2732830Sdjl 	adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1);
2740Sstevel@tonic-gate 	if (server != NULL) {
2750Sstevel@tonic-gate 		adata += strlen(DOORLINESEP) + 1;
2760Sstevel@tonic-gate 		adata += strlen(server) + 1;
2770Sstevel@tonic-gate 	}
2780Sstevel@tonic-gate 	ndata = sizeof (space);
2792830Sdjl 	len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
2800Sstevel@tonic-gate 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
2812830Sdjl 	if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
2822830Sdjl 		return (NS_LDAP_MEMORY);
2832830Sdjl 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >=
2844522Schinlong 	    len)
2852830Sdjl 		return (NS_LDAP_MEMORY);
2860Sstevel@tonic-gate 	if (server != NULL) {
2872830Sdjl 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
2884522Schinlong 		    DOORLINESEP, len) >= len)
2892830Sdjl 			return (NS_LDAP_MEMORY);
2902830Sdjl 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server,
2914522Schinlong 		    len) >= len)
2922830Sdjl 			return (NS_LDAP_MEMORY);
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 	sptr = &space.s_d;
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
2976842Sth160488 	case NS_CACHE_SUCCESS:
2980Sstevel@tonic-gate 		break;
2990Sstevel@tonic-gate 	/* this case is for when the $mgr is not running, but ldapclient */
3000Sstevel@tonic-gate 	/* is trying to initialize things */
3016842Sth160488 	case NS_CACHE_NOSERVER:
3026842Sth160488 		ret_code = getFirstFromConfig(ret, error);
3036842Sth160488 		if (ret_code != NS_LDAP_SUCCESS) {
3046842Sth160488 			return (ret_code);
3050Sstevel@tonic-gate 		}
3066842Sth160488 
3076842Sth160488 		if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
3086842Sth160488 			ret_code = __s_api_ip2hostname(ret->server,
3096842Sth160488 			    &ret->serverFQDN);
3106842Sth160488 			if (ret_code != NS_LDAP_SUCCESS) {
3116842Sth160488 				(void) snprintf(errstr,
3126842Sth160488 				    sizeof (errstr),
3136842Sth160488 				    gettext("The %s address "
3146842Sth160488 				    "can not be resolved into "
3156842Sth160488 				    "a host name. Returning "
3166842Sth160488 				    "the address as it is."),
3176842Sth160488 				    ret->server);
3186842Sth160488 				MKERROR(LOG_ERR,
3196842Sth160488 				    *error,
3206842Sth160488 				    NS_CONFIG_NOTLOADED,
3216842Sth160488 				    strdup(errstr),
3226842Sth160488 				    NS_LDAP_MEMORY);
3236842Sth160488 				free(ret->server);
3246842Sth160488 				ret->server = NULL;
3256842Sth160488 				return (NS_LDAP_INTERNAL);
3266842Sth160488 			}
3270Sstevel@tonic-gate 		}
3280Sstevel@tonic-gate 		return (NS_LDAP_SUCCESS);
3296842Sth160488 	case NS_CACHE_NOTFOUND:
3300Sstevel@tonic-gate 	default:
3310Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
3320Sstevel@tonic-gate 	}
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	/* copy info from door call return structure here */
3350Sstevel@tonic-gate 	rbuf =  space.s_d.ldap_ret.ldap_u.config;
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	/* Get the host */
3380Sstevel@tonic-gate 	ptr = strtok_r(rbuf, DOORLINESEP, &rest);
3390Sstevel@tonic-gate 	if (ptr == NULL) {
3400Sstevel@tonic-gate 		(void) sprintf(errstr, gettext("No server returned from "
3414522Schinlong 		    "ldap_cachemgr"));
3420Sstevel@tonic-gate 		MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
3436842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
3440Sstevel@tonic-gate 		return (NS_LDAP_OP_FAILED);
3450Sstevel@tonic-gate 	}
3460Sstevel@tonic-gate 	ret->server = strdup(ptr);
3470Sstevel@tonic-gate 	if (ret->server == NULL) {
3480Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
3490Sstevel@tonic-gate 	}
3504522Schinlong 	/* Get the host FQDN format */
3514522Schinlong 	if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
3524522Schinlong 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
3534522Schinlong 		if (ptr == NULL) {
3544522Schinlong 			(void) sprintf(errstr, gettext("No server FQDN format "
3554522Schinlong 			    "returned from ldap_cachemgr"));
3564522Schinlong 			MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
3574522Schinlong 			    strdup(errstr), NULL);
3584522Schinlong 			free(ret->server);
3594522Schinlong 			ret->server = NULL;
3604522Schinlong 			return (NS_LDAP_OP_FAILED);
3614522Schinlong 		}
3624522Schinlong 		ret->serverFQDN = strdup(ptr);
3634522Schinlong 		if (ret->serverFQDN == NULL) {
3644522Schinlong 			free(ret->server);
3654522Schinlong 			ret->server = NULL;
3664522Schinlong 			return (NS_LDAP_MEMORY);
3674522Schinlong 		}
3684522Schinlong 	}
3690Sstevel@tonic-gate 
3700Sstevel@tonic-gate 	/* get the Supported Controls/SASL mechs */
3710Sstevel@tonic-gate 	mptr = NULL;
3720Sstevel@tonic-gate 	mcnt = 0;
3730Sstevel@tonic-gate 	cptr = NULL;
3740Sstevel@tonic-gate 	ccnt = 0;
3756842Sth160488 	for (;;) {
3760Sstevel@tonic-gate 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
3770Sstevel@tonic-gate 		if (ptr == NULL)
3780Sstevel@tonic-gate 			break;
3790Sstevel@tonic-gate 		if (strncasecmp(ptr, _SASLMECHANISM,
3804522Schinlong 		    _SASLMECHANISM_LEN) == 0) {
3810Sstevel@tonic-gate 			dptr = strchr(ptr, '=');
3820Sstevel@tonic-gate 			if (dptr == NULL)
3830Sstevel@tonic-gate 				continue;
3840Sstevel@tonic-gate 			dptr++;
3850Sstevel@tonic-gate 			mptr1 = (char **)realloc((void *)mptr,
3864522Schinlong 			    sizeof (char *) * (mcnt+2));
3870Sstevel@tonic-gate 			if (mptr1 == NULL) {
3880Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
3890Sstevel@tonic-gate 				if (sptr != &space.s_d) {
3904522Schinlong 					(void) munmap((char *)sptr, ndata);
3910Sstevel@tonic-gate 				}
3920Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
3934522Schinlong 				__s_api_free_server_info(ret);
3940Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
3950Sstevel@tonic-gate 			}
3960Sstevel@tonic-gate 			mptr = mptr1;
3970Sstevel@tonic-gate 			mptr[mcnt] = strdup(dptr);
3980Sstevel@tonic-gate 			if (mptr[mcnt] == NULL) {
3990Sstevel@tonic-gate 				if (sptr != &space.s_d) {
4004522Schinlong 					(void) munmap((char *)sptr, ndata);
4010Sstevel@tonic-gate 				}
4020Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
4030Sstevel@tonic-gate 				cptr = NULL;
4040Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
4050Sstevel@tonic-gate 				mptr = NULL;
4064522Schinlong 				__s_api_free_server_info(ret);
4070Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
4080Sstevel@tonic-gate 			}
4090Sstevel@tonic-gate 			mcnt++;
4100Sstevel@tonic-gate 			mptr[mcnt] = NULL;
4110Sstevel@tonic-gate 		}
4120Sstevel@tonic-gate 		if (strncasecmp(ptr, _SUPPORTEDCONTROL,
4134522Schinlong 		    _SUPPORTEDCONTROL_LEN) == 0) {
4140Sstevel@tonic-gate 			dptr = strchr(ptr, '=');
4150Sstevel@tonic-gate 			if (dptr == NULL)
4160Sstevel@tonic-gate 				continue;
4170Sstevel@tonic-gate 			dptr++;
4180Sstevel@tonic-gate 			cptr1 = (char **)realloc((void *)cptr,
4194522Schinlong 			    sizeof (char *) * (ccnt+2));
4200Sstevel@tonic-gate 			if (cptr1 == NULL) {
4210Sstevel@tonic-gate 				if (sptr != &space.s_d) {
4224522Schinlong 					(void) munmap((char *)sptr, ndata);
4230Sstevel@tonic-gate 				}
4240Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
4250Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
4260Sstevel@tonic-gate 				mptr = NULL;
4274522Schinlong 				__s_api_free_server_info(ret);
4280Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
4290Sstevel@tonic-gate 			}
4300Sstevel@tonic-gate 			cptr = cptr1;
4310Sstevel@tonic-gate 			cptr[ccnt] = strdup(dptr);
4320Sstevel@tonic-gate 			if (cptr[ccnt] == NULL) {
4330Sstevel@tonic-gate 				if (sptr != &space.s_d) {
4344522Schinlong 					(void) munmap((char *)sptr, ndata);
4350Sstevel@tonic-gate 				}
4360Sstevel@tonic-gate 				__s_api_free2dArray(cptr);
4370Sstevel@tonic-gate 				cptr = NULL;
4380Sstevel@tonic-gate 				__s_api_free2dArray(mptr);
4390Sstevel@tonic-gate 				mptr = NULL;
4404522Schinlong 				__s_api_free_server_info(ret);
4410Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
4420Sstevel@tonic-gate 			}
4430Sstevel@tonic-gate 			ccnt++;
4440Sstevel@tonic-gate 			cptr[ccnt] = NULL;
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 	if (mptr != NULL) {
4480Sstevel@tonic-gate 		ret->saslMechanisms = mptr;
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 	if (cptr != NULL) {
4510Sstevel@tonic-gate 		ret->controls = cptr;
4520Sstevel@tonic-gate 	}
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	/* clean up door call */
4560Sstevel@tonic-gate 	if (sptr != &space.s_d) {
4570Sstevel@tonic-gate 		(void) munmap((char *)sptr, ndata);
4580Sstevel@tonic-gate 	}
4590Sstevel@tonic-gate 	*error = NULL;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
4620Sstevel@tonic-gate }
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 
4656842Sth160488 #ifdef DEBUG
4660Sstevel@tonic-gate /*
4670Sstevel@tonic-gate  * printCred(): prints the credential structure
4680Sstevel@tonic-gate  */
4690Sstevel@tonic-gate static void
4706842Sth160488 printCred(FILE *fp, const ns_cred_t *cred)
4710Sstevel@tonic-gate {
4722830Sdjl 	thread_t	t = thr_self();
4732830Sdjl 
4740Sstevel@tonic-gate 	if (cred == NULL) {
4756842Sth160488 		(void) fprintf(fp, "tid= %d: printCred: cred is NULL\n", t);
4760Sstevel@tonic-gate 		return;
4770Sstevel@tonic-gate 	}
4780Sstevel@tonic-gate 
4796842Sth160488 	(void) fprintf(fp, "tid= %d: AuthType=%d", t, cred->auth.type);
4806842Sth160488 	(void) fprintf(fp, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
4816842Sth160488 	(void) fprintf(fp, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
4826842Sth160488 	(void) fprintf(fp, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
4830Sstevel@tonic-gate 	if (cred->hostcertpath)
4846842Sth160488 		(void) fprintf(fp, "tid= %d: hostCertPath=%s\n",
4854522Schinlong 		    t, cred->hostcertpath);
4860Sstevel@tonic-gate 	if (cred->cred.unix_cred.userID)
4876842Sth160488 		(void) fprintf(fp, "tid= %d: userID=%s\n",
4884522Schinlong 		    t, cred->cred.unix_cred.userID);
4890Sstevel@tonic-gate 	if (cred->cred.unix_cred.passwd)
4906842Sth160488 		(void) fprintf(fp, "tid= %d: passwd=%s\n",
4914522Schinlong 		    t, cred->cred.unix_cred.passwd);
4920Sstevel@tonic-gate }
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate /*
4950Sstevel@tonic-gate  * printConnection(): prints the connection structure
4960Sstevel@tonic-gate  */
4970Sstevel@tonic-gate static void
4986842Sth160488 printConnection(FILE *fp, Connection *con)
4990Sstevel@tonic-gate {
5002830Sdjl 	thread_t	t = thr_self();
5012830Sdjl 
5022830Sdjl 	if (con == NULL)
5030Sstevel@tonic-gate 		return;
5040Sstevel@tonic-gate 
5056842Sth160488 	(void) fprintf(fp, "tid= %d: connectionID=%d\n", t, con->connectionId);
5066842Sth160488 	(void) fprintf(fp, "tid= %d: usedBit=%d\n", t, con->usedBit);
5076842Sth160488 	(void) fprintf(fp, "tid= %d: threadID=%d\n", t, con->threadID);
5080Sstevel@tonic-gate 	if (con->serverAddr) {
5096842Sth160488 		(void) fprintf(fp, "tid= %d: serverAddr=%s\n",
5104522Schinlong 		    t, con->serverAddr);
5110Sstevel@tonic-gate 	}
5126842Sth160488 	printCred(fp, con->auth);
5130Sstevel@tonic-gate }
5146842Sth160488 #endif
5150Sstevel@tonic-gate 
5160Sstevel@tonic-gate /*
5176842Sth160488  * addConnection(): inserts a connection in the connection list.
5186842Sth160488  * It will also sets use bit and the thread Id for the thread
5196842Sth160488  * using the connection for the first time.
5200Sstevel@tonic-gate  * Returns: -1 = failure, new Connection ID = success
5210Sstevel@tonic-gate  */
5220Sstevel@tonic-gate static int
5230Sstevel@tonic-gate addConnection(Connection *con)
5240Sstevel@tonic-gate {
5256842Sth160488 	int i;
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	if (!con)
5280Sstevel@tonic-gate 		return (-1);
5296842Sth160488 #ifdef DEBUG
5306842Sth160488 	(void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID);
5316842Sth160488 #endif /* DEBUG */
5326842Sth160488 	(void) mutex_lock(&sessionPoolLock);
5330Sstevel@tonic-gate 	if (sessionPool == NULL) {
5340Sstevel@tonic-gate 		sessionPoolSize = SESSION_CACHE_INC;
5350Sstevel@tonic-gate 		sessionPool = calloc(sessionPoolSize,
5366842Sth160488 		    sizeof (Connection *));
5370Sstevel@tonic-gate 		if (!sessionPool) {
5386842Sth160488 			(void) mutex_unlock(&sessionPoolLock);
5390Sstevel@tonic-gate 			return (-1);
5400Sstevel@tonic-gate 		}
5416842Sth160488 #ifdef DEBUG
5426842Sth160488 		(void) fprintf(stderr, "Initialized sessionPool\n");
5436842Sth160488 #endif /* DEBUG */
5440Sstevel@tonic-gate 	}
5450Sstevel@tonic-gate 	for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
5460Sstevel@tonic-gate 		;
5470Sstevel@tonic-gate 	if (i == sessionPoolSize) {
5480Sstevel@tonic-gate 		/* run out of array, need to increase sessionPool */
5490Sstevel@tonic-gate 		Connection **cl;
5500Sstevel@tonic-gate 		cl = (Connection **) realloc(sessionPool,
5514522Schinlong 		    (sessionPoolSize + SESSION_CACHE_INC) *
5524522Schinlong 		    sizeof (Connection *));
5530Sstevel@tonic-gate 		if (!cl) {
5546842Sth160488 			(void) mutex_unlock(&sessionPoolLock);
5550Sstevel@tonic-gate 			return (-1);
5560Sstevel@tonic-gate 		}
5570Sstevel@tonic-gate 		(void) memset(cl + sessionPoolSize, 0,
5586842Sth160488 		    SESSION_CACHE_INC * sizeof (Connection *));
5590Sstevel@tonic-gate 		sessionPool = cl;
5600Sstevel@tonic-gate 		sessionPoolSize += SESSION_CACHE_INC;
5616842Sth160488 #ifdef DEBUG
5626842Sth160488 		(void) fprintf(stderr, "Increased sessionPoolSize to: %d\n",
5636842Sth160488 		    sessionPoolSize);
5646842Sth160488 #endif /* DEBUG */
5650Sstevel@tonic-gate 	}
5660Sstevel@tonic-gate 	sessionPool[i] = con;
5676842Sth160488 	con->usedBit = B_TRUE;
5686842Sth160488 	(void) mutex_unlock(&sessionPoolLock);
5690Sstevel@tonic-gate 	con->connectionId = i + CONID_OFFSET;
5706842Sth160488 #ifdef DEBUG
5716842Sth160488 	(void) fprintf(stderr, "Connection added [%d]\n", i);
5726842Sth160488 	printConnection(stderr, con);
5736842Sth160488 #endif /* DEBUG */
5740Sstevel@tonic-gate 	return (i + CONID_OFFSET);
5750Sstevel@tonic-gate }
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate /*
5780Sstevel@tonic-gate  * findConnection(): find an available connection from the list
5790Sstevel@tonic-gate  * that matches the criteria specified in Connection structure.
5800Sstevel@tonic-gate  * If serverAddr is NULL, then find a connection to any server
5810Sstevel@tonic-gate  * as long as it matches the rest of the parameters.
5820Sstevel@tonic-gate  * Returns: -1 = failure, the Connection ID found = success.
5830Sstevel@tonic-gate  */
5840Sstevel@tonic-gate static int
5852830Sdjl findConnection(int flags, const char *serverAddr,
5862830Sdjl 	const ns_cred_t *auth, Connection **conp)
5870Sstevel@tonic-gate {
5880Sstevel@tonic-gate 	Connection *cp;
5896842Sth160488 	int i;
5902830Sdjl #ifdef DEBUG
5916842Sth160488 	thread_t t;
5922830Sdjl #endif /* DEBUG */
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 	if (auth == NULL || conp == NULL)
5950Sstevel@tonic-gate 		return (-1);
5960Sstevel@tonic-gate 	*conp = NULL;
5970Sstevel@tonic-gate 
5985840Smj162486 	/*
5995840Smj162486 	 * If a new connection is requested, no need to continue.
6006842Sth160488 	 * If the process is not nscd and is not requesting keep
6016842Sth160488 	 * connections alive, no need to continue.
6025840Smj162486 	 */
6035840Smj162486 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
6046072Smj162486 	    !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
6052830Sdjl 		return (-1);
6062830Sdjl 
6070Sstevel@tonic-gate #ifdef DEBUG
6086842Sth160488 	t = thr_self();
6092830Sdjl 	(void) fprintf(stderr, "tid= %d: Find connection\n", t);
6102830Sdjl 	(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
6110Sstevel@tonic-gate 	if (serverAddr && *serverAddr)
6122830Sdjl 		(void) fprintf(stderr, "tid= %d: serverAddr=%s\n",
6134522Schinlong 		    t, serverAddr);
6140Sstevel@tonic-gate 	else
6152830Sdjl 		(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
6166842Sth160488 	printCred(stderr, auth);
6170Sstevel@tonic-gate 	fflush(stderr);
6180Sstevel@tonic-gate #endif /* DEBUG */
6196842Sth160488 	if (sessionPool == NULL)
6200Sstevel@tonic-gate 		return (-1);
6216842Sth160488 	(void) mutex_lock(&sessionPoolLock);
6220Sstevel@tonic-gate 	for (i = 0; i < sessionPoolSize; ++i) {
6230Sstevel@tonic-gate 		if (sessionPool[i] == NULL)
6240Sstevel@tonic-gate 			continue;
6250Sstevel@tonic-gate 		cp = sessionPool[i];
6260Sstevel@tonic-gate #ifdef DEBUG
6276842Sth160488 		(void) fprintf(stderr,
6286842Sth160488 		    "tid: %d: checking connection [%d] ....\n", t, i);
6296842Sth160488 		printConnection(stderr, cp);
6300Sstevel@tonic-gate #endif /* DEBUG */
6316842Sth160488 		if ((cp->usedBit) || (serverAddr && *serverAddr &&
6320Sstevel@tonic-gate 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
6330Sstevel@tonic-gate 			continue;
6344048Schinlong 
6356842Sth160488 		if (__s_api_is_auth_matched(cp->auth, auth) == B_FALSE)
6366842Sth160488 			continue;
6374048Schinlong 
6380Sstevel@tonic-gate 		/* found an available connection */
6396842Sth160488 		cp->usedBit = B_TRUE;
6406842Sth160488 		(void) mutex_unlock(&sessionPoolLock);
6416842Sth160488 		cp->threadID = thr_self();
6420Sstevel@tonic-gate 		*conp = cp;
6430Sstevel@tonic-gate #ifdef DEBUG
6446842Sth160488 		(void) fprintf(stderr,
6456842Sth160488 		    "tid %d: Connection found cID=%d\n", t, i);
6460Sstevel@tonic-gate 		fflush(stderr);
6470Sstevel@tonic-gate #endif /* DEBUG */
6480Sstevel@tonic-gate 		return (i + CONID_OFFSET);
6490Sstevel@tonic-gate 	}
6506842Sth160488 	(void) mutex_unlock(&sessionPoolLock);
6516842Sth160488 	return (-1);
6520Sstevel@tonic-gate }
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate /*
6550Sstevel@tonic-gate  * Free a Connection structure
6560Sstevel@tonic-gate  */
6576842Sth160488 void
6586842Sth160488 __s_api_freeConnection(Connection *con)
6590Sstevel@tonic-gate {
6600Sstevel@tonic-gate 	if (con == NULL)
6610Sstevel@tonic-gate 		return;
6620Sstevel@tonic-gate 	if (con->serverAddr)
6630Sstevel@tonic-gate 		free(con->serverAddr);
6640Sstevel@tonic-gate 	if (con->auth)
6650Sstevel@tonic-gate 		(void) __ns_ldap_freeCred(&(con->auth));
6660Sstevel@tonic-gate 	if (con->saslMechanisms) {
6670Sstevel@tonic-gate 		__s_api_free2dArray(con->saslMechanisms);
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 	if (con->controls) {
6700Sstevel@tonic-gate 		__s_api_free2dArray(con->controls);
6710Sstevel@tonic-gate 	}
6720Sstevel@tonic-gate 	free(con);
6730Sstevel@tonic-gate }
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate /*
6760Sstevel@tonic-gate  * Find a connection matching the passed in criteria.  If an open
6770Sstevel@tonic-gate  * connection with that criteria exists use it, otherwise open a
6780Sstevel@tonic-gate  * new connection.
6790Sstevel@tonic-gate  * Success: returns the pointer to the Connection structure
6800Sstevel@tonic-gate  * Failure: returns NULL, error code and message should be in errorp
6810Sstevel@tonic-gate  */
6821687Sjanga 
6830Sstevel@tonic-gate static int
6840Sstevel@tonic-gate makeConnection(Connection **conp, const char *serverAddr,
6850Sstevel@tonic-gate 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
6861179Svv149972 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
6876842Sth160488 	int nopasswd_acct_mgmt, int flags, char ***badsrvrs,
6886842Sth160488 	ns_conn_user_t *conn_user)
6890Sstevel@tonic-gate {
6900Sstevel@tonic-gate 	Connection *con = NULL;
6910Sstevel@tonic-gate 	ConnectionID id;
6920Sstevel@tonic-gate 	char errmsg[MAXERROR];
6930Sstevel@tonic-gate 	int rc, exit_rc = NS_LDAP_SUCCESS;
6940Sstevel@tonic-gate 	ns_server_info_t sinfo;
6950Sstevel@tonic-gate 	char *hReq, *host = NULL;
6960Sstevel@tonic-gate 	LDAP *ld = NULL;
6970Sstevel@tonic-gate 	int passwd_mgmt = 0;
6981687Sjanga 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
6992830Sdjl 	short	memerr = 0; /* Variable for tracking memory allocation */
7004522Schinlong 	char *serverAddrType = NULL, **bindHost = NULL;
7012830Sdjl 
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	if (conp == NULL || errorp == NULL || auth == NULL)
7040Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
7050Sstevel@tonic-gate 	*errorp = NULL;
7060Sstevel@tonic-gate 	*conp = NULL;
7074522Schinlong 	(void) memset(&sinfo, 0, sizeof (sinfo));
7080Sstevel@tonic-gate 
7096842Sth160488 	if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) {
7100Sstevel@tonic-gate 		/* connection found in cache */
7110Sstevel@tonic-gate #ifdef DEBUG
7122830Sdjl 		(void) fprintf(stderr, "tid= %d: connection found in "
7134522Schinlong 		    "cache %d\n", thr_self(), id);
7140Sstevel@tonic-gate 		fflush(stderr);
7150Sstevel@tonic-gate #endif /* DEBUG */
7160Sstevel@tonic-gate 		*cID = id;
7170Sstevel@tonic-gate 		*conp = con;
7180Sstevel@tonic-gate 		return (NS_LDAP_SUCCESS);
7190Sstevel@tonic-gate 	}
7200Sstevel@tonic-gate 
7214522Schinlong 	if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
7222830Sdjl 		serverAddrType = NS_CACHE_ADDR_HOSTNAME;
7234522Schinlong 		bindHost = &sinfo.serverFQDN;
7244522Schinlong 	} else {
7252830Sdjl 		serverAddrType = NS_CACHE_ADDR_IP;
7264522Schinlong 		bindHost = &sinfo.server;
7274522Schinlong 	}
7282830Sdjl 
7290Sstevel@tonic-gate 	if (serverAddr) {
7306842Sth160488 		if (__s_api_isInitializing()) {
7316842Sth160488 			/*
7326842Sth160488 			 * When obtaining the root DSE, connect to the server
7336842Sth160488 			 * passed here through the serverAddr parameter
7346842Sth160488 			 */
7356842Sth160488 			sinfo.server = strdup(serverAddr);
7366842Sth160488 			if (sinfo.server == NULL)
7375559Ssdussud 				return (NS_LDAP_MEMORY);
7386842Sth160488 			if (strcmp(serverAddrType,
7396842Sth160488 			    NS_CACHE_ADDR_HOSTNAME) == 0) {
7406842Sth160488 				rc = __s_api_ip2hostname(sinfo.server,
7416842Sth160488 				    &sinfo.serverFQDN);
7426842Sth160488 				if (rc != NS_LDAP_SUCCESS) {
7436842Sth160488 					(void) snprintf(errmsg,
7446842Sth160488 					    sizeof (errmsg),
7456842Sth160488 					    gettext("The %s address "
7466842Sth160488 					    "can not be resolved into "
7476842Sth160488 					    "a host name. Returning "
7486842Sth160488 					    "the address as it is."),
7496842Sth160488 					    serverAddr);
7506842Sth160488 					MKERROR(LOG_ERR,
7516842Sth160488 					    *errorp,
7526842Sth160488 					    NS_CONFIG_NOTLOADED,
7536842Sth160488 					    strdup(errmsg),
7546842Sth160488 					    NS_LDAP_MEMORY);
7556842Sth160488 					__s_api_free_server_info(&sinfo);
7566842Sth160488 					return (NS_LDAP_INTERNAL);
7576842Sth160488 				}
7586842Sth160488 			}
7596842Sth160488 		} else {
7606842Sth160488 			/*
7616842Sth160488 			 * We're given the server address, just use it.
7626842Sth160488 			 * In case of sasl/GSSAPI, serverAddr would need
7636842Sth160488 			 * to be a FQDN.  We assume this is the case for now.
7646842Sth160488 			 *
7656842Sth160488 			 * Only the server address fields of sinfo structure
7666842Sth160488 			 * are filled in since these are the only relevant
7676842Sth160488 			 * data that we have. Other fields of this structure
7686842Sth160488 			 * (controls, saslMechanisms) are kept to NULL.
7696842Sth160488 			 */
7706842Sth160488 			sinfo.server = strdup(serverAddr);
7716842Sth160488 			if (sinfo.server == NULL)  {
7726842Sth160488 				return (NS_LDAP_MEMORY);
7736842Sth160488 			}
7746842Sth160488 			if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
7756842Sth160488 				sinfo.serverFQDN = strdup(serverAddr);
7766842Sth160488 				if (sinfo.serverFQDN == NULL) {
7776842Sth160488 					free(sinfo.server);
7786842Sth160488 					return (NS_LDAP_MEMORY);
7796842Sth160488 				}
7805559Ssdussud 			}
7812830Sdjl 		}
7824522Schinlong 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
7836842Sth160488 		    fail_if_new_pwd_reqd, passwd_mgmt, conn_user);
7840Sstevel@tonic-gate 		if (rc == NS_LDAP_SUCCESS || rc ==
7854522Schinlong 		    NS_LDAP_SUCCESS_WITH_INFO) {
7860Sstevel@tonic-gate 			exit_rc = rc;
7870Sstevel@tonic-gate 			goto create_con;
7880Sstevel@tonic-gate 		} else {
7895559Ssdussud 			if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
7905559Ssdussud 				(void) snprintf(errmsg, sizeof (errmsg),
7915559Ssdussud 				    "%s %s", gettext("makeConnection: "
7925559Ssdussud 				    "failed to open connection using "
7935559Ssdussud 				    "sasl/GSSAPI to"), *bindHost);
7945559Ssdussud 			} else {
7955559Ssdussud 				(void) snprintf(errmsg, sizeof (errmsg),
7965559Ssdussud 				    "%s %s", gettext("makeConnection: "
7975559Ssdussud 				    "failed to open connection to"),
7985559Ssdussud 				    *bindHost);
7995559Ssdussud 			}
8005559Ssdussud 			syslog(LOG_ERR, "libsldap: %s", errmsg);
8015559Ssdussud 			__s_api_free_server_info(&sinfo);
8020Sstevel@tonic-gate 			return (rc);
8030Sstevel@tonic-gate 		}
8040Sstevel@tonic-gate 	}
8050Sstevel@tonic-gate 
8060Sstevel@tonic-gate 	/* No cached connection, create one */
8070Sstevel@tonic-gate 	for (; ; ) {
8080Sstevel@tonic-gate 		if (host == NULL)
8090Sstevel@tonic-gate 			hReq = NS_CACHE_NEW;
8100Sstevel@tonic-gate 		else
8110Sstevel@tonic-gate 			hReq = NS_CACHE_NEXT;
8122830Sdjl 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
8134522Schinlong 		    serverAddrType);
8140Sstevel@tonic-gate 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
8154522Schinlong 		    (host && (strcasecmp(host, sinfo.server) == 0))) {
8160Sstevel@tonic-gate 			/* Log the error */
8170Sstevel@tonic-gate 			if (*errorp) {
8180Sstevel@tonic-gate 				(void) snprintf(errmsg, sizeof (errmsg),
8190Sstevel@tonic-gate 				"%s: (%s)", gettext("makeConnection: "
8200Sstevel@tonic-gate 				"unable to make LDAP connection, "
8210Sstevel@tonic-gate 				"request for a server failed"),
8220Sstevel@tonic-gate 				    (*errorp)->message);
8230Sstevel@tonic-gate 				syslog(LOG_ERR, "libsldap: %s", errmsg);
8240Sstevel@tonic-gate 			}
8250Sstevel@tonic-gate 
8264522Schinlong 			__s_api_free_server_info(&sinfo);
8270Sstevel@tonic-gate 			if (host)
8280Sstevel@tonic-gate 				free(host);
8290Sstevel@tonic-gate 			return (NS_LDAP_OP_FAILED);
8300Sstevel@tonic-gate 		}
8310Sstevel@tonic-gate 		if (host)
8320Sstevel@tonic-gate 			free(host);
8330Sstevel@tonic-gate 		host = strdup(sinfo.server);
8340Sstevel@tonic-gate 		if (host == NULL) {
8354522Schinlong 			__s_api_free_server_info(&sinfo);
8360Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
8370Sstevel@tonic-gate 		}
8380Sstevel@tonic-gate 
8390Sstevel@tonic-gate 		/* check if server supports password management */
8400Sstevel@tonic-gate 		passwd_mgmt = __s_api_contain_passwd_control_oid(
8414522Schinlong 		    sinfo.controls);
8421179Svv149972 		/* check if server supports password less account mgmt */
8431179Svv149972 		if (nopasswd_acct_mgmt &&
8444522Schinlong 		    !__s_api_contain_account_usable_control_oid(
8454522Schinlong 		    sinfo.controls)) {
8461179Svv149972 			syslog(LOG_WARNING, "libsldap: server %s does not "
8474522Schinlong 			    "provide account information without password",
8484522Schinlong 			    host);
8491179Svv149972 			free(host);
8504522Schinlong 			__s_api_free_server_info(&sinfo);
8511179Svv149972 			return (NS_LDAP_OP_FAILED);
8521179Svv149972 		}
8530Sstevel@tonic-gate 		/* make the connection */
8544522Schinlong 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
8556842Sth160488 		    fail_if_new_pwd_reqd, passwd_mgmt, conn_user);
8560Sstevel@tonic-gate 		/* if success, go to create connection structure */
8570Sstevel@tonic-gate 		if (rc == NS_LDAP_SUCCESS ||
8584522Schinlong 		    rc == NS_LDAP_SUCCESS_WITH_INFO) {
8590Sstevel@tonic-gate 			exit_rc = rc;
8600Sstevel@tonic-gate 			break;
8610Sstevel@tonic-gate 		}
8620Sstevel@tonic-gate 
8630Sstevel@tonic-gate 		/*
8640Sstevel@tonic-gate 		 * If not able to reach the server, inform the ldap
8650Sstevel@tonic-gate 		 * cache manager that the server should be removed
8660Sstevel@tonic-gate 		 * from its server list. Thus, the manager will not
8670Sstevel@tonic-gate 		 * return this server on the next get-server request
8680Sstevel@tonic-gate 		 * and will also reduce the server list refresh TTL,
8690Sstevel@tonic-gate 		 * so that it will find out sooner when the server
8700Sstevel@tonic-gate 		 * is up again.
8710Sstevel@tonic-gate 		 */
8720Sstevel@tonic-gate 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
8730Sstevel@tonic-gate 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
8744522Schinlong 			    (*errorp)->status == LDAP_SERVER_DOWN) {
8751687Sjanga 				/* Reset memory allocation error */
8761687Sjanga 				memerr = 0;
8771687Sjanga 				/*
8781687Sjanga 				 * We contacted a server that we could
8791687Sjanga 				 * not either authenticate to or contact.
8801687Sjanga 				 * If it is due to authentication, then
8811687Sjanga 				 * we need to try the server again. So,
8821687Sjanga 				 * do not remove the server yet, but
8831687Sjanga 				 * add it to the bad server list.
8841687Sjanga 				 * The caller routine will remove
8851687Sjanga 				 * the servers if:
8861687Sjanga 				 *	a). A good server is found or
8871687Sjanga 				 *	b). All the possible methods
8881687Sjanga 				 *	    are tried without finding
8891687Sjanga 				 *	    a good server
8901687Sjanga 				 */
8911687Sjanga 				if (*badsrvrs == NULL) {
8924522Schinlong 					if (!(*badsrvrs = (char **)malloc
8934522Schinlong 					    (sizeof (char *) * NUMTOMALLOC))) {
8944522Schinlong 						memerr = 1;
8954522Schinlong 					}
8961687Sjanga 				/* Allocate memory in chunks of NUMTOMALLOC */
8971687Sjanga 				} else if ((totalbad % NUMTOMALLOC) ==
8984522Schinlong 				    NUMTOMALLOC - 1) {
8994522Schinlong 					char **tmpptr;
9004522Schinlong 					if (!(tmpptr = (char **)realloc(
9014522Schinlong 					    *badsrvrs,
9021687Sjanga 					    (sizeof (char *) * NUMTOMALLOC *
9031687Sjanga 					    ((totalbad/NUMTOMALLOC) + 2))))) {
9044522Schinlong 						memerr = 1;
9054522Schinlong 					} else {
9064522Schinlong 						*badsrvrs = tmpptr;
9074522Schinlong 					}
908493Ssdussud 				}
9091687Sjanga 				/*
9101687Sjanga 				 * Store host only if there were no unsuccessful
9111687Sjanga 				 * memory allocations above
9121687Sjanga 				 */
9131687Sjanga 				if (!memerr &&
9141687Sjanga 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
9151687Sjanga 					memerr = 1;
9161687Sjanga 					totalbad--;
9171687Sjanga 				}
9181687Sjanga 				(*badsrvrs)[totalbad] = NULL;
919493Ssdussud 			}
9200Sstevel@tonic-gate 		}
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 		/* else, cleanup and go for the next server */
9234522Schinlong 		__s_api_free_server_info(&sinfo);
9244522Schinlong 
9251687Sjanga 		/* Return if we had memory allocation errors */
9261687Sjanga 		if (memerr)
9271687Sjanga 			return (NS_LDAP_MEMORY);
9280Sstevel@tonic-gate 		if (*errorp) {
9290Sstevel@tonic-gate 			/*
9300Sstevel@tonic-gate 			 * If openConnection() failed due to
9310Sstevel@tonic-gate 			 * password policy, or invalid credential,
9320Sstevel@tonic-gate 			 * keep *errorp and exit
9330Sstevel@tonic-gate 			 */
9340Sstevel@tonic-gate 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
9350Sstevel@tonic-gate 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
9360Sstevel@tonic-gate 				free(host);
9370Sstevel@tonic-gate 				return (rc);
9380Sstevel@tonic-gate 			} else {
9390Sstevel@tonic-gate 				(void) __ns_ldap_freeError(errorp);
9400Sstevel@tonic-gate 				*errorp = NULL;
9410Sstevel@tonic-gate 			}
9420Sstevel@tonic-gate 		}
9430Sstevel@tonic-gate 	}
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate create_con:
9460Sstevel@tonic-gate 	/* we have created ld, setup con structure */
9470Sstevel@tonic-gate 	if (host)
9480Sstevel@tonic-gate 		free(host);
9490Sstevel@tonic-gate 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
9504522Schinlong 		__s_api_free_server_info(&sinfo);
9510Sstevel@tonic-gate 		/*
9520Sstevel@tonic-gate 		 * If password control attached in **errorp,
9530Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
9540Sstevel@tonic-gate 		 * free the error structure
9550Sstevel@tonic-gate 		 */
9560Sstevel@tonic-gate 		if (*errorp) {
9570Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
9580Sstevel@tonic-gate 			*errorp = NULL;
9590Sstevel@tonic-gate 		}
9605559Ssdussud 		(void) ldap_unbind(ld);
9610Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
9620Sstevel@tonic-gate 	}
9630Sstevel@tonic-gate 
9644522Schinlong 	con->serverAddr = sinfo.server; /* Store original format */
9654522Schinlong 	if (sinfo.serverFQDN != NULL) {
9664522Schinlong 		free(sinfo.serverFQDN);
9674522Schinlong 		sinfo.serverFQDN = NULL;
9684522Schinlong 	}
9690Sstevel@tonic-gate 	con->saslMechanisms = sinfo.saslMechanisms;
9700Sstevel@tonic-gate 	con->controls = sinfo.controls;
9710Sstevel@tonic-gate 
9720Sstevel@tonic-gate 	con->auth = __ns_ldap_dupAuth(auth);
9730Sstevel@tonic-gate 	if (con->auth == NULL) {
9745559Ssdussud 		(void) ldap_unbind(ld);
9756842Sth160488 		__s_api_freeConnection(con);
9760Sstevel@tonic-gate 		/*
9770Sstevel@tonic-gate 		 * If password control attached in **errorp,
9780Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
9790Sstevel@tonic-gate 		 * free the error structure
9800Sstevel@tonic-gate 		 */
9810Sstevel@tonic-gate 		if (*errorp) {
9820Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
9830Sstevel@tonic-gate 			*errorp = NULL;
9840Sstevel@tonic-gate 		}
9850Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 
9880Sstevel@tonic-gate 	con->threadID = thr_self();
9893387Schinlong 	con->pid = getpid();
9902830Sdjl 
9910Sstevel@tonic-gate 	con->ld = ld;
9926842Sth160488 	/* add MT connection to the MT connection pool */
9936842Sth160488 	if (conn_user != NULL && conn_user->conn_mt != NULL) {
9946842Sth160488 		if (__s_api_conn_mt_add(con, conn_user, errorp) ==
9956842Sth160488 		    NS_LDAP_SUCCESS) {
9966842Sth160488 			*conp = con;
9976842Sth160488 			return (exit_rc);
9986842Sth160488 		} else {
9996842Sth160488 			(void) ldap_unbind(ld);
10006842Sth160488 			__s_api_freeConnection(con);
10016842Sth160488 			return ((*errorp)->status);
10026842Sth160488 		}
10036842Sth160488 	}
10046842Sth160488 
10056842Sth160488 	/* MT connection not supported or not required case */
10060Sstevel@tonic-gate 	if ((id = addConnection(con)) == -1) {
10075559Ssdussud 		(void) ldap_unbind(ld);
10086842Sth160488 		__s_api_freeConnection(con);
10090Sstevel@tonic-gate 		/*
10100Sstevel@tonic-gate 		 * If password control attached in **errorp,
10110Sstevel@tonic-gate 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
10120Sstevel@tonic-gate 		 * free the error structure
10130Sstevel@tonic-gate 		 */
10140Sstevel@tonic-gate 		if (*errorp) {
10150Sstevel@tonic-gate 			(void) __ns_ldap_freeError(errorp);
10160Sstevel@tonic-gate 			*errorp = NULL;
10170Sstevel@tonic-gate 		}
10180Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
10190Sstevel@tonic-gate 	}
10200Sstevel@tonic-gate #ifdef DEBUG
10212830Sdjl 	(void) fprintf(stderr, "tid= %d: connection added into "
10224522Schinlong 	    "cache %d\n", thr_self(), id);
10230Sstevel@tonic-gate 	fflush(stderr);
10240Sstevel@tonic-gate #endif /* DEBUG */
10250Sstevel@tonic-gate 	*cID = id;
10260Sstevel@tonic-gate 	*conp = con;
10270Sstevel@tonic-gate 	return (exit_rc);
10280Sstevel@tonic-gate }
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate /*
10310Sstevel@tonic-gate  * Return the specified connection to the pool.  If necessary
10320Sstevel@tonic-gate  * delete the connection.
10330Sstevel@tonic-gate  */
10340Sstevel@tonic-gate 
10350Sstevel@tonic-gate static void
10360Sstevel@tonic-gate _DropConnection(ConnectionID cID, int flag, int fini)
10370Sstevel@tonic-gate {
10380Sstevel@tonic-gate 	Connection *cp;
10390Sstevel@tonic-gate 	int id;
10406842Sth160488 	int use_mutex = !fini;
10416722Smj162486 	struct timeval	zerotime;
10426722Smj162486 	LDAPMessage	*res;
10436722Smj162486 
10446722Smj162486 	zerotime.tv_sec = zerotime.tv_usec = 0L;
10456722Smj162486 
10460Sstevel@tonic-gate 	id = cID - CONID_OFFSET;
10470Sstevel@tonic-gate 	if (id < 0 || id >= sessionPoolSize)
10480Sstevel@tonic-gate 		return;
10490Sstevel@tonic-gate #ifdef DEBUG
10506842Sth160488 	(void) fprintf(stderr,
10516842Sth160488 	    "tid %d: Dropping connection cID=%d flag=0x%x\n",
10526842Sth160488 	    thr_self(), cID, flag);
10530Sstevel@tonic-gate 	fflush(stderr);
10540Sstevel@tonic-gate #endif /* DEBUG */
10556842Sth160488 	if (use_mutex)
10566842Sth160488 		(void) mutex_lock(&sessionPoolLock);
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 	cp = sessionPool[id];
10590Sstevel@tonic-gate 	/* sanity check before removing */
10606842Sth160488 	if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) {
10616842Sth160488 		if (use_mutex)
10626842Sth160488 			(void) mutex_unlock(&sessionPoolLock);
10630Sstevel@tonic-gate 		return;
10640Sstevel@tonic-gate 	}
10650Sstevel@tonic-gate 
10660Sstevel@tonic-gate 	if (!fini &&
10676842Sth160488 	    ((flag & NS_LDAP_NEW_CONN) == 0) &&
10686072Smj162486 	    ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc() ||
10696072Smj162486 	    __s_api_peruser_proc())) {
10700Sstevel@tonic-gate 		/* release Connection (keep alive) */
10710Sstevel@tonic-gate 		cp->usedBit = B_FALSE;
10720Sstevel@tonic-gate 		cp->threadID = 0;	/* unmark the threadID */
10736722Smj162486 		/*
10746842Sth160488 		 * Do sanity cleanup of remaining results.
10756722Smj162486 		 */
10766842Sth160488 		while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL,
10776842Sth160488 		    &zerotime, &res) > 0) {
10786842Sth160488 			if (res != NULL)
10796842Sth160488 				(void) ldap_msgfree(res);
10806722Smj162486 		}
10816842Sth160488 		if (use_mutex)
10826842Sth160488 			(void) mutex_unlock(&sessionPoolLock);
10830Sstevel@tonic-gate 	} else {
10840Sstevel@tonic-gate 		/* delete Connection (disconnect) */
10856842Sth160488 		sessionPool[id] = NULL;
10866842Sth160488 		if (use_mutex)
10876842Sth160488 			(void) mutex_unlock(&sessionPoolLock);
10886842Sth160488 		(void) ldap_unbind(cp->ld);
10896842Sth160488 		__s_api_freeConnection(cp);
10900Sstevel@tonic-gate 	}
10910Sstevel@tonic-gate }
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate void
10940Sstevel@tonic-gate DropConnection(ConnectionID cID, int flag)
10950Sstevel@tonic-gate {
10960Sstevel@tonic-gate 	_DropConnection(cID, flag, 0);
10970Sstevel@tonic-gate }
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate /*
11000Sstevel@tonic-gate  * This routine is called after a bind operation is
11010Sstevel@tonic-gate  * done in openConnection() to process the password
11020Sstevel@tonic-gate  * management information, if any.
11030Sstevel@tonic-gate  *
11040Sstevel@tonic-gate  * Input:
11050Sstevel@tonic-gate  *   bind_type: "simple" or "sasl/DIGEST-MD5"
11060Sstevel@tonic-gate  *   ldaprc   : ldap rc from the ldap bind operation
11070Sstevel@tonic-gate  *   controls : controls returned by the server
11080Sstevel@tonic-gate  *   errmsg   : error message from the server
11090Sstevel@tonic-gate  *   fail_if_new_pwd_reqd:
11100Sstevel@tonic-gate  *              flag indicating if connection should be open
11110Sstevel@tonic-gate  *              when password needs to change immediately
11120Sstevel@tonic-gate  *   passwd_mgmt:
11130Sstevel@tonic-gate  *              flag indicating if server supports password
11140Sstevel@tonic-gate  *              policy/management
11150Sstevel@tonic-gate  *
11160Sstevel@tonic-gate  * Output     : ns_ldap_error structure, which may contain
11170Sstevel@tonic-gate  *              password status and number of seconds until
11180Sstevel@tonic-gate  *              expired
11190Sstevel@tonic-gate  *
11200Sstevel@tonic-gate  * return rc:
11210Sstevel@tonic-gate  * NS_LDAP_EXTERNAL: error, connection should not open
11220Sstevel@tonic-gate  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
11230Sstevel@tonic-gate  * NS_LDAP_SUCCESS: OK to open connection
11240Sstevel@tonic-gate  *
11250Sstevel@tonic-gate  */
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate static int
11280Sstevel@tonic-gate process_pwd_mgmt(char *bind_type, int ldaprc,
11290Sstevel@tonic-gate 		LDAPControl **controls,
11300Sstevel@tonic-gate 		char *errmsg, ns_ldap_error_t **errorp,
11310Sstevel@tonic-gate 		int fail_if_new_pwd_reqd,
11320Sstevel@tonic-gate 		int passwd_mgmt)
11330Sstevel@tonic-gate {
11340Sstevel@tonic-gate 	char		errstr[MAXERROR];
11350Sstevel@tonic-gate 	LDAPControl	**ctrl = NULL;
11360Sstevel@tonic-gate 	int		exit_rc;
11370Sstevel@tonic-gate 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
11380Sstevel@tonic-gate 	int		sec_until_exp = 0;
11390Sstevel@tonic-gate 
11400Sstevel@tonic-gate 	/*
11410Sstevel@tonic-gate 	 * errmsg may be an empty string,
11420Sstevel@tonic-gate 	 * even if ldaprc is LDAP_SUCCESS,
11430Sstevel@tonic-gate 	 * free the empty string if that's the case
11440Sstevel@tonic-gate 	 */
11450Sstevel@tonic-gate 	if (errmsg &&
11464522Schinlong 	    (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
11470Sstevel@tonic-gate 		ldap_memfree(errmsg);
11480Sstevel@tonic-gate 		errmsg = NULL;
11490Sstevel@tonic-gate 	}
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate 	if (ldaprc != LDAP_SUCCESS) {
11520Sstevel@tonic-gate 		/*
11530Sstevel@tonic-gate 		 * try to map ldap rc and error message to
11540Sstevel@tonic-gate 		 * a password status
11550Sstevel@tonic-gate 		 */
11560Sstevel@tonic-gate 		if (errmsg) {
11570Sstevel@tonic-gate 			if (passwd_mgmt)
11580Sstevel@tonic-gate 				pwd_status =
11594522Schinlong 				    __s_api_set_passwd_status(
11604522Schinlong 				    ldaprc, errmsg);
11610Sstevel@tonic-gate 			ldap_memfree(errmsg);
11620Sstevel@tonic-gate 		}
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 		(void) snprintf(errstr, sizeof (errstr),
11654522Schinlong 		    gettext("openConnection: "
11664522Schinlong 		    "%s bind failed "
11674522Schinlong 		    "- %s"), bind_type, ldap_err2string(ldaprc));
11680Sstevel@tonic-gate 
11690Sstevel@tonic-gate 		if (pwd_status != NS_PASSWD_GOOD) {
11700Sstevel@tonic-gate 			MKERROR_PWD_MGMT(*errorp,
11714522Schinlong 			    ldaprc, strdup(errstr),
11724522Schinlong 			    pwd_status, 0, NULL);
11730Sstevel@tonic-gate 		} else {
11740Sstevel@tonic-gate 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
11756842Sth160488 			    NS_LDAP_MEMORY);
11760Sstevel@tonic-gate 		}
11770Sstevel@tonic-gate 		if (controls)
11780Sstevel@tonic-gate 			ldap_controls_free(controls);
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 		return (NS_LDAP_INTERNAL);
11810Sstevel@tonic-gate 	}
11820Sstevel@tonic-gate 
11830Sstevel@tonic-gate 	/*
11840Sstevel@tonic-gate 	 * ldaprc is LDAP_SUCCESS,
11850Sstevel@tonic-gate 	 * process the password management controls, if any
11860Sstevel@tonic-gate 	 */
11870Sstevel@tonic-gate 	exit_rc = NS_LDAP_SUCCESS;
11880Sstevel@tonic-gate 	if (controls && passwd_mgmt) {
11890Sstevel@tonic-gate 		/*
11900Sstevel@tonic-gate 		 * The control with the OID
11910Sstevel@tonic-gate 		 * 2.16.840.1.113730.3.4.4 (or
11920Sstevel@tonic-gate 		 * LDAP_CONTROL_PWEXPIRED, as defined
11930Sstevel@tonic-gate 		 * in the ldap.h header file) is the
11940Sstevel@tonic-gate 		 * expired password control.
11950Sstevel@tonic-gate 		 *
11960Sstevel@tonic-gate 		 * This control is used if the server
11970Sstevel@tonic-gate 		 * is configured to require users to
11980Sstevel@tonic-gate 		 * change their passwords when first
11990Sstevel@tonic-gate 		 * logging in and whenever the
12000Sstevel@tonic-gate 		 * passwords are reset.
12010Sstevel@tonic-gate 		 *
12020Sstevel@tonic-gate 		 * If the user is logging in for the
12030Sstevel@tonic-gate 		 * first time or if the user's
12040Sstevel@tonic-gate 		 * password has been reset, the
12050Sstevel@tonic-gate 		 * server sends this control to
12060Sstevel@tonic-gate 		 * indicate that the client needs to
12070Sstevel@tonic-gate 		 * change the password immediately.
12080Sstevel@tonic-gate 		 *
12090Sstevel@tonic-gate 		 * At this point, the only operation
12100Sstevel@tonic-gate 		 * that the client can perform is to
12110Sstevel@tonic-gate 		 * change the user's password. If the
12120Sstevel@tonic-gate 		 * client requests any other LDAP
12130Sstevel@tonic-gate 		 * operation, the server sends back
12140Sstevel@tonic-gate 		 * an LDAP_UNWILLING_TO_PERFORM
12150Sstevel@tonic-gate 		 * result code with an expired
12160Sstevel@tonic-gate 		 * password control.
12170Sstevel@tonic-gate 		 *
12180Sstevel@tonic-gate 		 * The control with the OID
12190Sstevel@tonic-gate 		 * 2.16.840.1.113730.3.4.5 (or
12200Sstevel@tonic-gate 		 * LDAP_CONTROL_PWEXPIRING, as
12210Sstevel@tonic-gate 		 * defined in the ldap.h header file)
12220Sstevel@tonic-gate 		 * is the password expiration warning
12230Sstevel@tonic-gate 		 * control.
12240Sstevel@tonic-gate 		 *
12250Sstevel@tonic-gate 		 * This control is used if the server
12260Sstevel@tonic-gate 		 * is configured to expire user
12270Sstevel@tonic-gate 		 * passwords after a certain amount
12280Sstevel@tonic-gate 		 * of time.
12290Sstevel@tonic-gate 		 *
12300Sstevel@tonic-gate 		 * The server sends this control back
12310Sstevel@tonic-gate 		 * to the client if the client binds
12320Sstevel@tonic-gate 		 * using a password that will soon
12330Sstevel@tonic-gate 		 * expire.  The ldctl_value field of
12340Sstevel@tonic-gate 		 * the LDAPControl structure
12350Sstevel@tonic-gate 		 * specifies the number of seconds
12360Sstevel@tonic-gate 		 * before the password will expire.
12370Sstevel@tonic-gate 		 */
12380Sstevel@tonic-gate 		for (ctrl = controls; *ctrl; ctrl++) {
12390Sstevel@tonic-gate 
12400Sstevel@tonic-gate 			if (strcmp((*ctrl)->ldctl_oid,
12414522Schinlong 			    LDAP_CONTROL_PWEXPIRED) == 0) {
12420Sstevel@tonic-gate 				/*
12430Sstevel@tonic-gate 				 * if the caller wants this bind
12440Sstevel@tonic-gate 				 * to fail, set up the error info.
12450Sstevel@tonic-gate 				 * If call to this function is
12460Sstevel@tonic-gate 				 * for searching the LDAP directory,
12470Sstevel@tonic-gate 				 * e.g., __ns_ldap_list(),
12480Sstevel@tonic-gate 				 * there's really no sense to
12490Sstevel@tonic-gate 				 * let a connection open and
12500Sstevel@tonic-gate 				 * then fail immediately afterward
12510Sstevel@tonic-gate 				 * on the LDAP search operation with
12520Sstevel@tonic-gate 				 * the LDAP_UNWILLING_TO_PERFORM rc
12530Sstevel@tonic-gate 				 */
12540Sstevel@tonic-gate 				pwd_status =
12554522Schinlong 				    NS_PASSWD_CHANGE_NEEDED;
12560Sstevel@tonic-gate 				if (fail_if_new_pwd_reqd) {
12570Sstevel@tonic-gate 					(void) snprintf(errstr,
12584522Schinlong 					    sizeof (errstr),
12594522Schinlong 					    gettext(
12604522Schinlong 					    "openConnection: "
12614522Schinlong 					    "%s bind "
12624522Schinlong 					    "failed "
12634522Schinlong 					    "- password "
12644522Schinlong 					    "expired. It "
12654522Schinlong 					    " needs to change "
12664522Schinlong 					    "immediately!"),
12674522Schinlong 					    bind_type);
12680Sstevel@tonic-gate 					MKERROR_PWD_MGMT(*errorp,
12694522Schinlong 					    LDAP_SUCCESS,
12704522Schinlong 					    strdup(errstr),
12714522Schinlong 					    pwd_status,
12724522Schinlong 					    0,
12734522Schinlong 					    NULL);
12740Sstevel@tonic-gate 					exit_rc = NS_LDAP_INTERNAL;
12750Sstevel@tonic-gate 				} else {
12760Sstevel@tonic-gate 					MKERROR_PWD_MGMT(*errorp,
12774522Schinlong 					    LDAP_SUCCESS,
12784522Schinlong 					    NULL,
12794522Schinlong 					    pwd_status,
12804522Schinlong 					    0,
12814522Schinlong 					    NULL);
12820Sstevel@tonic-gate 					exit_rc =
12834522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO;
12840Sstevel@tonic-gate 				}
12850Sstevel@tonic-gate 				break;
12860Sstevel@tonic-gate 			} else if (strcmp((*ctrl)->ldctl_oid,
12874522Schinlong 			    LDAP_CONTROL_PWEXPIRING) == 0) {
12880Sstevel@tonic-gate 				pwd_status =
12894522Schinlong 				    NS_PASSWD_ABOUT_TO_EXPIRE;
12900Sstevel@tonic-gate 				if ((*ctrl)->
12914522Schinlong 				    ldctl_value.bv_len > 0 &&
12924522Schinlong 				    (*ctrl)->
12934522Schinlong 				    ldctl_value.bv_val)
12940Sstevel@tonic-gate 					sec_until_exp =
12954522Schinlong 					    atoi((*ctrl)->
12964522Schinlong 					    ldctl_value.bv_val);
12970Sstevel@tonic-gate 				MKERROR_PWD_MGMT(*errorp,
12984522Schinlong 				    LDAP_SUCCESS,
12994522Schinlong 				    NULL,
13004522Schinlong 				    pwd_status,
13014522Schinlong 				    sec_until_exp,
13024522Schinlong 				    NULL);
13030Sstevel@tonic-gate 				exit_rc =
13044522Schinlong 				    NS_LDAP_SUCCESS_WITH_INFO;
13050Sstevel@tonic-gate 				break;
13060Sstevel@tonic-gate 			}
13070Sstevel@tonic-gate 		}
13080Sstevel@tonic-gate 	}
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 	if (controls)
13110Sstevel@tonic-gate 		ldap_controls_free(controls);
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	return (exit_rc);
13140Sstevel@tonic-gate }
13150Sstevel@tonic-gate 
13160Sstevel@tonic-gate static int
13176842Sth160488 ldap_in_nss_switch(char *db)
13180Sstevel@tonic-gate {
13190Sstevel@tonic-gate 	enum __nsw_parse_err		pserr;
13200Sstevel@tonic-gate 	struct __nsw_switchconfig	*conf;
13210Sstevel@tonic-gate 	struct __nsw_lookup		*lkp;
13220Sstevel@tonic-gate 	const char			*name;
13230Sstevel@tonic-gate 	int				found = 0;
13240Sstevel@tonic-gate 
13256842Sth160488 	conf = __nsw_getconfig(db, &pserr);
13260Sstevel@tonic-gate 	if (conf == NULL) {
13270Sstevel@tonic-gate 		return (-1);
13280Sstevel@tonic-gate 	}
13290Sstevel@tonic-gate 
13300Sstevel@tonic-gate 	/* check for skip and count other backends */
13310Sstevel@tonic-gate 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
13320Sstevel@tonic-gate 		name = lkp->service_name;
13330Sstevel@tonic-gate 		if (strcmp(name, "ldap") == 0) {
13340Sstevel@tonic-gate 			found = 1;
13350Sstevel@tonic-gate 			break;
13360Sstevel@tonic-gate 		}
13370Sstevel@tonic-gate 	}
1338*11411SSurya.Prakki@Sun.COM 	(void) __nsw_freeconfig(conf);
13390Sstevel@tonic-gate 	return (found);
13400Sstevel@tonic-gate }
13410Sstevel@tonic-gate 
13420Sstevel@tonic-gate static int
13430Sstevel@tonic-gate openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
13440Sstevel@tonic-gate 	int timeoutSec, ns_ldap_error_t **errorp,
13456842Sth160488 	int fail_if_new_pwd_reqd, int passwd_mgmt,
13466842Sth160488 	ns_conn_user_t *conn_user)
13470Sstevel@tonic-gate {
13486842Sth160488 	LDAP			*ld = NULL;
13496842Sth160488 	int			ldapVersion = LDAP_VERSION3;
13506842Sth160488 	int			derefOption = LDAP_DEREF_ALWAYS;
13516842Sth160488 	int			zero = 0;
13526842Sth160488 	int			timeoutMilliSec = timeoutSec * 1000;
13536842Sth160488 	uint16_t		port = USE_DEFAULT_PORT;
13546842Sth160488 	char			*s;
13556842Sth160488 	char			errstr[MAXERROR];
13566842Sth160488 
13576842Sth160488 	ns_ldap_return_code	ret_code = NS_LDAP_SUCCESS;
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 	*errorp = NULL;
13600Sstevel@tonic-gate 	*ldp = NULL;
13610Sstevel@tonic-gate 
13626842Sth160488 	/* determine if the host name contains a port number */
13636842Sth160488 	s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
13646842Sth160488 	s = strchr(s != NULL ? s : serverAddr, ':');
13656842Sth160488 	if (s != NULL) {
13666842Sth160488 		if (sscanf(s + 1, "%hu", &port) != 1) {
13676842Sth160488 			(void) snprintf(errstr,
13686842Sth160488 			    sizeof (errstr),
13696842Sth160488 			    gettext("openConnection: cannot "
13706842Sth160488 			    "convert %s into a valid "
13716842Sth160488 			    "port number for the "
13726842Sth160488 			    "%s server. A default value "
13736842Sth160488 			    "will be used."),
13746842Sth160488 			    s,
13756842Sth160488 			    serverAddr);
13760Sstevel@tonic-gate 			syslog(LOG_ERR, "libsldap: %s", errstr);
13770Sstevel@tonic-gate 		} else {
13786842Sth160488 			*s = '\0';
13790Sstevel@tonic-gate 		}
13800Sstevel@tonic-gate 	}
13810Sstevel@tonic-gate 
13826842Sth160488 	ret_code = createSession(auth,
13836842Sth160488 	    serverAddr,
13846842Sth160488 	    port,
13856842Sth160488 	    timeoutMilliSec,
13866842Sth160488 	    &ld,
13876842Sth160488 	    errorp);
13886842Sth160488 	if (s != NULL) {
13896842Sth160488 		*s = ':';
13906842Sth160488 	}
13916842Sth160488 	if (ret_code != NS_LDAP_SUCCESS) {
13926842Sth160488 		return (ret_code);
13936842Sth160488 	}
13946842Sth160488 
13956842Sth160488 	/* check to see if the underlying libsldap supports MT connection */
13966842Sth160488 	if (conn_user != NULL) {
13976842Sth160488 		int rc;
13986842Sth160488 
13996842Sth160488 		rc = __s_api_check_libldap_MT_conn_support(conn_user, ld,
14006842Sth160488 		    errorp);
14016842Sth160488 		if (rc != NS_LDAP_SUCCESS) {
14026842Sth160488 			(void) ldap_unbind(ld);
14036842Sth160488 			return (rc);
14046842Sth160488 		}
14056842Sth160488 	}
14066842Sth160488 
14070Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
14080Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
14090Sstevel@tonic-gate 	/*
14100Sstevel@tonic-gate 	 * set LDAP_OPT_REFERRALS to OFF.
14110Sstevel@tonic-gate 	 * This library will handle the referral itself
14120Sstevel@tonic-gate 	 * based on API flags or configuration file
14130Sstevel@tonic-gate 	 * specification. If this option is not set
14140Sstevel@tonic-gate 	 * to OFF, libldap will never pass the
14150Sstevel@tonic-gate 	 * referral info up to this library
14160Sstevel@tonic-gate 	 */
14170Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
14180Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
14190Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
14200Sstevel@tonic-gate 	/* setup TCP/IP connect timeout */
14210Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
14224522Schinlong 	    &timeoutMilliSec);
14230Sstevel@tonic-gate 	/* retry if LDAP I/O was interrupted */
14240Sstevel@tonic-gate 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
14250Sstevel@tonic-gate 
14266842Sth160488 	ret_code = performBind(auth,
14276842Sth160488 	    ld,
14286842Sth160488 	    timeoutSec,
14296842Sth160488 	    errorp,
14306842Sth160488 	    fail_if_new_pwd_reqd,
14316842Sth160488 	    passwd_mgmt);
14320Sstevel@tonic-gate 
14336842Sth160488 	if (ret_code == NS_LDAP_SUCCESS ||
14346842Sth160488 	    ret_code == NS_LDAP_SUCCESS_WITH_INFO) {
14356842Sth160488 		*ldp = ld;
14360Sstevel@tonic-gate 	}
14370Sstevel@tonic-gate 
14386842Sth160488 	return (ret_code);
14390Sstevel@tonic-gate }
14400Sstevel@tonic-gate 
14410Sstevel@tonic-gate /*
14420Sstevel@tonic-gate  * FUNCTION:	__s_api_getDefaultAuth
14430Sstevel@tonic-gate  *
14440Sstevel@tonic-gate  *	Constructs a credential for authentication using the config module.
14450Sstevel@tonic-gate  *
14460Sstevel@tonic-gate  * RETURN VALUES:
14470Sstevel@tonic-gate  *
14480Sstevel@tonic-gate  * NS_LDAP_SUCCESS	If successful
14490Sstevel@tonic-gate  * NS_LDAP_CONFIG	If there are any config errors.
14500Sstevel@tonic-gate  * NS_LDAP_MEMORY	Memory errors.
14510Sstevel@tonic-gate  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
14520Sstevel@tonic-gate  *			not build a new authp.
14530Sstevel@tonic-gate  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
14540Sstevel@tonic-gate  *			necessary fields of a cred for a given auth method
14550Sstevel@tonic-gate  *			are not provided.
14560Sstevel@tonic-gate  * INPUT:
14570Sstevel@tonic-gate  *
14580Sstevel@tonic-gate  * cLevel	Currently requested credential level to be tried
14590Sstevel@tonic-gate  *
14600Sstevel@tonic-gate  * aMethod	Currently requested authentication method to be tried
14610Sstevel@tonic-gate  *
14629576SJulian.Pullen@Sun.COM  * getAdmin	If non 0,  get Admin -i.e., not proxyAgent- DN and password
14639576SJulian.Pullen@Sun.COM  *
14640Sstevel@tonic-gate  * OUTPUT:
14650Sstevel@tonic-gate  *
14660Sstevel@tonic-gate  * authp		authentication method to use.
14670Sstevel@tonic-gate  */
14680Sstevel@tonic-gate static int
14690Sstevel@tonic-gate __s_api_getDefaultAuth(
14700Sstevel@tonic-gate 	int	*cLevel,
14710Sstevel@tonic-gate 	ns_auth_t *aMethod,
14729576SJulian.Pullen@Sun.COM 	ns_cred_t **authp,
14739576SJulian.Pullen@Sun.COM 	int	getAdmin)
14740Sstevel@tonic-gate {
14750Sstevel@tonic-gate 	void		**paramVal = NULL;
14760Sstevel@tonic-gate 	char		*modparamVal = NULL;
14770Sstevel@tonic-gate 	int		getUid = 0;
14780Sstevel@tonic-gate 	int		getPasswd = 0;
14790Sstevel@tonic-gate 	int		getCertpath = 0;
14800Sstevel@tonic-gate 	int		rc = 0;
14810Sstevel@tonic-gate 	ns_ldap_error_t	*errorp = NULL;
14829576SJulian.Pullen@Sun.COM 	UnixCred_t	*AdminCred = NULL;
14830Sstevel@tonic-gate 
14840Sstevel@tonic-gate #ifdef DEBUG
14850Sstevel@tonic-gate 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
14860Sstevel@tonic-gate #endif
14870Sstevel@tonic-gate 
14880Sstevel@tonic-gate 	if (aMethod == NULL) {
14890Sstevel@tonic-gate 		/* Require an Auth */
14900Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 	}
14930Sstevel@tonic-gate 	/*
14942830Sdjl 	 * credential level "self" can work with auth method sasl/GSSAPI only
14950Sstevel@tonic-gate 	 */
14962830Sdjl 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
14974522Schinlong 	    aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
14980Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
15010Sstevel@tonic-gate 	if ((*authp) == NULL)
15020Sstevel@tonic-gate 		return (NS_LDAP_MEMORY);
15030Sstevel@tonic-gate 
15040Sstevel@tonic-gate 	(*authp)->auth = *aMethod;
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	switch (aMethod->type) {
15070Sstevel@tonic-gate 		case NS_LDAP_AUTH_NONE:
15080Sstevel@tonic-gate 			return (NS_LDAP_SUCCESS);
15090Sstevel@tonic-gate 		case NS_LDAP_AUTH_SIMPLE:
15100Sstevel@tonic-gate 			getUid++;
15110Sstevel@tonic-gate 			getPasswd++;
15120Sstevel@tonic-gate 			break;
15130Sstevel@tonic-gate 		case NS_LDAP_AUTH_SASL:
15140Sstevel@tonic-gate 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
15150Sstevel@tonic-gate 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
15160Sstevel@tonic-gate 				getUid++;
15170Sstevel@tonic-gate 				getPasswd++;
15182830Sdjl 			} else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
15190Sstevel@tonic-gate 				(void) __ns_ldap_freeCred(authp);
15200Sstevel@tonic-gate 				return (NS_LDAP_INVALID_PARAM);
15210Sstevel@tonic-gate 			}
15220Sstevel@tonic-gate 			break;
15230Sstevel@tonic-gate 		case NS_LDAP_AUTH_TLS:
15240Sstevel@tonic-gate 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
15250Sstevel@tonic-gate 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
15260Sstevel@tonic-gate 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
15270Sstevel@tonic-gate 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
15280Sstevel@tonic-gate 				getUid++;
15290Sstevel@tonic-gate 				getPasswd++;
15300Sstevel@tonic-gate 				getCertpath++;
15310Sstevel@tonic-gate 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
15320Sstevel@tonic-gate 				getCertpath++;
15330Sstevel@tonic-gate 			} else {
15340Sstevel@tonic-gate 				(void) __ns_ldap_freeCred(authp);
15350Sstevel@tonic-gate 				return (NS_LDAP_INVALID_PARAM);
15360Sstevel@tonic-gate 			}
15370Sstevel@tonic-gate 			break;
15380Sstevel@tonic-gate 	}
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 	if (getUid) {
15410Sstevel@tonic-gate 		paramVal = NULL;
15429576SJulian.Pullen@Sun.COM 		if (getAdmin) {
15439576SJulian.Pullen@Sun.COM 			/*
15449576SJulian.Pullen@Sun.COM 			 * Assume AdminCred has been retrieved from
15459576SJulian.Pullen@Sun.COM 			 * ldap_cachemgr already. It will not work
15469576SJulian.Pullen@Sun.COM 			 * without userID or password. Flags getUid
15479576SJulian.Pullen@Sun.COM 			 * and getPasswd should always be set
15489576SJulian.Pullen@Sun.COM 			 * together.
15499576SJulian.Pullen@Sun.COM 			 */
15509576SJulian.Pullen@Sun.COM 			AdminCred = calloc(1, sizeof (UnixCred_t));
15519576SJulian.Pullen@Sun.COM 			if (AdminCred == NULL) {
15529576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
15539576SJulian.Pullen@Sun.COM 				return (NS_LDAP_MEMORY);
15549576SJulian.Pullen@Sun.COM 			}
15559576SJulian.Pullen@Sun.COM 
15569576SJulian.Pullen@Sun.COM 			rc = requestAdminCred(&AdminCred, &errorp);
15579576SJulian.Pullen@Sun.COM 			if (rc != NS_LDAP_SUCCESS) {
15589576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
15599576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeUnixCred(&AdminCred);
15609576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeError(&errorp);
15619576SJulian.Pullen@Sun.COM 				return (rc);
15629576SJulian.Pullen@Sun.COM 			}
15630Sstevel@tonic-gate 
15649576SJulian.Pullen@Sun.COM 			if (AdminCred->userID == NULL) {
15659576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
15669576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeUnixCred(&AdminCred);
15679576SJulian.Pullen@Sun.COM 				return (NS_LDAP_INVALID_PARAM);
15689576SJulian.Pullen@Sun.COM 			}
15699576SJulian.Pullen@Sun.COM 			(*authp)->cred.unix_cred.userID = AdminCred->userID;
15709576SJulian.Pullen@Sun.COM 			AdminCred->userID = NULL;
15719576SJulian.Pullen@Sun.COM 		} else {
15729576SJulian.Pullen@Sun.COM 			rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
15739576SJulian.Pullen@Sun.COM 			    &paramVal, &errorp);
15749576SJulian.Pullen@Sun.COM 			if (rc != NS_LDAP_SUCCESS) {
15759576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
15769576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeError(&errorp);
15779576SJulian.Pullen@Sun.COM 				return (rc);
15789576SJulian.Pullen@Sun.COM 			}
15799576SJulian.Pullen@Sun.COM 
15809576SJulian.Pullen@Sun.COM 			if (paramVal == NULL || *paramVal == NULL) {
15819576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
15829576SJulian.Pullen@Sun.COM 				return (NS_LDAP_INVALID_PARAM);
15839576SJulian.Pullen@Sun.COM 			}
15849576SJulian.Pullen@Sun.COM 
15859576SJulian.Pullen@Sun.COM 			(*authp)->cred.unix_cred.userID =
15869576SJulian.Pullen@Sun.COM 			    strdup((char *)*paramVal);
15879576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeParam(&paramVal);
15880Sstevel@tonic-gate 		}
15890Sstevel@tonic-gate 		if ((*authp)->cred.unix_cred.userID == NULL) {
15900Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
15919576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeUnixCred(&AdminCred);
15920Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
15930Sstevel@tonic-gate 		}
15940Sstevel@tonic-gate 	}
15950Sstevel@tonic-gate 	if (getPasswd) {
15960Sstevel@tonic-gate 		paramVal = NULL;
15979576SJulian.Pullen@Sun.COM 		if (getAdmin) {
15989576SJulian.Pullen@Sun.COM 			/*
15999576SJulian.Pullen@Sun.COM 			 * Assume AdminCred has been retrieved from
16009576SJulian.Pullen@Sun.COM 			 * ldap_cachemgr already. It will not work
16019576SJulian.Pullen@Sun.COM 			 * without the userID anyway because for
16029576SJulian.Pullen@Sun.COM 			 * getting admin credential, flags getUid
16039576SJulian.Pullen@Sun.COM 			 * and getPasswd should always be set
16049576SJulian.Pullen@Sun.COM 			 * together.
16059576SJulian.Pullen@Sun.COM 			 */
16069576SJulian.Pullen@Sun.COM 			if (AdminCred == NULL || AdminCred->passwd == NULL) {
16079576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
16089576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeUnixCred(&AdminCred);
16099576SJulian.Pullen@Sun.COM 				return (NS_LDAP_INVALID_PARAM);
16109576SJulian.Pullen@Sun.COM 			}
16119576SJulian.Pullen@Sun.COM 			modparamVal = dvalue(AdminCred->passwd);
16129576SJulian.Pullen@Sun.COM 		} else {
16139576SJulian.Pullen@Sun.COM 			rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
16149576SJulian.Pullen@Sun.COM 			    &paramVal, &errorp);
16159576SJulian.Pullen@Sun.COM 			if (rc != NS_LDAP_SUCCESS) {
16169576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
16179576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeError(&errorp);
16189576SJulian.Pullen@Sun.COM 				return (rc);
16199576SJulian.Pullen@Sun.COM 			}
16209576SJulian.Pullen@Sun.COM 
16219576SJulian.Pullen@Sun.COM 			if (paramVal == NULL || *paramVal == NULL) {
16229576SJulian.Pullen@Sun.COM 				(void) __ns_ldap_freeCred(authp);
16239576SJulian.Pullen@Sun.COM 				return (NS_LDAP_INVALID_PARAM);
16249576SJulian.Pullen@Sun.COM 			}
16259576SJulian.Pullen@Sun.COM 
16269576SJulian.Pullen@Sun.COM 			modparamVal = dvalue((char *)*paramVal);
16279576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeParam(&paramVal);
16280Sstevel@tonic-gate 		}
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
16310Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
16329576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeUnixCred(&AdminCred);
16330Sstevel@tonic-gate 			if (modparamVal != NULL)
16340Sstevel@tonic-gate 				free(modparamVal);
16350Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
16360Sstevel@tonic-gate 		}
16370Sstevel@tonic-gate 
16380Sstevel@tonic-gate 		(*authp)->cred.unix_cred.passwd = modparamVal;
16390Sstevel@tonic-gate 	}
16400Sstevel@tonic-gate 	if (getCertpath) {
16410Sstevel@tonic-gate 		paramVal = NULL;
16420Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
16434522Schinlong 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
16440Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
16459576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeUnixCred(&AdminCred);
16460Sstevel@tonic-gate 			(void) __ns_ldap_freeError(&errorp);
16470Sstevel@tonic-gate 			*authp = NULL;
16480Sstevel@tonic-gate 			return (rc);
16490Sstevel@tonic-gate 		}
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 		if (paramVal == NULL || *paramVal == NULL) {
16520Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
16539576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeUnixCred(&AdminCred);
16540Sstevel@tonic-gate 			*authp = NULL;
16550Sstevel@tonic-gate 			return (NS_LDAP_INVALID_PARAM);
16560Sstevel@tonic-gate 		}
16570Sstevel@tonic-gate 
16580Sstevel@tonic-gate 		(*authp)->hostcertpath = strdup((char *)*paramVal);
16590Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
16600Sstevel@tonic-gate 		if ((*authp)->hostcertpath == NULL) {
16610Sstevel@tonic-gate 			(void) __ns_ldap_freeCred(authp);
16629576SJulian.Pullen@Sun.COM 			(void) __ns_ldap_freeUnixCred(&AdminCred);
16630Sstevel@tonic-gate 			*authp = NULL;
16640Sstevel@tonic-gate 			return (NS_LDAP_MEMORY);
16650Sstevel@tonic-gate 		}
16660Sstevel@tonic-gate 	}
16679576SJulian.Pullen@Sun.COM 	(void) __ns_ldap_freeUnixCred(&AdminCred);
16680Sstevel@tonic-gate 	return (NS_LDAP_SUCCESS);
16690Sstevel@tonic-gate }
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate /*
16726842Sth160488  * FUNCTION:	getConnection
16730Sstevel@tonic-gate  *
16746842Sth160488  *	internal version of __s_api_getConnection()
16750Sstevel@tonic-gate  */
16766842Sth160488 static int
16776842Sth160488 getConnection(
16780Sstevel@tonic-gate 	const char *server,
16790Sstevel@tonic-gate 	const int flags,
16800Sstevel@tonic-gate 	const ns_cred_t *cred,		/* credentials for bind */
16810Sstevel@tonic-gate 	ConnectionID *sessionId,
16820Sstevel@tonic-gate 	Connection **session,
16830Sstevel@tonic-gate 	ns_ldap_error_t **errorp,
16841179Svv149972 	int fail_if_new_pwd_reqd,
16856842Sth160488 	int nopasswd_acct_mgmt,
16866842Sth160488 	ns_conn_user_t *conn_user)
16870Sstevel@tonic-gate {
16880Sstevel@tonic-gate 	char		errmsg[MAXERROR];
16890Sstevel@tonic-gate 	ns_auth_t	**aMethod = NULL;
16900Sstevel@tonic-gate 	ns_auth_t	**aNext = NULL;
16910Sstevel@tonic-gate 	int		**cLevel = NULL;
16920Sstevel@tonic-gate 	int		**cNext = NULL;
16930Sstevel@tonic-gate 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
16940Sstevel@tonic-gate 	int		rc;
16950Sstevel@tonic-gate 	Connection	*con = NULL;
16960Sstevel@tonic-gate 	int		sec = 1;
16970Sstevel@tonic-gate 	ns_cred_t 	*authp = NULL;
16980Sstevel@tonic-gate 	ns_cred_t	anon;
16992830Sdjl 	int		version = NS_LDAP_V2, self_gssapi_only = 0;
17000Sstevel@tonic-gate 	void		**paramVal = NULL;
17011687Sjanga 	char		**badSrvrs = NULL; /* List of problem hostnames */
17020Sstevel@tonic-gate 
17030Sstevel@tonic-gate 	if ((session == NULL) || (sessionId == NULL)) {
17040Sstevel@tonic-gate 		return (NS_LDAP_INVALID_PARAM);
17050Sstevel@tonic-gate 	}
17060Sstevel@tonic-gate 	*session = NULL;
17070Sstevel@tonic-gate 
17086842Sth160488 	/* reuse MT connection if needed and if available */
17096842Sth160488 	if (conn_user != NULL) {
17106842Sth160488 		rc = __s_api_conn_mt_get(server, flags, cred, session, errorp,
17116842Sth160488 		    conn_user);
17126842Sth160488 		if (rc != NS_LDAP_NOTFOUND)
17136842Sth160488 			return (rc);
17140Sstevel@tonic-gate 	}
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	/* get profile version number */
17170Sstevel@tonic-gate 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
17184522Schinlong 	    &paramVal, errorp)) != NS_LDAP_SUCCESS)
17190Sstevel@tonic-gate 		return (rc);
17200Sstevel@tonic-gate 	if (paramVal == NULL) {
17210Sstevel@tonic-gate 		(void) sprintf(errmsg, gettext("getConnection: no file "
17224522Schinlong 		    "version"));
17230Sstevel@tonic-gate 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
17244522Schinlong 		    NS_LDAP_CONFIG);
17250Sstevel@tonic-gate 		return (NS_LDAP_CONFIG);
17260Sstevel@tonic-gate 	}
17270Sstevel@tonic-gate 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
17280Sstevel@tonic-gate 		version = NS_LDAP_V1;
17290Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&paramVal);
17300Sstevel@tonic-gate 
17310Sstevel@tonic-gate 	/* Get the bind timeout value */
17320Sstevel@tonic-gate 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
17330Sstevel@tonic-gate 	if (paramVal != NULL && *paramVal != NULL) {
17340Sstevel@tonic-gate 		timeoutSec = **((int **)paramVal);
17350Sstevel@tonic-gate 		(void) __ns_ldap_freeParam(&paramVal);
17360Sstevel@tonic-gate 	}
17370Sstevel@tonic-gate 	if (*errorp)
17380Sstevel@tonic-gate 		(void) __ns_ldap_freeError(errorp);
17390Sstevel@tonic-gate 
17400Sstevel@tonic-gate 	if (cred == NULL) {
17410Sstevel@tonic-gate 		/* Get the authentication method list */
17420Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
17434522Schinlong 		    (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
17440Sstevel@tonic-gate 			return (rc);
17450Sstevel@tonic-gate 		if (aMethod == NULL) {
17460Sstevel@tonic-gate 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
17470Sstevel@tonic-gate 			if (aMethod == NULL)
17480Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
17490Sstevel@tonic-gate 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
17500Sstevel@tonic-gate 			if (aMethod[0] == NULL) {
17510Sstevel@tonic-gate 				free(aMethod);
17520Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
17530Sstevel@tonic-gate 			}
17540Sstevel@tonic-gate 			if (version == NS_LDAP_V1)
17550Sstevel@tonic-gate 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
17560Sstevel@tonic-gate 			else {
17570Sstevel@tonic-gate 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
17580Sstevel@tonic-gate 				(aMethod[0])->saslmech =
17594522Schinlong 				    NS_LDAP_SASL_DIGEST_MD5;
17600Sstevel@tonic-gate 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
17610Sstevel@tonic-gate 			}
17620Sstevel@tonic-gate 		}
17630Sstevel@tonic-gate 
17640Sstevel@tonic-gate 		/* Get the credential level list */
17650Sstevel@tonic-gate 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
17664522Schinlong 		    (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
17670Sstevel@tonic-gate 			(void) __ns_ldap_freeParam((void ***)&aMethod);
17680Sstevel@tonic-gate 			return (rc);
17690Sstevel@tonic-gate 		}
17700Sstevel@tonic-gate 		if (cLevel == NULL) {
17710Sstevel@tonic-gate 			cLevel = (int **)calloc(2, sizeof (int *));
17720Sstevel@tonic-gate 			if (cLevel == NULL)
17730Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
17740Sstevel@tonic-gate 			cLevel[0] = (int *)calloc(1, sizeof (int));
17750Sstevel@tonic-gate 			if (cLevel[0] == NULL)
17760Sstevel@tonic-gate 				return (NS_LDAP_MEMORY);
17770Sstevel@tonic-gate 			if (version == NS_LDAP_V1)
17780Sstevel@tonic-gate 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
17790Sstevel@tonic-gate 			else
17800Sstevel@tonic-gate 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
17810Sstevel@tonic-gate 		}
17820Sstevel@tonic-gate 	}
17830Sstevel@tonic-gate 
17840Sstevel@tonic-gate 	/* setup the anon credential for anonymous connection */
17850Sstevel@tonic-gate 	(void) memset(&anon, 0, sizeof (ns_cred_t));
17860Sstevel@tonic-gate 	anon.auth.type = NS_LDAP_AUTH_NONE;
17870Sstevel@tonic-gate 
17886812Sraf 	for (;;) {
17890Sstevel@tonic-gate 		if (cred != NULL) {
17900Sstevel@tonic-gate 			/* using specified auth method */
17910Sstevel@tonic-gate 			rc = makeConnection(&con, server, cred,
17924522Schinlong 			    sessionId, timeoutSec, errorp,
17934522Schinlong 			    fail_if_new_pwd_reqd,
17946842Sth160488 			    nopasswd_acct_mgmt, flags, &badSrvrs, conn_user);
17954387Smj162486 			/* not using bad server if credentials were supplied */
17964387Smj162486 			if (badSrvrs && *badSrvrs) {
17974387Smj162486 				__s_api_free2dArray(badSrvrs);
17984387Smj162486 				badSrvrs = NULL;
17994387Smj162486 			}
18000Sstevel@tonic-gate 			if (rc == NS_LDAP_SUCCESS ||
18014522Schinlong 			    rc == NS_LDAP_SUCCESS_WITH_INFO) {
18020Sstevel@tonic-gate 				*session = con;
18030Sstevel@tonic-gate 				break;
18040Sstevel@tonic-gate 			}
18050Sstevel@tonic-gate 		} else {
18062830Sdjl 			self_gssapi_only = __s_api_self_gssapi_only_get();
18070Sstevel@tonic-gate 			/* for every cred level */
18080Sstevel@tonic-gate 			for (cNext = cLevel; *cNext != NULL; cNext++) {
18092830Sdjl 				if (self_gssapi_only &&
18104522Schinlong 				    **cNext != NS_LDAP_CRED_SELF)
18112830Sdjl 					continue;
18120Sstevel@tonic-gate 				if (**cNext == NS_LDAP_CRED_ANON) {
18131687Sjanga 					/*
18141687Sjanga 					 * make connection anonymously
18151687Sjanga 					 * Free the down server list before
18161687Sjanga 					 * looping through
18171687Sjanga 					 */
18181687Sjanga 					if (badSrvrs && *badSrvrs) {
18191687Sjanga 						__s_api_free2dArray(badSrvrs);
18201687Sjanga 						badSrvrs = NULL;
18211687Sjanga 					}
18220Sstevel@tonic-gate 					rc = makeConnection(&con, server, &anon,
18234522Schinlong 					    sessionId, timeoutSec, errorp,
18244522Schinlong 					    fail_if_new_pwd_reqd,
18254522Schinlong 					    nopasswd_acct_mgmt, flags,
18266842Sth160488 					    &badSrvrs, conn_user);
18270Sstevel@tonic-gate 					if (rc == NS_LDAP_SUCCESS ||
18284522Schinlong 					    rc ==
18294522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO) {
18300Sstevel@tonic-gate 						*session = con;
18310Sstevel@tonic-gate 						goto done;
18320Sstevel@tonic-gate 					}
18330Sstevel@tonic-gate 					continue;
18340Sstevel@tonic-gate 				}
18350Sstevel@tonic-gate 				/* for each cred level */
18360Sstevel@tonic-gate 				for (aNext = aMethod; *aNext != NULL; aNext++) {
18372830Sdjl 					if (self_gssapi_only &&
18384522Schinlong 					    (*aNext)->saslmech !=
18394522Schinlong 					    NS_LDAP_SASL_GSSAPI)
18402830Sdjl 						continue;
18412830Sdjl 					/*
18422830Sdjl 					 * self coexists with sasl/GSSAPI only
18432830Sdjl 					 * and non-self coexists with non-gssapi
18442830Sdjl 					 * only
18452830Sdjl 					 */
18462830Sdjl 					if ((**cNext == NS_LDAP_CRED_SELF &&
18474522Schinlong 					    (*aNext)->saslmech !=
18484522Schinlong 					    NS_LDAP_SASL_GSSAPI) ||
18494522Schinlong 					    (**cNext != NS_LDAP_CRED_SELF &&
18504522Schinlong 					    (*aNext)->saslmech ==
18514522Schinlong 					    NS_LDAP_SASL_GSSAPI))
18522830Sdjl 						continue;
18530Sstevel@tonic-gate 					/* make connection and authenticate */
18540Sstevel@tonic-gate 					/* with default credentials */
18550Sstevel@tonic-gate 					authp = NULL;
18560Sstevel@tonic-gate 					rc = __s_api_getDefaultAuth(*cNext,
18579576SJulian.Pullen@Sun.COM 					    *aNext, &authp,
18589576SJulian.Pullen@Sun.COM 					    flags & NS_LDAP_READ_SHADOW);
18590Sstevel@tonic-gate 					if (rc != NS_LDAP_SUCCESS) {
18600Sstevel@tonic-gate 						continue;
18610Sstevel@tonic-gate 					}
18621687Sjanga 					/*
18631687Sjanga 					 * Free the down server list before
18641687Sjanga 					 * looping through
18651687Sjanga 					 */
18661687Sjanga 					if (badSrvrs && *badSrvrs) {
18671687Sjanga 						__s_api_free2dArray(badSrvrs);
18681687Sjanga 						badSrvrs = NULL;
18691687Sjanga 					}
18700Sstevel@tonic-gate 					rc = makeConnection(&con, server, authp,
18714522Schinlong 					    sessionId, timeoutSec, errorp,
18724522Schinlong 					    fail_if_new_pwd_reqd,
18734522Schinlong 					    nopasswd_acct_mgmt, flags,
18746842Sth160488 					    &badSrvrs, conn_user);
18750Sstevel@tonic-gate 					(void) __ns_ldap_freeCred(&authp);
18760Sstevel@tonic-gate 					if (rc == NS_LDAP_SUCCESS ||
18774522Schinlong 					    rc ==
18784522Schinlong 					    NS_LDAP_SUCCESS_WITH_INFO) {
18790Sstevel@tonic-gate 						*session = con;
18800Sstevel@tonic-gate 						goto done;
18810Sstevel@tonic-gate 					}
18820Sstevel@tonic-gate 				}
18830Sstevel@tonic-gate 			}
18840Sstevel@tonic-gate 		}
18850Sstevel@tonic-gate 		if (flags & NS_LDAP_HARD) {
18860Sstevel@tonic-gate 			if (sec < LDAPMAXHARDLOOKUPTIME)
18870Sstevel@tonic-gate 				sec *= 2;
18886812Sraf 			(void) sleep(sec);
18890Sstevel@tonic-gate 		} else {
18900Sstevel@tonic-gate 			break;
18910Sstevel@tonic-gate 		}
18920Sstevel@tonic-gate 	}
18930Sstevel@tonic-gate 
18940Sstevel@tonic-gate done:
18952830Sdjl 	if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
18962830Sdjl 		/*
18972830Sdjl 		 * self_gssapi_only is true but no self/sasl/gssapi is
18982830Sdjl 		 * configured
18992830Sdjl 		 */
19002830Sdjl 		rc = NS_LDAP_CONFIG;
19012830Sdjl 	}
19022830Sdjl 
19030Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&aMethod);
19040Sstevel@tonic-gate 	(void) __ns_ldap_freeParam((void ***)&cLevel);
19051687Sjanga 
19061687Sjanga 	if (badSrvrs && *badSrvrs) {
19071687Sjanga 		/*
19081687Sjanga 		 * At this point, either we have a successful
19091687Sjanga 		 * connection or exhausted all the possible auths.
19101687Sjanga 		 * and creds. Mark the problem servers as down
19111687Sjanga 		 * so that the problem servers are not contacted
19121687Sjanga 		 * again until the refresh_ttl expires.
19131687Sjanga 		 */
19141687Sjanga 		(void) __s_api_removeBadServers(badSrvrs);
19151687Sjanga 		__s_api_free2dArray(badSrvrs);
19161687Sjanga 	}
19170Sstevel@tonic-gate 	return (rc);
19180Sstevel@tonic-gate }
19190Sstevel@tonic-gate 
19206842Sth160488 /*
19216842Sth160488  * FUNCTION:	__s_api_getConnection
19226842Sth160488  *
19236842Sth160488  *	Bind to the specified server or one from the server
19246842Sth160488  *	list and return the pointer.
19256842Sth160488  *
19266842Sth160488  *	This function can rebind or not (NS_LDAP_HARD), it can require a
19276842Sth160488  *	credential or bind anonymously
19286842Sth160488  *
19296842Sth160488  *	This function follows the DUA configuration schema algorithm
19306842Sth160488  *
19316842Sth160488  * RETURN VALUES:
19326842Sth160488  *
19336842Sth160488  * NS_LDAP_SUCCESS	A connection was made successfully.
19346842Sth160488  * NS_LDAP_SUCCESS_WITH_INFO
19356842Sth160488  * 			A connection was made successfully, but with
19366842Sth160488  *			password management info in *errorp
19376842Sth160488  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
19386842Sth160488  * NS_LDAP_CONFIG	If there are any config errors.
19396842Sth160488  * NS_LDAP_MEMORY	Memory errors.
19406842Sth160488  * NS_LDAP_INTERNAL	If there was a ldap error.
19416842Sth160488  *
19426842Sth160488  * INPUT:
19436842Sth160488  *
19446842Sth160488  * server	Bind to this LDAP server only
19456842Sth160488  * flags	If NS_LDAP_HARD is set function will not return until it has
19466842Sth160488  *		a connection unless there is a authentication problem.
19476842Sth160488  *		If NS_LDAP_NEW_CONN is set the function must force a new
19486842Sth160488  *              connection to be created
19496842Sth160488  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
19506842Sth160488  * auth		Credentials for bind. This could be NULL in which case
19516842Sth160488  *		a default cred built from the config module is used.
19526842Sth160488  * sessionId	cookie that points to a previous session
19536842Sth160488  * fail_if_new_pwd_reqd
19546842Sth160488  *		a flag indicating this function should fail if the passwd
19556842Sth160488  *		in auth needs to change immediately
19566842Sth160488  * nopasswd_acct_mgmt
19576842Sth160488  *		a flag indicating that makeConnection should check before
19586842Sth160488  *		binding if server supports LDAP V3 password less
19596842Sth160488  *		account management
19606842Sth160488  *
19616842Sth160488  * OUTPUT:
19626842Sth160488  *
19636842Sth160488  * session	pointer to a session with connection information
19646842Sth160488  * errorp	Set if there are any INTERNAL, or CONFIG error.
19656842Sth160488  */
19666842Sth160488 int
19676842Sth160488 __s_api_getConnection(
19686842Sth160488 	const char *server,
19696842Sth160488 	const int flags,
19706842Sth160488 	const ns_cred_t *cred,		/* credentials for bind */
19716842Sth160488 	ConnectionID *sessionId,
19726842Sth160488 	Connection **session,
19736842Sth160488 	ns_ldap_error_t **errorp,
19746842Sth160488 	int fail_if_new_pwd_reqd,
19756842Sth160488 	int nopasswd_acct_mgmt,
19766842Sth160488 	ns_conn_user_t *conn_user)
19776842Sth160488 {
19786842Sth160488 	int rc;
19796842Sth160488 
19806842Sth160488 	rc = getConnection(server, flags, cred, sessionId, session,
19816842Sth160488 	    errorp, fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
19826842Sth160488 	    conn_user);
19836842Sth160488 
19846842Sth160488 	if (rc != NS_LDAP_SUCCESS && rc != NS_LDAP_SUCCESS_WITH_INFO) {
19856842Sth160488 		if (conn_user != NULL && conn_user->conn_mt != NULL)
19866842Sth160488 			__s_api_conn_mt_remove(conn_user, rc, errorp);
19876842Sth160488 	}
19886842Sth160488 
19896842Sth160488 	return (rc);
19906842Sth160488 }
19916842Sth160488 
19926842Sth160488 void
19936842Sth160488 __s_api_free_sessionPool()
19940Sstevel@tonic-gate {
19950Sstevel@tonic-gate 	int id;
19960Sstevel@tonic-gate 
19976842Sth160488 	(void) mutex_lock(&sessionPoolLock);
19986842Sth160488 
19990Sstevel@tonic-gate 	if (sessionPool != NULL) {
20000Sstevel@tonic-gate 		for (id = 0; id < sessionPoolSize; id++)
20010Sstevel@tonic-gate 			_DropConnection(id + CONID_OFFSET, 0, 1);
20020Sstevel@tonic-gate 		free(sessionPool);
20030Sstevel@tonic-gate 		sessionPool = NULL;
20040Sstevel@tonic-gate 		sessionPoolSize = 0;
20050Sstevel@tonic-gate 	}
20066842Sth160488 	(void) mutex_unlock(&sessionPoolLock);
20076842Sth160488 }
20086842Sth160488 
20096842Sth160488 /*
20106842Sth160488  * This function initializes a TLS LDAP session. On success LDAP* is returned
20116842Sth160488  * (pointed by *ldp). Otherwise, the function returns an NS error code and
20126842Sth160488  * provide an additional info pointed by *errorp.
20136842Sth160488  */
20146842Sth160488 static
20156842Sth160488 ns_ldap_return_code
20166842Sth160488 createTLSSession(const ns_cred_t *auth, const char *serverAddr,
20176842Sth160488 		    uint16_t port, int timeoutMilliSec,
20186842Sth160488 		    LDAP **ldp, ns_ldap_error_t **errorp)
20196842Sth160488 {
20206842Sth160488 	const char	*hostcertpath;
20216842Sth160488 	char		*alloc_hcp = NULL, errstr[MAXERROR];
20226842Sth160488 	int		ldap_rc;
20236842Sth160488 
20246842Sth160488 #ifdef DEBUG
20256842Sth160488 	(void) fprintf(stderr, "tid= %d: +++TLS transport\n",
20266842Sth160488 	    thr_self());
20276842Sth160488 #endif /* DEBUG */
20286842Sth160488 
20296842Sth160488 	if (prldap_set_session_option(NULL, NULL,
20306842Sth160488 	    PRLDAP_OPT_IO_MAX_TIMEOUT,
20316842Sth160488 	    timeoutMilliSec) != LDAP_SUCCESS) {
20326842Sth160488 		(void) snprintf(errstr, sizeof (errstr),
20336842Sth160488 		    gettext("createTLSSession: failed to initialize "
20346842Sth160488 		    "TLS security"));
20356842Sth160488 		MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
20366842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
20376842Sth160488 		return (NS_LDAP_INTERNAL);
20386842Sth160488 	}
20396842Sth160488 
20406842Sth160488 	hostcertpath = auth->hostcertpath;
20416842Sth160488 	if (hostcertpath == NULL) {
20426842Sth160488 		alloc_hcp = __s_get_hostcertpath();
20436842Sth160488 		hostcertpath = alloc_hcp;
20446842Sth160488 	}
20456842Sth160488 
20466842Sth160488 	if (hostcertpath == NULL)
20476842Sth160488 		return (NS_LDAP_MEMORY);
20486842Sth160488 
20496842Sth160488 	if ((ldap_rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
20506842Sth160488 		if (alloc_hcp != NULL) {
20516842Sth160488 			free(alloc_hcp);
20526842Sth160488 		}
20536842Sth160488 		(void) snprintf(errstr, sizeof (errstr),
20546842Sth160488 		    gettext("createTLSSession: failed to initialize "
20556842Sth160488 		    "TLS security (%s)"),
20566842Sth160488 		    ldapssl_err2string(ldap_rc));
20576842Sth160488 		MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
20586842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
20596842Sth160488 		return (NS_LDAP_INTERNAL);
20606842Sth160488 	}
20616842Sth160488 	if (alloc_hcp)
20626842Sth160488 		free(alloc_hcp);
20636842Sth160488 
20646842Sth160488 	*ldp = ldapssl_init(serverAddr, port, 1);
20656842Sth160488 
20666842Sth160488 	if (*ldp == NULL ||
20676842Sth160488 	    ldapssl_install_gethostbyaddr(*ldp, "ldap") != 0) {
20686842Sth160488 		(void) snprintf(errstr, sizeof (errstr),
20696842Sth160488 		    gettext("createTLSSession: failed to connect "
20706842Sth160488 		    "using TLS (%s)"), strerror(errno));
20716842Sth160488 		MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
20726842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
20736842Sth160488 		return (NS_LDAP_INTERNAL);
20746842Sth160488 	}
20756842Sth160488 
20766842Sth160488 	return (NS_LDAP_SUCCESS);
20776842Sth160488 }
20786842Sth160488 
20796842Sth160488 /*
20806842Sth160488  * Convert (resolve) hostname to IP address.
20816842Sth160488  *
20826842Sth160488  * INPUT:
20836842Sth160488  *
20846842Sth160488  * 	server	- \[IPv6_address\][:port]
20856842Sth160488  *		- IPv4_address[:port]
20866842Sth160488  *		- hostname[:port]
20876842Sth160488  *
20886842Sth160488  * 	newaddr - Buffer to which this function writes resulting address,
20896842Sth160488  *		including the port number, if specified in server argument.
20906842Sth160488  *
20916842Sth160488  * 	newaddr_size - Size of the newaddr buffer.
20926842Sth160488  *
20936842Sth160488  * 	errstr  - Buffer to which error string is written if error occurs.
20946842Sth160488  *
20956842Sth160488  * 	errstr_size - Size of the errstr buffer.
20966842Sth160488  *
20976842Sth160488  * OUTPUT:
20986842Sth160488  *
20996842Sth160488  * 	Returns 1 for success, 0 in case of error.
21006842Sth160488  *
21016842Sth160488  * 	newaddr - See above (INPUT section).
21026842Sth160488  *
21036842Sth160488  *	errstr	- See above (INPUT section).
21046842Sth160488  */
21056842Sth160488 static int
21066842Sth160488 cvt_hostname2ip(char *server, char *newaddr, int newaddr_size,
21076842Sth160488     char *errstr, int errstr_size)
21086842Sth160488 {
21096842Sth160488 	char	*s;
21106842Sth160488 	unsigned short port = 0;
21116842Sth160488 	int	err;
21126842Sth160488 	char	buffer[NSS_BUFLEN_HOSTS];
21136842Sth160488 	struct hostent	result;
21146842Sth160488 
21156842Sth160488 	/* Determine if the host name contains a port number. */
21166842Sth160488 
21176842Sth160488 	/* Skip over IPv6 address. */
21186842Sth160488 	s = strchr(server, ']');
21196842Sth160488 	s = strchr(s != NULL ? s : server, ':');
21206842Sth160488 	if (s != NULL) {
21216842Sth160488 		if (sscanf(s + 1, "%hu", &port) != 1) {
21226842Sth160488 			/* Address misformatted. No port number after : */
21236842Sth160488 			(void) snprintf(errstr, errstr_size, "%s",
21246842Sth160488 			    gettext("Invalid host:port format"));
21256842Sth160488 			return (0);
21266842Sth160488 		} else
21276842Sth160488 			/* Cut off the :<port> part. */
21286842Sth160488 			*s = '\0';
21296842Sth160488 	}
21306842Sth160488 
21316842Sth160488 	buffer[0] = '\0';
21326842Sth160488 	/*
21336842Sth160488 	 * Resolve hostname and fill in hostent structure.
21346842Sth160488 	 */
21356842Sth160488 	if (!__s_api_hostname2ip(server, &result, buffer, NSS_BUFLEN_HOSTS,
21366842Sth160488 	    &err)) {
21376842Sth160488 		/*
21386842Sth160488 		 * The only possible error here could be TRY_AGAIN if buffer was
21396842Sth160488 		 * not big enough. NSS_BUFLEN_HOSTS should have been enough
21406842Sth160488 		 * though.
21416842Sth160488 		 */
21426842Sth160488 		(void) snprintf(errstr, errstr_size, "%s",
21436842Sth160488 		    gettext("Unable to resolve address."));
21446842Sth160488 		return (0);
21456842Sth160488 	}
21466842Sth160488 
21476842Sth160488 
21486842Sth160488 	buffer[0] = '\0';
21496842Sth160488 	/*
21506842Sth160488 	 * Convert the address to string.
21516842Sth160488 	 */
21526842Sth160488 	if (!inet_ntop(result.h_addrtype, result.h_addr_list[0], buffer,
21536842Sth160488 	    NSS_BUFLEN_HOSTS)) {
21546842Sth160488 		/* There's not much we can do. */
21556842Sth160488 		(void) snprintf(errstr, errstr_size, "%s",
21566842Sth160488 		    gettext("Unable to convert address to string."));
21576842Sth160488 		return (0);
21586842Sth160488 	}
21596842Sth160488 
21606842Sth160488 	/* Put together the address and the port */
21616842Sth160488 	if (port > 0) {
21626842Sth160488 		switch (result.h_addrtype) {
21636842Sth160488 			case AF_INET6:
21646842Sth160488 				(void) snprintf(newaddr,
21656842Sth160488 				    /* [IP]:<port>\0 */
21666842Sth160488 				    1 + strlen(buffer) + 1 + 1 + 5 + 1,
21676842Sth160488 				    "[%s]:%hu",
21686842Sth160488 				    buffer,
21696842Sth160488 				    port);
21706842Sth160488 				break;
21716842Sth160488 			/* AF_INET */
21726842Sth160488 			default :
21736842Sth160488 				(void) snprintf(newaddr,
21746842Sth160488 				    /* IP:<port>\0 */
21756842Sth160488 				    strlen(buffer) + 1 + 5 + 1,
21766842Sth160488 				    "%s:%hu",
21776842Sth160488 				    buffer,
21786842Sth160488 				    port);
21796842Sth160488 				break;
21806842Sth160488 		}
21816842Sth160488 	} else {
21826842Sth160488 		(void) strncpy(newaddr, buffer, newaddr_size);
21836842Sth160488 	}
21846842Sth160488 
21856842Sth160488 	return (1);
21866842Sth160488 }
21876842Sth160488 
21886842Sth160488 
21896842Sth160488 /*
21906842Sth160488  * This finction initializes a none-TLS LDAP session.  On success LDAP*
21916842Sth160488  * is returned (pointed by *ldp). Otherwise, the function returns
21926842Sth160488  * an NS error code and provides an additional info pointed by *errorp.
21936842Sth160488  */
21946842Sth160488 static
21956842Sth160488 ns_ldap_return_code
21966842Sth160488 createNonTLSSession(const char *serverAddr,
21976842Sth160488 		uint16_t port, int gssapi,
21986842Sth160488 		LDAP **ldp, ns_ldap_error_t **errorp)
21996842Sth160488 {
22006842Sth160488 	char		errstr[MAXERROR];
22016842Sth160488 	char		*addr;
22026842Sth160488 	int		is_ip = 0;
22036842Sth160488 			/* [INET6_ADDRSTRLEN]:<port>\0 */
22046842Sth160488 	char		svraddr[1+INET6_ADDRSTRLEN+1+1+5+1];
22056842Sth160488 #ifdef DEBUG
22066842Sth160488 	(void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
22076842Sth160488 	    thr_self());
22086842Sth160488 #endif /* DEBUG */
22096842Sth160488 
22106842Sth160488 	if (gssapi == 0) {
22116842Sth160488 		is_ip = (__s_api_isipv4((char *)serverAddr) ||
22126842Sth160488 		    __s_api_isipv6((char *)serverAddr));
22136842Sth160488 	}
22146842Sth160488 
22156842Sth160488 	/*
22166842Sth160488 	 * Let's try to resolve IP address of server.
22176842Sth160488 	 */
22186842Sth160488 	if (is_ip == 0 && !gssapi && (ldap_in_nss_switch((char *)"hosts") > 0 ||
22196842Sth160488 	    ldap_in_nss_switch((char *)"ipnodes") > 0)) {
22206842Sth160488 		addr = strdup(serverAddr);
22216842Sth160488 		if (addr == NULL)
22226842Sth160488 			return (NS_LDAP_MEMORY);
22236842Sth160488 		svraddr[0] = '\0';
22246842Sth160488 		if (cvt_hostname2ip(addr, svraddr, sizeof (svraddr),
22256842Sth160488 		    errstr, MAXERROR) == 1) {
22266842Sth160488 			serverAddr = svraddr;
22276842Sth160488 			free(addr);
22286842Sth160488 		} else {
22296842Sth160488 			free(addr);
22306842Sth160488 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
22316842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
22326842Sth160488 			return (NS_LDAP_INTERNAL);
22336842Sth160488 		}
22346842Sth160488 	}
22356842Sth160488 
22366842Sth160488 	/* Warning message IF cannot connect to host(s) */
22376842Sth160488 	if ((*ldp = ldap_init((char *)serverAddr, port)) == NULL) {
22386842Sth160488 		char *p = strerror(errno);
22396842Sth160488 		MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
22406842Sth160488 		    strdup(p), NS_LDAP_MEMORY);
22416842Sth160488 		return (NS_LDAP_INTERNAL);
22426842Sth160488 	}
22436842Sth160488 
22446842Sth160488 	return (NS_LDAP_SUCCESS);
22456842Sth160488 }
22466842Sth160488 
22476842Sth160488 /*
22486842Sth160488  * This finction initializes an LDAP session.
22496842Sth160488  *
22506842Sth160488  * INPUT:
22516842Sth160488  *     auth - a structure specified an authenticastion method and credentials,
22526842Sth160488  *     serverAddr - the address of a server to which a connection
22536842Sth160488  *                  will be established,
22546842Sth160488  *     port - a port being listened by the server,
22556842Sth160488  *     timeoutMilliSec - a timeout in milliseconds for the Bind operation.
22566842Sth160488  *
22576842Sth160488  * OUTPUT:
22586842Sth160488  *     ldp - a pointer to an LDAP structure which will be used
22596842Sth160488  *           for all the subsequent operations against the server.
22606921Smichen  *     If an error occurs, the function returns an NS error code
22616842Sth160488  *     and provides an additional info pointed by *errorp.
22626842Sth160488  */
22636842Sth160488 static
22646842Sth160488 ns_ldap_return_code
22656842Sth160488 createSession(const ns_cred_t *auth, const char *serverAddr,
22666842Sth160488 		    uint16_t port, int timeoutMilliSec,
22676842Sth160488 		    LDAP **ldp, ns_ldap_error_t **errorp)
22686842Sth160488 {
22696842Sth160488 	int	useSSL = 0, gssapi = 0;
22706842Sth160488 	char	errstr[MAXERROR];
22716842Sth160488 
22726842Sth160488 	switch (auth->auth.type) {
22736842Sth160488 		case NS_LDAP_AUTH_NONE:
22746842Sth160488 		case NS_LDAP_AUTH_SIMPLE:
22756842Sth160488 		case NS_LDAP_AUTH_SASL:
22766842Sth160488 			break;
22776842Sth160488 		case NS_LDAP_AUTH_TLS:
22786842Sth160488 			useSSL = 1;
22796842Sth160488 			break;
22806842Sth160488 		default:
22816842Sth160488 			(void) sprintf(errstr,
22826842Sth160488 			    gettext("openConnection: unsupported "
22836842Sth160488 			    "authentication method (%d)"), auth->auth.type);
22846842Sth160488 			MKERROR(LOG_WARNING, *errorp,
22856842Sth160488 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
22866842Sth160488 			    NS_LDAP_MEMORY);
22876842Sth160488 			return (NS_LDAP_INTERNAL);
22886842Sth160488 	}
22896842Sth160488 
22906842Sth160488 	if (port == USE_DEFAULT_PORT) {
22916842Sth160488 		port = useSSL ? LDAPS_PORT : LDAP_PORT;
22926842Sth160488 	}
22936842Sth160488 
22946842Sth160488 	if (auth->auth.type == NS_LDAP_AUTH_SASL &&
22956842Sth160488 	    auth->auth.saslmech == NS_LDAP_SASL_GSSAPI)
22966842Sth160488 		gssapi = 1;
22976842Sth160488 
22986842Sth160488 	if (useSSL)
22996842Sth160488 		return (createTLSSession(auth, serverAddr, port,
23006842Sth160488 		    timeoutMilliSec, ldp, errorp));
23016842Sth160488 	else
23026842Sth160488 		return (createNonTLSSession(serverAddr, port, gssapi,
23036842Sth160488 		    ldp, errorp));
23040Sstevel@tonic-gate }
23056842Sth160488 
23066842Sth160488 /*
23076842Sth160488  * This finction performs a non-SASL bind operation.  If an error accures,
23086842Sth160488  * the function returns an NS error code and provides an additional info
23096842Sth160488  * pointed by *errorp.
23106842Sth160488  */
23116842Sth160488 static
23126842Sth160488 ns_ldap_return_code
23136842Sth160488 doSimpleBind(const ns_cred_t *auth,
23146842Sth160488 		LDAP *ld,
23156842Sth160488 		int timeoutSec,
23166842Sth160488 		ns_ldap_error_t **errorp,
23176842Sth160488 		int fail_if_new_pwd_reqd,
23186842Sth160488 		int passwd_mgmt)
23196842Sth160488 {
23206842Sth160488 	char			*binddn, *passwd, errstr[MAXERROR], *errmsg;
23216842Sth160488 	int			msgId, errnum = 0, ldap_rc;
23226842Sth160488 	ns_ldap_return_code	ret_code;
23236842Sth160488 	LDAPMessage		*resultMsg = NULL;
23246842Sth160488 	LDAPControl		**controls;
23256842Sth160488 	struct timeval		tv;
23266842Sth160488 
23276842Sth160488 	binddn = auth->cred.unix_cred.userID;
23286842Sth160488 	passwd = auth->cred.unix_cred.passwd;
23296842Sth160488 	if (passwd == NULL || *passwd == '\0' ||
23306842Sth160488 	    binddn == NULL || *binddn == '\0') {
23316842Sth160488 		(void) sprintf(errstr, gettext("openConnection: "
23326842Sth160488 		    "missing credentials for Simple bind"));
23336842Sth160488 		MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
23346842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
23356842Sth160488 		(void) ldap_unbind(ld);
23366842Sth160488 		return (NS_LDAP_INTERNAL);
23376842Sth160488 	}
23386842Sth160488 
23396842Sth160488 #ifdef DEBUG
23406842Sth160488 	(void) fprintf(stderr, "tid= %d: +++Simple bind\n",
23416842Sth160488 	    thr_self());
23426842Sth160488 #endif /* DEBUG */
23436842Sth160488 	msgId = ldap_simple_bind(ld, binddn, passwd);
23446842Sth160488 
23456842Sth160488 	if (msgId == -1) {
23466842Sth160488 		(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
23476842Sth160488 		    (void *)&errnum);
23486842Sth160488 		(void) snprintf(errstr, sizeof (errstr),
23496842Sth160488 		    gettext("openConnection: simple bind failed "
23506842Sth160488 		    "- %s"), ldap_err2string(errnum));
23516842Sth160488 		(void) ldap_unbind(ld);
23526842Sth160488 		MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
23536842Sth160488 		    NS_LDAP_MEMORY);
23546842Sth160488 		return (NS_LDAP_INTERNAL);
23556842Sth160488 	}
23566842Sth160488 
23576842Sth160488 	tv.tv_sec = timeoutSec;
23586842Sth160488 	tv.tv_usec = 0;
23596842Sth160488 	ldap_rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
23606842Sth160488 
23616842Sth160488 	if ((ldap_rc == -1) || (ldap_rc == 0)) {
23626842Sth160488 		(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
23636842Sth160488 		    (void *)&errnum);
23646842Sth160488 		(void) snprintf(errstr, sizeof (errstr),
23656842Sth160488 		    gettext("openConnection: simple bind failed "
23666842Sth160488 		    "- %s"), ldap_err2string(errnum));
23676842Sth160488 		(void) ldap_msgfree(resultMsg);
23686842Sth160488 		(void) ldap_unbind(ld);
23696842Sth160488 		MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
23706842Sth160488 		    NS_LDAP_MEMORY);
23716842Sth160488 		return (NS_LDAP_INTERNAL);
23726842Sth160488 	}
23736842Sth160488 
23746842Sth160488 	/*
23756842Sth160488 	 * get ldaprc, controls, and error msg
23766842Sth160488 	 */
23776842Sth160488 	ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
23786842Sth160488 	    &errmsg, NULL, &controls, 1);
23796842Sth160488 
23806842Sth160488 	if (ldap_rc != LDAP_SUCCESS) {
23816842Sth160488 		(void) snprintf(errstr, sizeof (errstr),
23826842Sth160488 		    gettext("openConnection: simple bind failed "
23836842Sth160488 		    "- unable to parse result"));
23846842Sth160488 		(void) ldap_unbind(ld);
23856842Sth160488 		MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
23866842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
23876842Sth160488 		return (NS_LDAP_INTERNAL);
23886842Sth160488 	}
23896842Sth160488 
23906842Sth160488 	/* process the password management info, if any */
23916842Sth160488 	ret_code = process_pwd_mgmt("simple",
23926842Sth160488 	    errnum, controls, errmsg,
23936842Sth160488 	    errorp,
23946842Sth160488 	    fail_if_new_pwd_reqd,
23956842Sth160488 	    passwd_mgmt);
23966842Sth160488 
23976842Sth160488 	if (ret_code == NS_LDAP_INTERNAL) {
23986842Sth160488 		(void) ldap_unbind(ld);
23996842Sth160488 	}
24006842Sth160488 
24016842Sth160488 	return (ret_code);
24026842Sth160488 }
24036842Sth160488 
24046842Sth160488 /*
24056842Sth160488  * This finction performs a SASL bind operation.  If an error accures,
24066842Sth160488  * the function returns an NS error code and provides an additional info
24076842Sth160488  * pointed by *errorp.
24086842Sth160488  */
24096842Sth160488 static
24106842Sth160488 ns_ldap_return_code
24116842Sth160488 doSASLBind(const ns_cred_t *auth,
24126842Sth160488 		LDAP *ld,
24136842Sth160488 		int timeoutSec,
24146842Sth160488 		ns_ldap_error_t **errorp,
24156842Sth160488 		int fail_if_new_pwd_reqd,
24166842Sth160488 		int passwd_mgmt)
24176842Sth160488 {
24186842Sth160488 	char			*binddn, *passwd, *digest_md5_name,
24196842Sth160488 	    errstr[MAXERROR], *errmsg;
24206842Sth160488 	struct berval		cred;
24216842Sth160488 	int			ldap_rc, errnum = 0;
24226842Sth160488 	ns_ldap_return_code	ret_code;
24236842Sth160488 	struct timeval		tv;
24246842Sth160488 	LDAPMessage		*resultMsg;
24256842Sth160488 	LDAPControl		**controls;
24266842Sth160488 	int			min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
24276842Sth160488 	ns_sasl_cb_param_t	sasl_param;
24286842Sth160488 
24296842Sth160488 	if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
24306842Sth160488 	    auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
24316842Sth160488 		(void) sprintf(errstr,
24326842Sth160488 		    gettext("openConnection: SASL options are "
24336842Sth160488 		    "not supported (%d) for non-GSSAPI sasl bind"),
24346842Sth160488 		    auth->auth.saslopt);
24356842Sth160488 		MKERROR(LOG_WARNING, *errorp,
24366842Sth160488 		    LDAP_AUTH_METHOD_NOT_SUPPORTED,
24376842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
24386842Sth160488 		(void) ldap_unbind(ld);
24396842Sth160488 		return (NS_LDAP_INTERNAL);
24406842Sth160488 	}
24416842Sth160488 	if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
24426842Sth160488 		binddn = auth->cred.unix_cred.userID;
24436842Sth160488 		passwd = auth->cred.unix_cred.passwd;
24446842Sth160488 		if (passwd == NULL || *passwd == '\0' ||
24456842Sth160488 		    binddn == NULL || *binddn == '\0') {
24466842Sth160488 			(void) sprintf(errstr,
24476842Sth160488 			gettext("openConnection: missing credentials "
24486842Sth160488 			    "for SASL bind"));
24496842Sth160488 			MKERROR(LOG_WARNING, *errorp,
24506842Sth160488 			    LDAP_INVALID_CREDENTIALS,
24516842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
24526842Sth160488 			(void) ldap_unbind(ld);
24536842Sth160488 			return (NS_LDAP_INTERNAL);
24546842Sth160488 		}
24556842Sth160488 		cred.bv_val = passwd;
24566842Sth160488 		cred.bv_len = strlen(passwd);
24576842Sth160488 	}
24586842Sth160488 
24596842Sth160488 	ret_code = NS_LDAP_SUCCESS;
24606842Sth160488 
24616842Sth160488 	switch (auth->auth.saslmech) {
24626842Sth160488 	case NS_LDAP_SASL_CRAM_MD5:
24636842Sth160488 		/*
24646842Sth160488 		 * NOTE: if iDS changes to support cram_md5,
24656842Sth160488 		 * please add password management code here.
24666842Sth160488 		 * Since ldap_sasl_cram_md5_bind_s does not
24676842Sth160488 		 * return anything that could be used to
24686842Sth160488 		 * extract the ldap rc/errmsg/control to
24696842Sth160488 		 * determine if bind failed due to password
24706842Sth160488 		 * policy, a new cram_md5_bind API will need
24716842Sth160488 		 * to be introduced. See
24726842Sth160488 		 * ldap_x_sasl_digest_md5_bind() and case
24736842Sth160488 		 * NS_LDAP_SASL_DIGEST_MD5 below for details.
24746842Sth160488 		 */
24756842Sth160488 		if ((ldap_rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
24766842Sth160488 		    &cred, NULL, NULL)) != LDAP_SUCCESS) {
24776842Sth160488 			(void) ldap_get_option(ld,
24786842Sth160488 			    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
24796842Sth160488 			(void) snprintf(errstr, sizeof (errstr),
24806842Sth160488 			    gettext("openConnection: "
24816842Sth160488 			    "sasl/CRAM-MD5 bind failed - %s"),
24826842Sth160488 			    ldap_err2string(errnum));
24836842Sth160488 			MKERROR(LOG_WARNING, *errorp, errnum,
24846842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
24856842Sth160488 			(void) ldap_unbind(ld);
24866842Sth160488 			return (NS_LDAP_INTERNAL);
24876842Sth160488 		}
24886842Sth160488 		break;
24896842Sth160488 	case NS_LDAP_SASL_DIGEST_MD5:
24906842Sth160488 		digest_md5_name = malloc(strlen(binddn) + 5);
24916842Sth160488 		/* 5 = strlen("dn: ") + 1 */
24926842Sth160488 		if (digest_md5_name == NULL) {
24936842Sth160488 			(void) ldap_unbind(ld);
24946842Sth160488 			return (NS_LDAP_MEMORY);
24956842Sth160488 		}
24966842Sth160488 		(void) strcpy(digest_md5_name, "dn: ");
24976842Sth160488 		(void) strcat(digest_md5_name, binddn);
24986842Sth160488 
24996842Sth160488 		tv.tv_sec = timeoutSec;
25006842Sth160488 		tv.tv_usec = 0;
25016842Sth160488 		ldap_rc = ldap_x_sasl_digest_md5_bind(ld,
25026842Sth160488 		    digest_md5_name, &cred, NULL, NULL,
25036842Sth160488 		    &tv, &resultMsg);
25046842Sth160488 
25056842Sth160488 		if (resultMsg == NULL) {
25066842Sth160488 			free(digest_md5_name);
25076842Sth160488 			(void) ldap_get_option(ld,
25086842Sth160488 			    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
25096842Sth160488 			(void) snprintf(errstr, sizeof (errstr),
25106842Sth160488 			    gettext("openConnection: "
25116842Sth160488 			    "DIGEST-MD5 bind failed - %s"),
25126842Sth160488 			    ldap_err2string(errnum));
25136842Sth160488 			(void) ldap_unbind(ld);
25146842Sth160488 			MKERROR(LOG_WARNING, *errorp, errnum,
25156842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
25166842Sth160488 			return (NS_LDAP_INTERNAL);
25176842Sth160488 		}
25186842Sth160488 
25196842Sth160488 		/*
25206842Sth160488 		 * get ldaprc, controls, and error msg
25216842Sth160488 		 */
25226842Sth160488 		ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
25236842Sth160488 		    &errmsg, NULL, &controls, 1);
25246842Sth160488 
25256842Sth160488 		if (ldap_rc != LDAP_SUCCESS) {
25266842Sth160488 			free(digest_md5_name);
25276842Sth160488 			(void) snprintf(errstr, sizeof (errstr),
25286842Sth160488 			    gettext("openConnection: "
25296842Sth160488 			    "DIGEST-MD5 bind failed "
25306842Sth160488 			    "- unable to parse result"));
25316842Sth160488 			(void) ldap_unbind(ld);
25326842Sth160488 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
25336842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
25346842Sth160488 			return (NS_LDAP_INTERNAL);
25356842Sth160488 		}
25366842Sth160488 
25376842Sth160488 		/* process the password management info, if any */
25386842Sth160488 		ret_code = process_pwd_mgmt("sasl/DIGEST-MD5",
25396842Sth160488 		    errnum, controls, errmsg,
25406842Sth160488 		    errorp,
25416842Sth160488 		    fail_if_new_pwd_reqd,
25426842Sth160488 		    passwd_mgmt);
25436842Sth160488 
25446842Sth160488 		if (ret_code == NS_LDAP_INTERNAL) {
25456842Sth160488 			(void) ldap_unbind(ld);
25466842Sth160488 		}
25476842Sth160488 
25486842Sth160488 		free(digest_md5_name);
25496842Sth160488 		break;
25506842Sth160488 	case NS_LDAP_SASL_GSSAPI:
25516842Sth160488 		if (sasl_gssapi_inited == 0) {
25526842Sth160488 			ret_code = __s_api_sasl_gssapi_init();
25536842Sth160488 			if (ret_code != NS_LDAP_SUCCESS) {
25546842Sth160488 				(void) snprintf(errstr, sizeof (errstr),
25556842Sth160488 				    gettext("openConnection: "
25566842Sth160488 				    "GSSAPI initialization "
25576842Sth160488 				    "failed"));
25586842Sth160488 				(void) ldap_unbind(ld);
25596842Sth160488 				MKERROR(LOG_WARNING, *errorp, ret_code,
25606842Sth160488 				    strdup(errstr), NS_LDAP_MEMORY);
25616842Sth160488 				return (ret_code);
25626842Sth160488 			}
25636842Sth160488 		}
25646842Sth160488 		(void) memset(&sasl_param, 0,
25656842Sth160488 		    sizeof (ns_sasl_cb_param_t));
25666842Sth160488 		sasl_param.authid = NULL;
25676842Sth160488 		sasl_param.authzid = "";
25686842Sth160488 		(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
25696842Sth160488 		    (void *)&min_ssf);
25706842Sth160488 		(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
25716842Sth160488 		    (void *)&max_ssf);
25726842Sth160488 
25736842Sth160488 		ldap_rc = ldap_sasl_interactive_bind_s(
25746842Sth160488 		    ld, NULL, "GSSAPI",
25756842Sth160488 		    NULL, NULL, LDAP_SASL_INTERACTIVE,
25766842Sth160488 		    __s_api_sasl_bind_callback,
25776842Sth160488 		    &sasl_param);
25786842Sth160488 
25796842Sth160488 		if (ldap_rc != LDAP_SUCCESS) {
25806842Sth160488 			(void) snprintf(errstr, sizeof (errstr),
25816842Sth160488 			    gettext("openConnection: "
25826842Sth160488 			    "GSSAPI bind failed "
25836842Sth160488 			    "- %d %s"),
25846842Sth160488 			    ldap_rc,
25856842Sth160488 			    ldap_err2string(ldap_rc));
25866842Sth160488 			(void) ldap_unbind(ld);
25876842Sth160488 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
25886842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
25896842Sth160488 			return (NS_LDAP_INTERNAL);
25906842Sth160488 		}
25916842Sth160488 
25926842Sth160488 		break;
25936842Sth160488 	default:
25946842Sth160488 		(void) ldap_unbind(ld);
25956842Sth160488 		(void) sprintf(errstr,
25966842Sth160488 		    gettext("openConnection: unsupported SASL "
25976842Sth160488 		    "mechanism (%d)"), auth->auth.saslmech);
25986842Sth160488 		MKERROR(LOG_WARNING, *errorp,
25996842Sth160488 		    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
26006842Sth160488 		    NS_LDAP_MEMORY);
26016842Sth160488 		return (NS_LDAP_INTERNAL);
26026842Sth160488 	}
26036842Sth160488 
26046842Sth160488 	return (ret_code);
26056842Sth160488 }
26066842Sth160488 
26076842Sth160488 /*
26086842Sth160488  * This function performs an LDAP Bind operation proceeding
26096842Sth160488  * from a type of the connection specified by auth->auth.type.
26106842Sth160488  *
26116842Sth160488  * INPUT:
26126842Sth160488  *     auth - a structure specified an authenticastion method and credentials,
26136842Sth160488  *     ld - a pointer returned by the createSession() function,
26146842Sth160488  *     timeoutSec - a timeout in seconds for the Bind operation,
26156842Sth160488  *     fail_if_new_pwd_reqd - a flag indicating that the call should fail
26166842Sth160488  *                            if a new password is required,
26176842Sth160488  *     passwd_mgmt - a flag indicating that the server supports
26186842Sth160488  *                   password management.
26196842Sth160488  *
26206842Sth160488  * OUTPUT:
26216842Sth160488  *     If an error accures, the function returns an NS error code
26226842Sth160488  *     and provides an additional info pointed by *errorp.
26236842Sth160488  */
26246842Sth160488 static
26256842Sth160488 ns_ldap_return_code
26266842Sth160488 performBind(const ns_cred_t *auth,
26276842Sth160488 		LDAP *ld,
26286842Sth160488 		int timeoutSec,
26296842Sth160488 		ns_ldap_error_t **errorp,
26306842Sth160488 		int fail_if_new_pwd_reqd,
26316842Sth160488 		int passwd_mgmt)
26326842Sth160488 {
26336842Sth160488 	int	bindType;
26346842Sth160488 	char	errstr[MAXERROR];
26356842Sth160488 
26366842Sth160488 	ns_ldap_return_code (*binder)(const ns_cred_t *auth,
26376842Sth160488 	    LDAP *ld,
26386842Sth160488 	    int timeoutSec,
26396842Sth160488 	    ns_ldap_error_t **errorp,
26406842Sth160488 	    int fail_if_new_pwd_reqd,
26416842Sth160488 	    int passwd_mgmt) = NULL;
26426842Sth160488 
26436842Sth160488 	if (!ld) {
26446842Sth160488 		(void) sprintf(errstr,
26456842Sth160488 		    "performBind: LDAP session "
26466842Sth160488 		    "is not initialized.");
26476842Sth160488 		MKERROR(LOG_WARNING, *errorp,
26486842Sth160488 		    LDAP_AUTH_METHOD_NOT_SUPPORTED,
26496842Sth160488 		    strdup(errstr), NS_LDAP_MEMORY);
26506842Sth160488 		return (NS_LDAP_INTERNAL);
26516842Sth160488 	}
26526842Sth160488 
26536842Sth160488 	bindType = auth->auth.type == NS_LDAP_AUTH_TLS ?
26546842Sth160488 	    auth->auth.tlstype : auth->auth.type;
26556842Sth160488 
26566842Sth160488 	switch (bindType) {
26576842Sth160488 		case NS_LDAP_AUTH_NONE:
26586842Sth160488 #ifdef DEBUG
26596842Sth160488 		(void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
26606842Sth160488 		    thr_self());
26616842Sth160488 #endif /* DEBUG */
26626842Sth160488 			break;
26636842Sth160488 		case NS_LDAP_AUTH_SIMPLE:
26646842Sth160488 			binder = doSimpleBind;
26656842Sth160488 			break;
26666842Sth160488 		case NS_LDAP_AUTH_SASL:
26676842Sth160488 			binder = doSASLBind;
26686842Sth160488 			break;
26696842Sth160488 		default:
26706842Sth160488 			(void) sprintf(errstr,
26716842Sth160488 			    gettext("openConnection: unsupported "
26726842Sth160488 			    "authentication method "
26736842Sth160488 			    "(%d)"), bindType);
26746842Sth160488 			MKERROR(LOG_WARNING, *errorp,
26756842Sth160488 			    LDAP_AUTH_METHOD_NOT_SUPPORTED,
26766842Sth160488 			    strdup(errstr), NS_LDAP_MEMORY);
26776842Sth160488 			(void) ldap_unbind(ld);
26786842Sth160488 			return (NS_LDAP_INTERNAL);
26796842Sth160488 	}
26806842Sth160488 
26816842Sth160488 	if (binder != NULL) {
26826842Sth160488 		return (*binder)(auth,
26836842Sth160488 		    ld,
26846842Sth160488 		    timeoutSec,
26856842Sth160488 		    errorp,
26866842Sth160488 		    fail_if_new_pwd_reqd,
26876842Sth160488 		    passwd_mgmt);
26886842Sth160488 	}
26896842Sth160488 
26906842Sth160488 	return (NS_LDAP_SUCCESS);
26916842Sth160488 }
2692