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