xref: /titanic_41/usr/src/lib/libsldap/common/ns_connect.c (revision 69889278ff50c08a6682a39ce6b5d97c5f0c2387)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <synch.h>
33 #include <time.h>
34 #include <libintl.h>
35 #include <thread.h>
36 #include <syslog.h>
37 #include <sys/mman.h>
38 #include <nsswitch.h>
39 #include <nss_dbdefs.h>
40 #include "solaris-priv.h"
41 #include "ns_sldap.h"
42 #include "ns_internal.h"
43 #include "ns_cache_door.h"
44 #include "ldappr.h"
45 #include <sys/stat.h>
46 #include <fcntl.h>
47 #include <procfs.h>
48 #include <unistd.h>
49 
50 extern unsigned int _sleep(unsigned int);
51 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
52 		LDAPControl **, LDAPControl **);
53 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
54 
55 static int openConnection(LDAP **, const char *, const ns_cred_t *,
56 		int, ns_ldap_error_t **, int, int);
57 
58 static mutex_t	sessionPoolLock = DEFAULTMUTEX;
59 
60 static Connection **sessionPool = NULL;
61 static int sessionPoolSize = 0;
62 
63 
64 static mutex_t	nscdLock = DEFAULTMUTEX;
65 static int	nscdChecked = 0;
66 static pid_t	checkedPid = -1;
67 static int	isNscd = 0;
68 
69 /* Number of hostnames to allocate memory for */
70 #define	NUMTOMALLOC	32
71 
72 
73 /*
74  * Check /proc/PID/psinfo to see if this process is nscd
75  * If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce
76  * constant reconnects for many operations.
77  * A more complete solution is to develop true connection pooling.
78  * However, this is much better than a new connection for every request.
79  */
80 static int
81 nscd_proc()
82 {
83 	pid_t		my_pid;
84 	psinfo_t	pinfo;
85 	char		fname[BUFSIZ];
86 	int		ret;
87 	int		fd;
88 
89 	/* Don't bother checking if this process isn't root. */
90 	/* It can't be nscd */
91 	if (getuid() != 0)
92 		return (0);
93 
94 	my_pid = getpid();
95 	if (nscdChecked && (my_pid == checkedPid)) {
96 		return (isNscd);
97 	}
98 	(void) mutex_lock(&nscdLock);
99 	if (nscdChecked && (my_pid == checkedPid)) {
100 		(void) mutex_unlock(&nscdLock);
101 		return (isNscd);
102 	}
103 	nscdChecked = 1;
104 	checkedPid = my_pid;
105 	isNscd = 0;
106 	if (snprintf(fname, BUFSIZ, "/proc/%d/psinfo", my_pid) != 0) {
107 		if ((fd = open(fname,  O_RDONLY)) > 0) {
108 			ret = read(fd, &pinfo, sizeof (psinfo_t));
109 			(void) close(fd);
110 			if (ret == sizeof (psinfo_t) &&
111 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
112 				/* process runs as root and is named nscd */
113 				/* that's good enough for now */
114 				isNscd = 1;
115 			}
116 		}
117 	}
118 	(void) mutex_unlock(&nscdLock);
119 	return (isNscd);
120 }
121 
122 /*
123  * This function requests a server from the cache manager through
124  * the door functionality
125  */
126 
127 static int
128 __s_api_requestServer(const char *request, const char *server,
129 	ns_server_info_t *ret, ns_ldap_error_t **error)
130 {
131 	union {
132 		ldap_data_t	s_d;
133 		char		s_b[DOORBUFFERSIZE];
134 	} space;
135 	ldap_data_t	*sptr;
136 	int		ndata;
137 	int		adata;
138 	char		errstr[MAXERROR];
139 	const char	*ireq;
140 	char		*rbuf, *ptr, *rest;
141 	char		*dptr;
142 	char		**mptr, **mptr1, **cptr, **cptr1;
143 	int		mcnt, ccnt;
144 	char		**servers;
145 	int		rc;
146 
147 	if (ret == NULL || error == NULL) {
148 		return (NS_LDAP_OP_FAILED);
149 	}
150 	(void) memset(ret, 0, sizeof (ns_server_info_t));
151 	*error = NULL;
152 
153 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
154 
155 	if (request == NULL)
156 		ireq = NS_CACHE_NEW;
157 	else
158 		ireq = request;
159 
160 	adata = (sizeof (ldap_call_t) + strlen(ireq) +1);
161 	if (server != NULL) {
162 		adata += strlen(DOORLINESEP) + 1;
163 		adata += strlen(server) + 1;
164 	}
165 	ndata = sizeof (space);
166 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
167 	(void) strcpy(space.s_d.ldap_call.ldap_u.domainname, ireq);
168 	if (server != NULL) {
169 		(void) strcat(space.s_d.ldap_call.ldap_u.domainname,
170 			DOORLINESEP);
171 		(void) strcat(space.s_d.ldap_call.ldap_u.domainname, server);
172 	}
173 	sptr = &space.s_d;
174 
175 	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
176 	case SUCCESS:
177 		break;
178 	/* this case is for when the $mgr is not running, but ldapclient */
179 	/* is trying to initialize things */
180 	case NOSERVER:
181 		/* get first server from config list unavailable otherwise */
182 		servers = NULL;
183 		rc = __s_api_getServers(&servers, error);
184 		if (rc != NS_LDAP_SUCCESS) {
185 			if (servers != NULL) {
186 				__s_api_free2dArray(servers);
187 				servers = NULL;
188 			}
189 			return (rc);
190 		}
191 		if (servers == NULL || servers[0] == NULL) {
192 			__s_api_free2dArray(servers);
193 			servers = NULL;
194 			(void) sprintf(errstr,
195 				gettext("No server found in configuration"));
196 			MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
197 				strdup(errstr), NULL);
198 			return (NS_LDAP_CONFIG);
199 		}
200 		ret->server = strdup(servers[0]);
201 		if (ret->server == NULL) {
202 			__s_api_free2dArray(servers);
203 			return (NS_LDAP_MEMORY);
204 		}
205 		ret->saslMechanisms = NULL;
206 		ret->controls = NULL;
207 		__s_api_free2dArray(servers);
208 		servers = NULL;
209 		return (NS_LDAP_SUCCESS);
210 	case NOTFOUND:
211 	default:
212 		return (NS_LDAP_OP_FAILED);
213 	}
214 
215 	/* copy info from door call return structure here */
216 	rbuf =  space.s_d.ldap_ret.ldap_u.config;
217 
218 	/* Get the host */
219 	ptr = strtok_r(rbuf, DOORLINESEP, &rest);
220 	if (ptr == NULL) {
221 		(void) sprintf(errstr, gettext("No server returned from "
222 			"ldap_cachemgr"));
223 		MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
224 			strdup(errstr), NULL);
225 		return (NS_LDAP_OP_FAILED);
226 	}
227 	ret->server = strdup(ptr);
228 	if (ret->server == NULL) {
229 		return (NS_LDAP_MEMORY);
230 	}
231 
232 	/* get the Supported Controls/SASL mechs */
233 	mptr = NULL;
234 	mcnt = 0;
235 	cptr = NULL;
236 	ccnt = 0;
237 	for (; ; ) {
238 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
239 		if (ptr == NULL)
240 			break;
241 		if (strncasecmp(ptr, _SASLMECHANISM,
242 				_SASLMECHANISM_LEN) == 0) {
243 			dptr = strchr(ptr, '=');
244 			if (dptr == NULL)
245 				continue;
246 			dptr++;
247 			mptr1 = (char **)realloc((void *)mptr,
248 					sizeof (char *) * (mcnt+2));
249 			if (mptr1 == NULL) {
250 				__s_api_free2dArray(mptr);
251 				if (sptr != &space.s_d) {
252 				    (void) munmap((char *)sptr, ndata);
253 				}
254 				__s_api_free2dArray(cptr);
255 				free(ret->server);
256 				ret->server = NULL;
257 				return (NS_LDAP_MEMORY);
258 			}
259 			mptr = mptr1;
260 			mptr[mcnt] = strdup(dptr);
261 			if (mptr[mcnt] == NULL) {
262 				if (sptr != &space.s_d) {
263 				    (void) munmap((char *)sptr, ndata);
264 				}
265 				__s_api_free2dArray(cptr);
266 				cptr = NULL;
267 				__s_api_free2dArray(mptr);
268 				mptr = NULL;
269 				free(ret->server);
270 				ret->server = NULL;
271 				return (NS_LDAP_MEMORY);
272 			}
273 			mcnt++;
274 			mptr[mcnt] = NULL;
275 		}
276 		if (strncasecmp(ptr, _SUPPORTEDCONTROL,
277 			_SUPPORTEDCONTROL_LEN) == 0) {
278 			dptr = strchr(ptr, '=');
279 			if (dptr == NULL)
280 				continue;
281 			dptr++;
282 			cptr1 = (char **)realloc((void *)cptr,
283 					sizeof (char *) * (ccnt+2));
284 			if (cptr1 == NULL) {
285 				if (sptr != &space.s_d) {
286 				    (void) munmap((char *)sptr, ndata);
287 				}
288 				__s_api_free2dArray(cptr);
289 				__s_api_free2dArray(mptr);
290 				mptr = NULL;
291 				free(ret->server);
292 				ret->server = NULL;
293 				return (NS_LDAP_MEMORY);
294 			}
295 			cptr = cptr1;
296 			cptr[ccnt] = strdup(dptr);
297 			if (cptr[ccnt] == NULL) {
298 				if (sptr != &space.s_d) {
299 				    (void) munmap((char *)sptr, ndata);
300 				}
301 				__s_api_free2dArray(cptr);
302 				cptr = NULL;
303 				__s_api_free2dArray(mptr);
304 				mptr = NULL;
305 				free(ret->server);
306 				ret->server = NULL;
307 				return (NS_LDAP_MEMORY);
308 			}
309 			ccnt++;
310 			cptr[ccnt] = NULL;
311 		}
312 	}
313 	if (mptr != NULL) {
314 		ret->saslMechanisms = mptr;
315 	}
316 	if (cptr != NULL) {
317 		ret->controls = cptr;
318 	}
319 
320 
321 	/* clean up door call */
322 	if (sptr != &space.s_d) {
323 		(void) munmap((char *)sptr, ndata);
324 	}
325 	*error = NULL;
326 
327 	return (NS_LDAP_SUCCESS);
328 }
329 
330 #ifdef DEBUG
331 
332 /*
333  * printCred(): prints the credential structure
334  */
335 static void
336 printCred(FILE *fp, const ns_cred_t *cred)
337 {
338 	if (fp == NULL) {
339 		(void) fprintf(fp, "printCred: fp is NULL\n");
340 		return;
341 	}
342 	if (cred == NULL) {
343 		(void) fprintf(fp, "printCred: cred is NULL\n");
344 		return;
345 	}
346 
347 	(void) fprintf(fp, "AuthType=%d\n", cred->auth.type);
348 	(void) fprintf(fp, "TlsType=%d\n", cred->auth.tlstype);
349 	(void) fprintf(fp, "SaslMech=%d\n", cred->auth.saslmech);
350 	(void) fprintf(fp, "SaslOpt=%d\n", cred->auth.saslopt);
351 	if (cred->hostcertpath)
352 		(void) fprintf(fp, "hostCertPath=%s\n", cred->hostcertpath);
353 	if (cred->cred.unix_cred.userID)
354 		(void) fprintf(fp, "userID=%s\n", cred->cred.unix_cred.userID);
355 	if (cred->cred.unix_cred.passwd)
356 		(void) fprintf(fp, "passwd=%s\n", cred->cred.unix_cred.passwd);
357 }
358 
359 /*
360  * printConnection(): prints the connection structure
361  */
362 static void
363 printConnection(FILE *fp, Connection *con)
364 {
365 	if (fp == NULL || con == NULL)
366 		return;
367 
368 	(void) fprintf(fp, "connectionID=%d\n", con->connectionId);
369 	(void) fprintf(fp, "usedBit=%d\n", con->usedBit);
370 	(void) fprintf(fp, "threadID=%d\n", con->threadID);
371 	if (con->serverAddr) {
372 		(void) fprintf(fp, "serverAddr=%s\n", con->serverAddr);
373 	}
374 	printCred(fp, con->auth);
375 	(void) fprintf(fp, "-----------------------------------------------\n");
376 	fflush(fp);
377 }
378 
379 #endif /* DEBUG */
380 
381 
382 /*
383  * addConnection(): inserts a connection in the connection list.
384  * It will also sets use bit and the thread Id for the thread
385  * using the connection for the first time.
386  * Returns: -1 = failure, new Connection ID = success
387  */
388 static int
389 addConnection(Connection *con)
390 {
391 	int i;
392 
393 	if (!con)
394 		return (-1);
395 #ifdef DEBUG
396 	(void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID);
397 #endif /* DEBUG */
398 	(void) mutex_lock(&sessionPoolLock);
399 	if (sessionPool == NULL) {
400 		sessionPoolSize = SESSION_CACHE_INC;
401 		sessionPool = calloc(sessionPoolSize,
402 				sizeof (struct connection **));
403 		if (!sessionPool) {
404 			(void) mutex_unlock(&sessionPoolLock);
405 			return (-1);
406 		}
407 #ifdef DEBUG
408 		(void) fprintf(stderr, "Initialized sessionPool\n");
409 #endif /* DEBUG */
410 	}
411 	for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
412 		;
413 	if (i == sessionPoolSize) {
414 		/* run out of array, need to increase sessionPool */
415 		Connection **cl;
416 		cl = (Connection **) realloc(sessionPool,
417 			(sessionPoolSize + SESSION_CACHE_INC) *
418 			sizeof (Connection *));
419 		if (!cl) {
420 			(void) mutex_unlock(&sessionPoolLock);
421 			return (-1);
422 		}
423 		(void) memset(cl + sessionPoolSize, 0,
424 			SESSION_CACHE_INC * sizeof (struct connection *));
425 		sessionPool = cl;
426 		sessionPoolSize += SESSION_CACHE_INC;
427 #ifdef DEBUG
428 		(void) fprintf(stderr, "Increased sessionPoolSize to: %d\n",
429 				sessionPoolSize);
430 #endif /* DEBUG */
431 	}
432 	sessionPool[i] = con;
433 	con->usedBit = B_TRUE;
434 	(void) mutex_unlock(&sessionPoolLock);
435 	con->connectionId = i + CONID_OFFSET;
436 #ifdef DEBUG
437 	(void) fprintf(stderr, "Connection added [%d]\n", i);
438 	printConnection(stderr, con);
439 #endif /* DEBUG */
440 	return (i + CONID_OFFSET);
441 }
442 
443 /*
444  * See if the specified session matches a currently available
445  */
446 
447 static int
448 findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
449 	Connection **conp)
450 {
451 	Connection *cp;
452 	int id;
453 
454 	if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
455 		return (-1);
456 	*conp = NULL;
457 	if (sessionPool == NULL)
458 		return (-1);
459 	id = cID - CONID_OFFSET;
460 	if (id < 0 || id >= sessionPoolSize)
461 		return (-1);
462 
463 	(void) mutex_lock(&sessionPoolLock);
464 	if (sessionPool[id] == NULL) {
465 		(void) mutex_unlock(&sessionPoolLock);
466 		return (-1);
467 	}
468 	cp = sessionPool[id];
469 
470 	/*
471 	 * Make sure the connection has the same type of authentication method
472 	 */
473 	if ((cp->usedBit) ||
474 	    (cp->auth->auth.type != auth->auth.type) ||
475 	    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
476 	    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
477 	    (cp->auth->auth.saslopt != auth->auth.saslopt)) {
478 		(void) mutex_unlock(&sessionPoolLock);
479 		return (-1);
480 	}
481 	if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
482 		((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
483 		(cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
484 		(cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
485 		((cp->auth->cred.unix_cred.userID == NULL) ||
486 		(strcasecmp(cp->auth->cred.unix_cred.userID,
487 		auth->cred.unix_cred.userID) != 0))) {
488 		(void) mutex_unlock(&sessionPoolLock);
489 		return (-1);
490 	}
491 	/* An existing connection is found but it needs to be reset */
492 	if (flags & NS_LDAP_NEW_CONN) {
493 		(void) mutex_unlock(&sessionPoolLock);
494 		DropConnection(cID, 0);
495 		return (-1);
496 	}
497 	/* found an available connection */
498 	cp->usedBit = B_TRUE;
499 	(void) mutex_unlock(&sessionPoolLock);
500 	cp->threadID = thr_self();
501 	*conp = cp;
502 	return (cID);
503 }
504 
505 /*
506  * findConnection(): find an available connection from the list
507  * that matches the criteria specified in Connection structure.
508  * If serverAddr is NULL, then find a connection to any server
509  * as long as it matches the rest of the parameters.
510  * Returns: -1 = failure, the Connection ID found = success.
511  */
512 static int
513 findConnection(const char *serverAddr, const ns_cred_t *auth, Connection **conp)
514 {
515 	Connection *cp;
516 	int i;
517 
518 	if (auth == NULL || conp == NULL)
519 		return (-1);
520 	*conp = NULL;
521 
522 #ifdef DEBUG
523 	(void) fprintf(stderr, "Find connection\n");
524 	(void) fprintf(stderr, "Looking for ....\n");
525 	if (serverAddr && *serverAddr)
526 		(void) fprintf(stderr, "serverAddr=%s\n", serverAddr);
527 	else
528 		(void) fprintf(stderr, "serverAddr=NULL\n");
529 	printCred(stderr, auth);
530 	fflush(stderr);
531 #endif /* DEBUG */
532 	if (sessionPool == NULL)
533 		return (-1);
534 	(void) mutex_lock(&sessionPoolLock);
535 	for (i = 0; i < sessionPoolSize; ++i) {
536 		if (sessionPool[i] == NULL)
537 			continue;
538 		cp = sessionPool[i];
539 #ifdef DEBUG
540 		(void) fprintf(stderr, "checking connection [%d] ....\n", i);
541 		printConnection(stderr, cp);
542 #endif /* DEBUG */
543 		if ((cp->usedBit) ||
544 		    (cp->auth->auth.type != auth->auth.type) ||
545 		    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
546 		    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
547 		    (cp->auth->auth.saslopt != auth->auth.saslopt) ||
548 		    (serverAddr && *serverAddr &&
549 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
550 			continue;
551 		if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
552 		    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
553 		    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
554 		    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
555 		    ((cp->auth->cred.unix_cred.userID == NULL) ||
556 		    (cp->auth->cred.unix_cred.passwd == NULL) ||
557 		    ((strcasecmp(cp->auth->cred.unix_cred.userID,
558 			    auth->cred.unix_cred.userID) != 0)) ||
559 		    ((strcmp(cp->auth->cred.unix_cred.passwd,
560 			    auth->cred.unix_cred.passwd) != 0))))
561 				continue;
562 		/* found an available connection */
563 		cp->usedBit = B_TRUE;
564 		(void) mutex_unlock(&sessionPoolLock);
565 		cp->threadID = thr_self();
566 		*conp = cp;
567 #ifdef DEBUG
568 		(void) fprintf(stderr, "Connection found cID=%d\n", i);
569 		fflush(stderr);
570 #endif /* DEBUG */
571 		return (i + CONID_OFFSET);
572 	}
573 	(void) mutex_unlock(&sessionPoolLock);
574 	return (-1);
575 }
576 
577 /*
578  * Free a Connection structure
579  */
580 static void
581 freeConnection(Connection *con)
582 {
583 	if (con == NULL)
584 		return;
585 	if (con->serverAddr)
586 		free(con->serverAddr);
587 	if (con->auth)
588 		(void) __ns_ldap_freeCred(&(con->auth));
589 	if (con->saslMechanisms) {
590 		__s_api_free2dArray(con->saslMechanisms);
591 	}
592 	if (con->controls) {
593 		__s_api_free2dArray(con->controls);
594 	}
595 	free(con);
596 }
597 
598 /*
599  * Find a connection matching the passed in criteria.  If an open
600  * connection with that criteria exists use it, otherwise open a
601  * new connection.
602  * Success: returns the pointer to the Connection structure
603  * Failure: returns NULL, error code and message should be in errorp
604  */
605 
606 static int
607 makeConnection(Connection **conp, const char *serverAddr,
608 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
609 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
610 	int nopasswd_acct_mgmt, char ***badsrvrs)
611 {
612 	Connection *con = NULL;
613 	ConnectionID id;
614 	char errmsg[MAXERROR];
615 	int rc, exit_rc = NS_LDAP_SUCCESS;
616 	ns_server_info_t sinfo;
617 	char *hReq, *host = NULL;
618 	LDAP *ld = NULL;
619 	int passwd_mgmt = 0;
620 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
621 	short	memerr = 0; /* Variable for tracking memory allocation errors */
622 
623 	if (conp == NULL || errorp == NULL || auth == NULL)
624 		return (NS_LDAP_INVALID_PARAM);
625 	*errorp = NULL;
626 	*conp = NULL;
627 	sinfo.server = NULL;
628 	sinfo.controls = NULL;
629 	sinfo.saslMechanisms = NULL;
630 
631 	if ((id = findConnection(serverAddr, auth, &con)) != -1) {
632 		/* connection found in cache */
633 #ifdef DEBUG
634 		(void) fprintf(stderr, "connection found in cache %d\n", id);
635 		fflush(stderr);
636 #endif /* DEBUG */
637 		*cID = id;
638 		*conp = con;
639 		return (NS_LDAP_SUCCESS);
640 	}
641 
642 	if (serverAddr) {
643 		rc = openConnection(&ld, serverAddr, auth, timeoutSec, errorp,
644 				fail_if_new_pwd_reqd, passwd_mgmt);
645 		if (rc == NS_LDAP_SUCCESS || rc ==
646 				NS_LDAP_SUCCESS_WITH_INFO) {
647 			exit_rc = rc;
648 			rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr,
649 				&sinfo, errorp);
650 			if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
651 				(void) snprintf(errmsg, sizeof (errmsg),
652 				gettext("makeConnection: unable to get "
653 				"server information for %s"), serverAddr);
654 				syslog(LOG_ERR, "libsldap: %s", errmsg);
655 				return (NS_LDAP_OP_FAILED);
656 			}
657 			goto create_con;
658 		} else {
659 			return (rc);
660 		}
661 	}
662 
663 	/* No cached connection, create one */
664 	for (; ; ) {
665 		if (host == NULL)
666 			hReq = NS_CACHE_NEW;
667 		else
668 			hReq = NS_CACHE_NEXT;
669 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp);
670 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
671 			(host && (strcasecmp(host, sinfo.server) == 0))) {
672 			/* Log the error */
673 			if (*errorp) {
674 				(void) snprintf(errmsg, sizeof (errmsg),
675 				"%s: (%s)", gettext("makeConnection: "
676 				"unable to make LDAP connection, "
677 				"request for a server failed"),
678 				    (*errorp)->message);
679 				syslog(LOG_ERR, "libsldap: %s", errmsg);
680 			}
681 
682 			if (sinfo.server)
683 				free(sinfo.server);
684 			__s_api_free2dArray(sinfo.saslMechanisms);
685 			__s_api_free2dArray(sinfo.controls);
686 			if (host)
687 				free(host);
688 			return (NS_LDAP_OP_FAILED);
689 		}
690 		if (host)
691 			free(host);
692 		host = strdup(sinfo.server);
693 		if (host == NULL) {
694 			free(sinfo.server);
695 			__s_api_free2dArray(sinfo.saslMechanisms);
696 			__s_api_free2dArray(sinfo.controls);
697 			return (NS_LDAP_MEMORY);
698 		}
699 
700 		/* check if server supports password management */
701 		passwd_mgmt = __s_api_contain_passwd_control_oid(
702 			sinfo.controls);
703 		/* check if server supports password less account mgmt */
704 		if (nopasswd_acct_mgmt &&
705 			!__s_api_contain_account_usable_control_oid(
706 			sinfo.controls)) {
707 			syslog(LOG_WARNING, "libsldap: server %s does not "
708 				"provide account information without password",
709 				host);
710 			free(host);
711 			free(sinfo.server);
712 			__s_api_free2dArray(sinfo.saslMechanisms);
713 			__s_api_free2dArray(sinfo.controls);
714 			return (NS_LDAP_OP_FAILED);
715 		}
716 		/* make the connection */
717 		rc = openConnection(&ld, host, auth, timeoutSec, errorp,
718 				fail_if_new_pwd_reqd, passwd_mgmt);
719 		/* if success, go to create connection structure */
720 		if (rc == NS_LDAP_SUCCESS ||
721 				rc == NS_LDAP_SUCCESS_WITH_INFO) {
722 			exit_rc = rc;
723 			break;
724 		}
725 
726 		/*
727 		 * If not able to reach the server, inform the ldap
728 		 * cache manager that the server should be removed
729 		 * from its server list. Thus, the manager will not
730 		 * return this server on the next get-server request
731 		 * and will also reduce the server list refresh TTL,
732 		 * so that it will find out sooner when the server
733 		 * is up again.
734 		 */
735 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
736 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
737 				(*errorp)->status == LDAP_SERVER_DOWN) {
738 				/* Reset memory allocation error */
739 				memerr = 0;
740 				/*
741 				 * We contacted a server that we could
742 				 * not either authenticate to or contact.
743 				 * If it is due to authentication, then
744 				 * we need to try the server again. So,
745 				 * do not remove the server yet, but
746 				 * add it to the bad server list.
747 				 * The caller routine will remove
748 				 * the servers if:
749 				 *	a). A good server is found or
750 				 *	b). All the possible methods
751 				 *	    are tried without finding
752 				 *	    a good server
753 				 */
754 				if (*badsrvrs == NULL) {
755 				    if (!(*badsrvrs = (char **)malloc
756 					(sizeof (char *) * NUMTOMALLOC))) {
757 					memerr = 1;
758 				    }
759 				/* Allocate memory in chunks of NUMTOMALLOC */
760 				} else if ((totalbad % NUMTOMALLOC) ==
761 					    NUMTOMALLOC - 1) {
762 				    char **tmpptr;
763 				    if (!(tmpptr = (char **)realloc(*badsrvrs,
764 					    (sizeof (char *) * NUMTOMALLOC *
765 					    ((totalbad/NUMTOMALLOC) + 2))))) {
766 					memerr = 1;
767 				    } else {
768 					*badsrvrs = tmpptr;
769 				    }
770 				}
771 				/*
772 				 * Store host only if there were no unsuccessful
773 				 * memory allocations above
774 				 */
775 				if (!memerr &&
776 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
777 					memerr = 1;
778 					totalbad--;
779 				}
780 				(*badsrvrs)[totalbad] = NULL;
781 			}
782 		}
783 
784 		/* else, cleanup and go for the next server */
785 		if (sinfo.server) {
786 			free(sinfo.server);
787 			sinfo.server = NULL;
788 		}
789 		__s_api_free2dArray(sinfo.saslMechanisms);
790 		sinfo.saslMechanisms = NULL;
791 		__s_api_free2dArray(sinfo.controls);
792 		sinfo.controls = NULL;
793 		/* Return if we had memory allocation errors */
794 		if (memerr)
795 			return (NS_LDAP_MEMORY);
796 		if (*errorp) {
797 			/*
798 			 * If openConnection() failed due to
799 			 * password policy, or invalid credential,
800 			 * keep *errorp and exit
801 			 */
802 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
803 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
804 				free(host);
805 				return (rc);
806 			} else {
807 				(void) __ns_ldap_freeError(errorp);
808 				*errorp = NULL;
809 			}
810 		}
811 	}
812 
813 create_con:
814 	/* we have created ld, setup con structure */
815 	if (host)
816 		free(host);
817 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
818 		if (sinfo.server)
819 			free(sinfo.server);
820 		__s_api_free2dArray(sinfo.saslMechanisms);
821 		__s_api_free2dArray(sinfo.controls);
822 		/*
823 		 * If password control attached in **errorp,
824 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
825 		 * free the error structure
826 		 */
827 		if (*errorp) {
828 			(void) __ns_ldap_freeError(errorp);
829 			*errorp = NULL;
830 		}
831 		return (NS_LDAP_MEMORY);
832 	}
833 
834 	con->serverAddr = sinfo.server;
835 	con->saslMechanisms = sinfo.saslMechanisms;
836 	con->controls = sinfo.controls;
837 
838 	con->auth = __ns_ldap_dupAuth(auth);
839 	if (con->auth == NULL) {
840 		free(con);
841 		/*
842 		 * If password control attached in **errorp,
843 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
844 		 * free the error structure
845 		 */
846 		if (*errorp) {
847 			(void) __ns_ldap_freeError(errorp);
848 			*errorp = NULL;
849 		}
850 		return (NS_LDAP_MEMORY);
851 	}
852 
853 	con->threadID = thr_self();
854 	con->ld = ld;
855 	if ((id = addConnection(con)) == -1) {
856 		freeConnection(con);
857 		/*
858 		 * If password control attached in **errorp,
859 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
860 		 * free the error structure
861 		 */
862 		if (*errorp) {
863 			(void) __ns_ldap_freeError(errorp);
864 			*errorp = NULL;
865 		}
866 		return (NS_LDAP_MEMORY);
867 	}
868 #ifdef DEBUG
869 	(void) fprintf(stderr, "connection added into cache %d\n", id);
870 	fflush(stderr);
871 #endif /* DEBUG */
872 	*cID = id;
873 	*conp = con;
874 	return (exit_rc);
875 }
876 
877 /*
878  * Return the specified connection to the pool.  If necessary
879  * delete the connection.
880  */
881 
882 static void
883 _DropConnection(ConnectionID cID, int flag, int fini)
884 {
885 	Connection *cp;
886 	int id;
887 	int use_mutex = !fini;
888 
889 	id = cID - CONID_OFFSET;
890 	if (id < 0 || id >= sessionPoolSize)
891 		return;
892 #ifdef DEBUG
893 	(void) fprintf(stderr,
894 		"Dropping connection cID=%d flag=0x%x\n", cID, flag);
895 	fflush(stderr);
896 #endif /* DEBUG */
897 	if (use_mutex)
898 		(void) mutex_lock(&sessionPoolLock);
899 
900 	cp = sessionPool[id];
901 	/* sanity check before removing */
902 	if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) {
903 		if (use_mutex)
904 			(void) mutex_unlock(&sessionPoolLock);
905 		return;
906 	}
907 
908 	if (!fini &&
909 		((flag & NS_LDAP_NEW_CONN) == 0) &&
910 		((flag & NS_LDAP_KEEP_CONN) || nscd_proc())) {
911 		/* release Connection (keep alive) */
912 		cp->usedBit = B_FALSE;
913 		cp->threadID = 0;	/* unmark the threadID */
914 		if (use_mutex)
915 			(void) mutex_unlock(&sessionPoolLock);
916 	} else {
917 		/* delete Connection (disconnect) */
918 		sessionPool[id] = NULL;
919 		if (use_mutex)
920 			(void) mutex_unlock(&sessionPoolLock);
921 		(void) ldap_unbind(cp->ld);
922 		freeConnection(cp);
923 	}
924 }
925 
926 void
927 DropConnection(ConnectionID cID, int flag)
928 {
929 	_DropConnection(cID, flag, 0);
930 }
931 
932 /*
933  * This routine is called after a bind operation is
934  * done in openConnection() to process the password
935  * management information, if any.
936  *
937  * Input:
938  *   bind_type: "simple" or "sasl/DIGEST-MD5"
939  *   ldaprc   : ldap rc from the ldap bind operation
940  *   controls : controls returned by the server
941  *   errmsg   : error message from the server
942  *   fail_if_new_pwd_reqd:
943  *              flag indicating if connection should be open
944  *              when password needs to change immediately
945  *   passwd_mgmt:
946  *              flag indicating if server supports password
947  *              policy/management
948  *
949  * Output     : ns_ldap_error structure, which may contain
950  *              password status and number of seconds until
951  *              expired
952  *
953  * return rc:
954  * NS_LDAP_EXTERNAL: error, connection should not open
955  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
956  * NS_LDAP_SUCCESS: OK to open connection
957  *
958  */
959 
960 static int
961 process_pwd_mgmt(char *bind_type, int ldaprc,
962 		LDAPControl **controls,
963 		char *errmsg, ns_ldap_error_t **errorp,
964 		int fail_if_new_pwd_reqd,
965 		int passwd_mgmt)
966 {
967 	char		errstr[MAXERROR];
968 	LDAPControl	**ctrl = NULL;
969 	int		exit_rc;
970 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
971 	int		sec_until_exp = 0;
972 
973 	/*
974 	 * errmsg may be an empty string,
975 	 * even if ldaprc is LDAP_SUCCESS,
976 	 * free the empty string if that's the case
977 	 */
978 	if (errmsg &&
979 		(*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
980 		ldap_memfree(errmsg);
981 		errmsg = NULL;
982 	}
983 
984 	if (ldaprc != LDAP_SUCCESS) {
985 		/*
986 		 * try to map ldap rc and error message to
987 		 * a password status
988 		 */
989 		if (errmsg) {
990 			if (passwd_mgmt)
991 				pwd_status =
992 					__s_api_set_passwd_status(
993 					ldaprc, errmsg);
994 			ldap_memfree(errmsg);
995 		}
996 
997 		(void) snprintf(errstr, sizeof (errstr),
998 			gettext("openConnection: "
999 			"%s bind failed "
1000 			"- %s"), bind_type, ldap_err2string(ldaprc));
1001 
1002 		if (pwd_status != NS_PASSWD_GOOD) {
1003 			MKERROR_PWD_MGMT(*errorp,
1004 				ldaprc, strdup(errstr),
1005 				pwd_status, 0, NULL);
1006 		} else {
1007 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
1008 				NULL);
1009 		}
1010 		if (controls)
1011 			ldap_controls_free(controls);
1012 
1013 		return (NS_LDAP_INTERNAL);
1014 	}
1015 
1016 	/*
1017 	 * ldaprc is LDAP_SUCCESS,
1018 	 * process the password management controls, if any
1019 	 */
1020 	exit_rc = NS_LDAP_SUCCESS;
1021 	if (controls && passwd_mgmt) {
1022 		/*
1023 		 * The control with the OID
1024 		 * 2.16.840.1.113730.3.4.4 (or
1025 		 * LDAP_CONTROL_PWEXPIRED, as defined
1026 		 * in the ldap.h header file) is the
1027 		 * expired password control.
1028 		 *
1029 		 * This control is used if the server
1030 		 * is configured to require users to
1031 		 * change their passwords when first
1032 		 * logging in and whenever the
1033 		 * passwords are reset.
1034 		 *
1035 		 * If the user is logging in for the
1036 		 * first time or if the user's
1037 		 * password has been reset, the
1038 		 * server sends this control to
1039 		 * indicate that the client needs to
1040 		 * change the password immediately.
1041 		 *
1042 		 * At this point, the only operation
1043 		 * that the client can perform is to
1044 		 * change the user's password. If the
1045 		 * client requests any other LDAP
1046 		 * operation, the server sends back
1047 		 * an LDAP_UNWILLING_TO_PERFORM
1048 		 * result code with an expired
1049 		 * password control.
1050 		 *
1051 		 * The control with the OID
1052 		 * 2.16.840.1.113730.3.4.5 (or
1053 		 * LDAP_CONTROL_PWEXPIRING, as
1054 		 * defined in the ldap.h header file)
1055 		 * is the password expiration warning
1056 		 * control.
1057 		 *
1058 		 * This control is used if the server
1059 		 * is configured to expire user
1060 		 * passwords after a certain amount
1061 		 * of time.
1062 		 *
1063 		 * The server sends this control back
1064 		 * to the client if the client binds
1065 		 * using a password that will soon
1066 		 * expire.  The ldctl_value field of
1067 		 * the LDAPControl structure
1068 		 * specifies the number of seconds
1069 		 * before the password will expire.
1070 		 */
1071 		for (ctrl = controls; *ctrl; ctrl++) {
1072 
1073 			if (strcmp((*ctrl)->ldctl_oid,
1074 				LDAP_CONTROL_PWEXPIRED) == 0) {
1075 				/*
1076 				 * if the caller wants this bind
1077 				 * to fail, set up the error info.
1078 				 * If call to this function is
1079 				 * for searching the LDAP directory,
1080 				 * e.g., __ns_ldap_list(),
1081 				 * there's really no sense to
1082 				 * let a connection open and
1083 				 * then fail immediately afterward
1084 				 * on the LDAP search operation with
1085 				 * the LDAP_UNWILLING_TO_PERFORM rc
1086 				 */
1087 				pwd_status =
1088 					NS_PASSWD_CHANGE_NEEDED;
1089 				if (fail_if_new_pwd_reqd) {
1090 					(void) snprintf(errstr,
1091 						sizeof (errstr),
1092 						gettext(
1093 						"openConnection: "
1094 						"%s bind "
1095 						"failed "
1096 						"- password "
1097 						"expired. It "
1098 						" needs to change "
1099 						"immediately!"),
1100 						bind_type);
1101 					MKERROR_PWD_MGMT(*errorp,
1102 						LDAP_SUCCESS,
1103 						strdup(errstr),
1104 						pwd_status,
1105 						0,
1106 						NULL);
1107 					exit_rc = NS_LDAP_INTERNAL;
1108 				} else {
1109 					MKERROR_PWD_MGMT(*errorp,
1110 						LDAP_SUCCESS,
1111 						NULL,
1112 						pwd_status,
1113 						0,
1114 						NULL);
1115 					exit_rc =
1116 					NS_LDAP_SUCCESS_WITH_INFO;
1117 				}
1118 				break;
1119 			} else if (strcmp((*ctrl)->ldctl_oid,
1120 				LDAP_CONTROL_PWEXPIRING) == 0) {
1121 				pwd_status =
1122 					NS_PASSWD_ABOUT_TO_EXPIRE;
1123 				if ((*ctrl)->
1124 					ldctl_value.bv_len > 0 &&
1125 					(*ctrl)->
1126 						ldctl_value.bv_val)
1127 					sec_until_exp =
1128 						atoi((*ctrl)->
1129 						ldctl_value.bv_val);
1130 				MKERROR_PWD_MGMT(*errorp,
1131 					LDAP_SUCCESS,
1132 					NULL,
1133 					pwd_status,
1134 					sec_until_exp,
1135 					NULL);
1136 				exit_rc =
1137 					NS_LDAP_SUCCESS_WITH_INFO;
1138 				break;
1139 			}
1140 		}
1141 	}
1142 
1143 	if (controls)
1144 		ldap_controls_free(controls);
1145 
1146 	return (exit_rc);
1147 }
1148 
1149 static int
1150 ldap_in_hosts_switch()
1151 {
1152 	enum __nsw_parse_err		pserr;
1153 	struct __nsw_switchconfig	*conf;
1154 	struct __nsw_lookup		*lkp;
1155 	const char			*name;
1156 	int				found = 0;
1157 
1158 	conf = __nsw_getconfig("hosts", &pserr);
1159 	if (conf == NULL) {
1160 		return (-1);
1161 	}
1162 
1163 	/* check for skip and count other backends */
1164 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
1165 		name = lkp->service_name;
1166 		if (strcmp(name, "ldap") == 0) {
1167 			found = 1;
1168 			break;
1169 		}
1170 	}
1171 	__nsw_freeconfig(conf);
1172 	return (found);
1173 }
1174 
1175 static int
1176 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
1177 	int timeoutSec, ns_ldap_error_t **errorp,
1178 	int fail_if_new_pwd_reqd, int passwd_mgmt)
1179 {
1180 	LDAP		*ld = NULL;
1181 	char		*binddn, *passwd;
1182 	char		*digest_md5_name;
1183 	const char	*s;
1184 	int		ldapVersion = LDAP_VERSION3;
1185 	int		derefOption = LDAP_DEREF_ALWAYS;
1186 	int		zero = 0;
1187 	int		rc;
1188 	char		errstr[MAXERROR];
1189 	int		errnum = 0;
1190 	LDAPMessage	*resultMsg;
1191 	int		msgId;
1192 	int		useSSL = 0;
1193 	struct timeval	tv;
1194 	AuthType_t	bindType;
1195 	int		timeoutMilliSec = timeoutSec * 1000;
1196 	struct berval	cred;
1197 	char		*sslServerAddr;
1198 	char		*s1;
1199 	char		*errmsg;
1200 	LDAPControl	**controls;
1201 	int		pwd_rc;
1202 
1203 	*errorp = NULL;
1204 	*ldp = NULL;
1205 
1206 	switch (auth->auth.type) {
1207 		case NS_LDAP_AUTH_NONE:
1208 		case NS_LDAP_AUTH_SIMPLE:
1209 		case NS_LDAP_AUTH_SASL:
1210 			bindType = auth->auth.type;
1211 			break;
1212 		case NS_LDAP_AUTH_TLS:
1213 			useSSL = 1;
1214 			switch (auth->auth.tlstype) {
1215 				case NS_LDAP_TLS_NONE:
1216 					bindType = NS_LDAP_AUTH_NONE;
1217 					break;
1218 				case NS_LDAP_TLS_SIMPLE:
1219 					bindType = NS_LDAP_AUTH_SIMPLE;
1220 					break;
1221 				case NS_LDAP_TLS_SASL:
1222 					bindType = NS_LDAP_AUTH_SASL;
1223 					break;
1224 				default:
1225 					(void) sprintf(errstr,
1226 					gettext("openConnection: unsupported "
1227 						"TLS authentication method "
1228 						"(%d)"), auth->auth.tlstype);
1229 					MKERROR(LOG_WARNING, *errorp,
1230 						LDAP_AUTH_METHOD_NOT_SUPPORTED,
1231 						strdup(errstr), NULL);
1232 					return (NS_LDAP_INTERNAL);
1233 			}
1234 			break;
1235 		default:
1236 			(void) sprintf(errstr,
1237 				gettext("openConnection: unsupported "
1238 				"authentication method (%d)"), auth->auth.type);
1239 			MKERROR(LOG_WARNING, *errorp,
1240 				LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
1241 				NULL);
1242 			return (NS_LDAP_INTERNAL);
1243 	}
1244 
1245 	if (useSSL) {
1246 		const char	*hostcertpath;
1247 		char		*alloc_hcp = NULL;
1248 #ifdef DEBUG
1249 		(void) fprintf(stderr, "+++TLS transport\n");
1250 #endif /* DEBUG */
1251 
1252 		if (prldap_set_session_option(NULL, NULL,
1253 		    PRLDAP_OPT_IO_MAX_TIMEOUT,
1254 		    timeoutMilliSec) != LDAP_SUCCESS) {
1255 			(void) snprintf(errstr, sizeof (errstr),
1256 				gettext("openConnection: failed to initialize "
1257 				"TLS security"));
1258 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1259 				strdup(errstr), NULL);
1260 			return (NS_LDAP_INTERNAL);
1261 		}
1262 
1263 		hostcertpath = auth->hostcertpath;
1264 		if (hostcertpath == NULL) {
1265 			alloc_hcp = __s_get_hostcertpath();
1266 			hostcertpath = alloc_hcp;
1267 		}
1268 
1269 		if (hostcertpath == NULL)
1270 			return (NS_LDAP_MEMORY);
1271 
1272 		if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
1273 			if (alloc_hcp)
1274 				free(alloc_hcp);
1275 			(void) snprintf(errstr, sizeof (errstr),
1276 				gettext("openConnection: failed to initialize "
1277 				"TLS security (%s)"),
1278 				ldapssl_err2string(rc));
1279 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1280 				strdup(errstr), NULL);
1281 			return (NS_LDAP_INTERNAL);
1282 		}
1283 		if (alloc_hcp)
1284 			free(alloc_hcp);
1285 
1286 		/* determine if the host name contains a port number */
1287 		s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
1288 		if (s == NULL)
1289 			s = serverAddr;
1290 		s = strchr(s, ':');
1291 		if (s != NULL) {
1292 			/*
1293 			 * If we do get a port number, we will try stripping
1294 			 * it. At present, referrals will always have a
1295 			 * port number.
1296 			 */
1297 			sslServerAddr = strdup(serverAddr);
1298 			if (sslServerAddr == NULL)
1299 				return (NS_LDAP_MEMORY);
1300 			s1 = strrchr(sslServerAddr, ':');
1301 			if (s1 != NULL)
1302 				*s1 = '\0';
1303 			(void) snprintf(errstr, sizeof (errstr),
1304 			    gettext("openConnection: cannot use tls with %s. "
1305 				"Trying %s"),
1306 				serverAddr, sslServerAddr);
1307 			syslog(LOG_ERR, "libsldap: %s", errstr);
1308 		} else
1309 			sslServerAddr = (char *)serverAddr;
1310 
1311 		ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
1312 
1313 		if (sslServerAddr != serverAddr)
1314 			free(sslServerAddr);
1315 
1316 		if (ld == NULL ||
1317 		    ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
1318 			(void) snprintf(errstr, sizeof (errstr),
1319 				gettext("openConnection: failed to connect "
1320 				"using TLS (%s)"), strerror(errno));
1321 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1322 				strdup(errstr), NULL);
1323 			return (NS_LDAP_INTERNAL);
1324 		}
1325 	} else {
1326 #ifdef DEBUG
1327 		(void) fprintf(stderr, "+++Unsecure transport\n");
1328 #endif /* DEBUG */
1329 			/* Warning message IF cannot connect to host(s) */
1330 		if ((ld = ldap_init((char *)serverAddr, LDAP_PORT)) == NULL) {
1331 			char *p = strerror(errno);
1332 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
1333 				strdup(p), NULL);
1334 			return (NS_LDAP_INTERNAL);
1335 		} else {
1336 			/* check and avoid gethostname recursion */
1337 			if (ldap_in_hosts_switch() > 0 &&
1338 				! __s_api_isipv4((char *)serverAddr) &&
1339 				! __s_api_isipv6((char *)serverAddr)) {
1340 				/* host: ldap - found, attempt to recover */
1341 				if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
1342 						    "ldap") != 0) {
1343 					(void) snprintf(errstr, sizeof (errstr),
1344 						gettext("openConnection: "
1345 						"unrecoverable gethostname "
1346 						"recursion detected "
1347 						"in /etc/nsswitch.conf"));
1348 					MKERROR(LOG_WARNING, *errorp,
1349 						LDAP_CONNECT_ERROR,
1350 						strdup(errstr), NULL);
1351 					(void) ldap_unbind(ld);
1352 					return (NS_LDAP_INTERNAL);
1353 				}
1354 			}
1355 		}
1356 	}
1357 
1358 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
1359 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
1360 	/*
1361 	 * set LDAP_OPT_REFERRALS to OFF.
1362 	 * This library will handle the referral itself
1363 	 * based on API flags or configuration file
1364 	 * specification. If this option is not set
1365 	 * to OFF, libldap will never pass the
1366 	 * referral info up to this library
1367 	 */
1368 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1369 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
1370 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
1371 	/* setup TCP/IP connect timeout */
1372 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
1373 							&timeoutMilliSec);
1374 	/* retry if LDAP I/O was interrupted */
1375 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1376 
1377 	switch (bindType) {
1378 	case NS_LDAP_AUTH_NONE:
1379 #ifdef DEBUG
1380 		(void) fprintf(stderr, "+++Anonymous bind\n");
1381 #endif /* DEBUG */
1382 		break;
1383 	case NS_LDAP_AUTH_SIMPLE:
1384 		binddn = auth->cred.unix_cred.userID;
1385 		passwd = auth->cred.unix_cred.passwd;
1386 		if (passwd == NULL || *passwd == '\0' ||
1387 		    binddn == NULL || *binddn == '\0') {
1388 			(void) sprintf(errstr, gettext("openConnection: "
1389 				"missing credentials for Simple bind"));
1390 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
1391 				strdup(errstr), NULL);
1392 			(void) ldap_unbind(ld);
1393 			return (NS_LDAP_INTERNAL);
1394 		}
1395 
1396 #ifdef DEBUG
1397 		(void) fprintf(stderr, "+++Simple bind\n");
1398 #endif /* DEBUG */
1399 		msgId = ldap_simple_bind(ld, binddn, passwd);
1400 
1401 		if (msgId == -1) {
1402 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
1403 				(void *)&errnum);
1404 			(void) snprintf(errstr, sizeof (errstr),
1405 				gettext("openConnection: simple bind failed "
1406 				"- %s"), ldap_err2string(errnum));
1407 			(void) ldap_unbind(ld);
1408 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
1409 				NULL);
1410 			return (NS_LDAP_INTERNAL);
1411 		}
1412 
1413 		tv.tv_sec = timeoutSec;
1414 		tv.tv_usec = 0;
1415 		rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
1416 
1417 		if ((rc == -1) || (rc == 0)) {
1418 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
1419 				(void *)&errnum);
1420 			(void) snprintf(errstr, sizeof (errstr),
1421 				gettext("openConnection: simple bind failed "
1422 				"- %s"), ldap_err2string(errnum));
1423 			(void) ldap_msgfree(resultMsg);
1424 			(void) ldap_unbind(ld);
1425 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
1426 				NULL);
1427 			return (NS_LDAP_INTERNAL);
1428 		}
1429 
1430 		/*
1431 		 * get ldaprc, controls, and error msg
1432 		 */
1433 		rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
1434 				&errmsg, NULL, &controls, 1);
1435 
1436 		if (rc != LDAP_SUCCESS) {
1437 			(void) snprintf(errstr, sizeof (errstr),
1438 				gettext("openConnection: simple bind failed "
1439 				"- unable to parse result"));
1440 			(void) ldap_unbind(ld);
1441 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
1442 					strdup(errstr), NULL);
1443 			return (NS_LDAP_INTERNAL);
1444 		}
1445 
1446 		/* process the password management info, if any */
1447 		pwd_rc = process_pwd_mgmt("simple",
1448 					errnum, controls, errmsg,
1449 					errorp,
1450 					fail_if_new_pwd_reqd,
1451 					passwd_mgmt);
1452 
1453 		if (pwd_rc == NS_LDAP_INTERNAL) {
1454 			(void) ldap_unbind(ld);
1455 			return (pwd_rc);
1456 		}
1457 
1458 		if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
1459 			*ldp = ld;
1460 			return (pwd_rc);
1461 		}
1462 
1463 		break;
1464 	case NS_LDAP_AUTH_SASL:
1465 		/* We don't support any sasl options yet */
1466 		if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE) {
1467 			(void) sprintf(errstr,
1468 				gettext("openConnection: SASL options are "
1469 				"not supported (%d)"), auth->auth.saslopt);
1470 			MKERROR(LOG_WARNING, *errorp,
1471 				LDAP_AUTH_METHOD_NOT_SUPPORTED,
1472 				strdup(errstr), NULL);
1473 			(void) ldap_unbind(ld);
1474 			return (NS_LDAP_INTERNAL);
1475 		}
1476 		binddn = auth->cred.unix_cred.userID;
1477 		passwd = auth->cred.unix_cred.passwd;
1478 		if (passwd == NULL || *passwd == '\0' ||
1479 		    binddn == NULL || *binddn == '\0') {
1480 			(void) sprintf(errstr,
1481 				gettext("openConnection: missing credentials "
1482 				"for SASL bind"));
1483 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
1484 				strdup(errstr), NULL);
1485 			(void) ldap_unbind(ld);
1486 			return (NS_LDAP_INTERNAL);
1487 		}
1488 		cred.bv_val = passwd;
1489 		cred.bv_len = strlen(passwd);
1490 
1491 		switch (auth->auth.saslmech) {
1492 		case NS_LDAP_SASL_CRAM_MD5:
1493 			/*
1494 			 * NOTE: if iDS changes to support cram_md5,
1495 			 * please add password management code here.
1496 			 * Since ldap_sasl_cram_md5_bind_s does not
1497 			 * return anything that could be used to
1498 			 * extract the ldap rc/errmsg/control to
1499 			 * determine if bind failed due to password
1500 			 * policy, a new cram_md5_bind API will need
1501 			 * to be introduced. See
1502 			 * ldap_x_sasl_digest_md5_bind() and case
1503 			 * NS_LDAP_SASL_DIGEST_MD5 below for details.
1504 			 */
1505 			if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
1506 				&cred, NULL, NULL)) != LDAP_SUCCESS) {
1507 				(void) ldap_get_option(ld,
1508 					LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
1509 				(void) snprintf(errstr, sizeof (errstr),
1510 					gettext("openConnection: "
1511 					"sasl/CRAM-MD5 bind failed - %s"),
1512 					ldap_err2string(errnum));
1513 				MKERROR(LOG_WARNING, *errorp, errnum,
1514 					strdup(errstr), NULL);
1515 				(void) ldap_unbind(ld);
1516 				return (NS_LDAP_INTERNAL);
1517 			}
1518 			break;
1519 		case NS_LDAP_SASL_DIGEST_MD5:
1520 			digest_md5_name = malloc(strlen(binddn) + 5);
1521 			/* 5 = strlen("dn: ") + 1 */
1522 			if (digest_md5_name == NULL) {
1523 				(void) ldap_unbind(ld);
1524 				return (NS_LDAP_MEMORY);
1525 			}
1526 			(void) strcpy(digest_md5_name, "dn: ");
1527 			(void) strcat(digest_md5_name, binddn);
1528 
1529 			tv.tv_sec = timeoutSec;
1530 			tv.tv_usec = 0;
1531 			rc = ldap_x_sasl_digest_md5_bind(ld,
1532 				digest_md5_name, &cred, NULL, NULL,
1533 				&tv, &resultMsg);
1534 
1535 			if (resultMsg == NULL) {
1536 				free(digest_md5_name);
1537 				(void) ldap_get_option(ld,
1538 					LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
1539 				(void) snprintf(errstr, sizeof (errstr),
1540 					gettext("openConnection: "
1541 					"DIGEST-MD5 bind failed - %s"),
1542 					ldap_err2string(errnum));
1543 				(void) ldap_unbind(ld);
1544 				MKERROR(LOG_WARNING, *errorp, errnum,
1545 						strdup(errstr), NULL);
1546 				return (NS_LDAP_INTERNAL);
1547 			}
1548 
1549 			/*
1550 			 * get ldaprc, controls, and error msg
1551 			 */
1552 			rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
1553 				&errmsg, NULL, &controls, 1);
1554 
1555 			if (rc != LDAP_SUCCESS) {
1556 				free(digest_md5_name);
1557 				(void) snprintf(errstr, sizeof (errstr),
1558 					gettext("openConnection: "
1559 					"DIGEST-MD5 bind failed "
1560 					"- unable to parse result"));
1561 				(void) ldap_unbind(ld);
1562 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
1563 						strdup(errstr), NULL);
1564 				return (NS_LDAP_INTERNAL);
1565 			}
1566 
1567 			/* process the password management info, if any */
1568 			pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
1569 						errnum, controls, errmsg,
1570 						errorp,
1571 						fail_if_new_pwd_reqd,
1572 						passwd_mgmt);
1573 
1574 			if (pwd_rc == NS_LDAP_INTERNAL) {
1575 				free(digest_md5_name);
1576 				(void) ldap_unbind(ld);
1577 				return (pwd_rc);
1578 			}
1579 
1580 			if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
1581 				*ldp = ld;
1582 				return (pwd_rc);
1583 			}
1584 
1585 			free(digest_md5_name);
1586 			break;
1587 		default:
1588 			(void) ldap_unbind(ld);
1589 			(void) sprintf(errstr,
1590 				gettext("openConnection: unsupported SASL "
1591 				"mechanism (%d)"), auth->auth.saslmech);
1592 			MKERROR(LOG_WARNING, *errorp,
1593 				LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
1594 				NULL);
1595 			return (NS_LDAP_INTERNAL);
1596 		}
1597 	}
1598 
1599 	*ldp = ld;
1600 	return (NS_LDAP_SUCCESS);
1601 }
1602 
1603 /*
1604  * FUNCTION:	__s_api_getDefaultAuth
1605  *
1606  *	Constructs a credential for authentication using the config module.
1607  *
1608  * RETURN VALUES:
1609  *
1610  * NS_LDAP_SUCCESS	If successful
1611  * NS_LDAP_CONFIG	If there are any config errors.
1612  * NS_LDAP_MEMORY	Memory errors.
1613  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
1614  *			not build a new authp.
1615  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
1616  *			necessary fields of a cred for a given auth method
1617  *			are not provided.
1618  * INPUT:
1619  *
1620  * cLevel	Currently requested credential level to be tried
1621  *
1622  * aMethod	Currently requested authentication method to be tried
1623  *
1624  * OUTPUT:
1625  *
1626  * authp		authentication method to use.
1627  */
1628 static int
1629 __s_api_getDefaultAuth(
1630 	int	*cLevel,
1631 	ns_auth_t *aMethod,
1632 	ns_cred_t **authp)
1633 {
1634 	void		**paramVal = NULL;
1635 	char		*modparamVal = NULL;
1636 	int		getUid = 0;
1637 	int		getPasswd = 0;
1638 	int		getCertpath = 0;
1639 	int		rc = 0;
1640 	ns_ldap_error_t	*errorp = NULL;
1641 
1642 #ifdef DEBUG
1643 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
1644 #endif
1645 
1646 	if (aMethod == NULL) {
1647 		/* Require an Auth */
1648 		return (NS_LDAP_INVALID_PARAM);
1649 
1650 	}
1651 
1652 	/*
1653 	 * Do not differentiate between (proxy/self) at this time, but
1654 	 * reject self credential levels at this time
1655 	 */
1656 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF)
1657 		return (NS_LDAP_INVALID_PARAM);
1658 
1659 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
1660 	if ((*authp) == NULL)
1661 		return (NS_LDAP_MEMORY);
1662 
1663 	(*authp)->auth = *aMethod;
1664 
1665 	switch (aMethod->type) {
1666 		case NS_LDAP_AUTH_NONE:
1667 			return (NS_LDAP_SUCCESS);
1668 		case NS_LDAP_AUTH_SIMPLE:
1669 			getUid++;
1670 			getPasswd++;
1671 			break;
1672 		case NS_LDAP_AUTH_SASL:
1673 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
1674 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
1675 				getUid++;
1676 				getPasswd++;
1677 			} else {
1678 				(void) __ns_ldap_freeCred(authp);
1679 				*authp = NULL;
1680 				return (NS_LDAP_INVALID_PARAM);
1681 			}
1682 			break;
1683 		case NS_LDAP_AUTH_TLS:
1684 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
1685 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
1686 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
1687 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
1688 				getUid++;
1689 				getPasswd++;
1690 				getCertpath++;
1691 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
1692 				getCertpath++;
1693 			} else {
1694 				(void) __ns_ldap_freeCred(authp);
1695 				*authp = NULL;
1696 				return (NS_LDAP_INVALID_PARAM);
1697 			}
1698 			break;
1699 	}
1700 
1701 	if (getUid) {
1702 		paramVal = NULL;
1703 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
1704 			&paramVal, &errorp)) != NS_LDAP_SUCCESS) {
1705 			(void) __ns_ldap_freeCred(authp);
1706 			(void) __ns_ldap_freeError(&errorp);
1707 			*authp = NULL;
1708 			return (rc);
1709 		}
1710 
1711 		if (paramVal == NULL || *paramVal == NULL) {
1712 			(void) __ns_ldap_freeCred(authp);
1713 			*authp = NULL;
1714 			return (NS_LDAP_INVALID_PARAM);
1715 		}
1716 
1717 		(*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
1718 		(void) __ns_ldap_freeParam(&paramVal);
1719 		if ((*authp)->cred.unix_cred.userID == NULL) {
1720 			(void) __ns_ldap_freeCred(authp);
1721 			*authp = NULL;
1722 			return (NS_LDAP_MEMORY);
1723 		}
1724 	}
1725 	if (getPasswd) {
1726 		paramVal = NULL;
1727 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
1728 			&paramVal, &errorp)) != NS_LDAP_SUCCESS) {
1729 			(void) __ns_ldap_freeCred(authp);
1730 			(void) __ns_ldap_freeError(&errorp);
1731 			*authp = NULL;
1732 			return (rc);
1733 		}
1734 
1735 		if (paramVal == NULL || *paramVal == NULL) {
1736 			(void) __ns_ldap_freeCred(authp);
1737 			*authp = NULL;
1738 			return (NS_LDAP_INVALID_PARAM);
1739 		}
1740 
1741 		modparamVal = dvalue((char *)*paramVal);
1742 		(void) __ns_ldap_freeParam(&paramVal);
1743 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
1744 			(void) __ns_ldap_freeCred(authp);
1745 			if (modparamVal != NULL)
1746 				free(modparamVal);
1747 			*authp = NULL;
1748 			return (NS_LDAP_INVALID_PARAM);
1749 		}
1750 
1751 		(*authp)->cred.unix_cred.passwd = modparamVal;
1752 	}
1753 	if (getCertpath) {
1754 		paramVal = NULL;
1755 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
1756 			&paramVal, &errorp)) != NS_LDAP_SUCCESS) {
1757 			(void) __ns_ldap_freeCred(authp);
1758 			(void) __ns_ldap_freeError(&errorp);
1759 			*authp = NULL;
1760 			return (rc);
1761 		}
1762 
1763 		if (paramVal == NULL || *paramVal == NULL) {
1764 			(void) __ns_ldap_freeCred(authp);
1765 			*authp = NULL;
1766 			return (NS_LDAP_INVALID_PARAM);
1767 		}
1768 
1769 		(*authp)->hostcertpath = strdup((char *)*paramVal);
1770 		(void) __ns_ldap_freeParam(&paramVal);
1771 		if ((*authp)->hostcertpath == NULL) {
1772 			(void) __ns_ldap_freeCred(authp);
1773 			*authp = NULL;
1774 			return (NS_LDAP_MEMORY);
1775 		}
1776 	}
1777 	return (NS_LDAP_SUCCESS);
1778 }
1779 
1780 /*
1781  * FUNCTION:	__s_api_getConnection
1782  *
1783  *	Bind to the specified server or one from the server
1784  *	list and return the pointer.
1785  *
1786  *	This function can rebind or not (NS_LDAP_HARD), it can require a
1787  *	credential or bind anonymously
1788  *
1789  *	This function follows the DUA configuration schema algorithm
1790  *
1791  * RETURN VALUES:
1792  *
1793  * NS_LDAP_SUCCESS	A connection was made successfully.
1794  * NS_LDAP_SUCCESS_WITH_INFO
1795  * 			A connection was made successfully, but with
1796  *			password management info in *errorp
1797  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
1798  * NS_LDAP_CONFIG	If there are any config errors.
1799  * NS_LDAP_MEMORY	Memory errors.
1800  * NS_LDAP_INTERNAL	If there was a ldap error.
1801  *
1802  * INPUT:
1803  *
1804  * server	Bind to this LDAP server only
1805  * flags	If NS_LDAP_HARD is set function will not return until it has
1806  *		a connection unless there is a authentication problem.
1807  *		If NS_LDAP_NEW_CONN is set the function must force a new
1808  *              connection to be created
1809  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
1810  * auth		Credentials for bind. This could be NULL in which case
1811  *		a default cred built from the config module is used.
1812  * sessionId	cookie that points to a previous session
1813  * fail_if_new_pwd_reqd
1814  *		a flag indicating this function should fail if the passwd
1815  *		in auth needs to change immediately
1816  * nopasswd_acct_mgmt
1817  *		a flag indicating that makeConnection should check before
1818  *		binding if server supports LDAP V3 password less
1819  *		account management
1820  *
1821  * OUTPUT:
1822  *
1823  * session	pointer to a session with connection information
1824  * errorp	Set if there are any INTERNAL, or CONFIG error.
1825  */
1826 int
1827 __s_api_getConnection(
1828 	const char *server,
1829 	const int flags,
1830 	const ns_cred_t *cred,		/* credentials for bind */
1831 	ConnectionID *sessionId,
1832 	Connection **session,
1833 	ns_ldap_error_t **errorp,
1834 	int fail_if_new_pwd_reqd,
1835 	int nopasswd_acct_mgmt)
1836 {
1837 	char		errmsg[MAXERROR];
1838 	ns_auth_t	**aMethod = NULL;
1839 	ns_auth_t	**aNext = NULL;
1840 	int		**cLevel = NULL;
1841 	int		**cNext = NULL;
1842 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
1843 	int		rc;
1844 	Connection	*con = NULL;
1845 	int		sec = 1;
1846 	ns_cred_t 	*authp = NULL;
1847 	ns_cred_t	anon;
1848 	int		version = NS_LDAP_V2;
1849 	void		**paramVal = NULL;
1850 	char		**badSrvrs = NULL; /* List of problem hostnames */
1851 
1852 	if ((session == NULL) || (sessionId == NULL)) {
1853 		return (NS_LDAP_INVALID_PARAM);
1854 	}
1855 	*session = NULL;
1856 
1857 	/* if we already have a session id try to reuse connection */
1858 	if (*sessionId > 0) {
1859 		rc = findConnectionById(flags, cred, *sessionId, &con);
1860 		if (rc == *sessionId && con) {
1861 			*session = con;
1862 			return (NS_LDAP_SUCCESS);
1863 		}
1864 		*sessionId = 0;
1865 	}
1866 
1867 	/* get profile version number */
1868 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
1869 			&paramVal, errorp)) != NS_LDAP_SUCCESS)
1870 		return (rc);
1871 	if (paramVal == NULL) {
1872 		(void) sprintf(errmsg, gettext("getConnection: no file "
1873 			"version"));
1874 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
1875 			NS_LDAP_CONFIG);
1876 		return (NS_LDAP_CONFIG);
1877 	}
1878 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
1879 		version = NS_LDAP_V1;
1880 	(void) __ns_ldap_freeParam((void ***)&paramVal);
1881 
1882 	/* Get the bind timeout value */
1883 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
1884 	if (paramVal != NULL && *paramVal != NULL) {
1885 		timeoutSec = **((int **)paramVal);
1886 		(void) __ns_ldap_freeParam(&paramVal);
1887 	}
1888 	if (*errorp)
1889 		(void) __ns_ldap_freeError(errorp);
1890 
1891 	if (cred == NULL) {
1892 		/* Get the authentication method list */
1893 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
1894 			(void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
1895 			return (rc);
1896 		if (aMethod == NULL) {
1897 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
1898 			if (aMethod == NULL)
1899 				return (NS_LDAP_MEMORY);
1900 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
1901 			if (aMethod[0] == NULL) {
1902 				free(aMethod);
1903 				return (NS_LDAP_MEMORY);
1904 			}
1905 			if (version == NS_LDAP_V1)
1906 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
1907 			else {
1908 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
1909 				(aMethod[0])->saslmech =
1910 					NS_LDAP_SASL_DIGEST_MD5;
1911 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
1912 			}
1913 		}
1914 
1915 		/* Get the credential level list */
1916 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
1917 			(void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
1918 			(void) __ns_ldap_freeParam((void ***)&aMethod);
1919 			return (rc);
1920 		}
1921 		if (cLevel == NULL) {
1922 			cLevel = (int **)calloc(2, sizeof (int *));
1923 			if (cLevel == NULL)
1924 				return (NS_LDAP_MEMORY);
1925 			cLevel[0] = (int *)calloc(1, sizeof (int));
1926 			if (cLevel[0] == NULL)
1927 				return (NS_LDAP_MEMORY);
1928 			if (version == NS_LDAP_V1)
1929 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
1930 			else
1931 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
1932 		}
1933 	}
1934 
1935 	/* setup the anon credential for anonymous connection */
1936 	(void) memset(&anon, 0, sizeof (ns_cred_t));
1937 	anon.auth.type = NS_LDAP_AUTH_NONE;
1938 
1939 	for (; ; ) {
1940 		if (cred != NULL) {
1941 			/* using specified auth method */
1942 			rc = makeConnection(&con, server, cred,
1943 				sessionId, timeoutSec, errorp,
1944 				fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
1945 				&badSrvrs);
1946 			if (rc == NS_LDAP_SUCCESS ||
1947 				rc == NS_LDAP_SUCCESS_WITH_INFO) {
1948 				*session = con;
1949 				break;
1950 			}
1951 		} else {
1952 			/* for every cred level */
1953 			for (cNext = cLevel; *cNext != NULL; cNext++) {
1954 				if (**cNext == NS_LDAP_CRED_ANON) {
1955 					/*
1956 					 * make connection anonymously
1957 					 * Free the down server list before
1958 					 * looping through
1959 					 */
1960 					if (badSrvrs && *badSrvrs) {
1961 						__s_api_free2dArray(badSrvrs);
1962 						badSrvrs = NULL;
1963 					}
1964 					rc = makeConnection(&con, server, &anon,
1965 						sessionId, timeoutSec, errorp,
1966 						fail_if_new_pwd_reqd,
1967 						nopasswd_acct_mgmt, &badSrvrs);
1968 					if (rc == NS_LDAP_SUCCESS ||
1969 						rc ==
1970 						NS_LDAP_SUCCESS_WITH_INFO) {
1971 						*session = con;
1972 						goto done;
1973 					}
1974 					continue;
1975 				}
1976 				/* for each cred level */
1977 				for (aNext = aMethod; *aNext != NULL; aNext++) {
1978 					/* make connection and authenticate */
1979 					/* with default credentials */
1980 					authp = NULL;
1981 					rc = __s_api_getDefaultAuth(*cNext,
1982 						*aNext, &authp);
1983 					if (rc != NS_LDAP_SUCCESS) {
1984 						continue;
1985 					}
1986 					/*
1987 					 * Free the down server list before
1988 					 * looping through
1989 					 */
1990 					if (badSrvrs && *badSrvrs) {
1991 						__s_api_free2dArray(badSrvrs);
1992 						badSrvrs = NULL;
1993 					}
1994 					rc = makeConnection(&con, server, authp,
1995 						sessionId, timeoutSec, errorp,
1996 						fail_if_new_pwd_reqd,
1997 						nopasswd_acct_mgmt, &badSrvrs);
1998 					(void) __ns_ldap_freeCred(&authp);
1999 					if (rc == NS_LDAP_SUCCESS ||
2000 						rc ==
2001 						NS_LDAP_SUCCESS_WITH_INFO) {
2002 						*session = con;
2003 						goto done;
2004 					}
2005 				}
2006 			}
2007 		}
2008 		if (flags & NS_LDAP_HARD) {
2009 			if (sec < LDAPMAXHARDLOOKUPTIME)
2010 				sec *= 2;
2011 			_sleep(sec);
2012 		} else {
2013 			break;
2014 		}
2015 	}
2016 
2017 done:
2018 	(void) __ns_ldap_freeParam((void ***)&aMethod);
2019 	(void) __ns_ldap_freeParam((void ***)&cLevel);
2020 
2021 	if (badSrvrs && *badSrvrs) {
2022 		/*
2023 		 * At this point, either we have a successful
2024 		 * connection or exhausted all the possible auths.
2025 		 * and creds. Mark the problem servers as down
2026 		 * so that the problem servers are not contacted
2027 		 * again until the refresh_ttl expires.
2028 		 */
2029 		(void) __s_api_removeBadServers(badSrvrs);
2030 		__s_api_free2dArray(badSrvrs);
2031 	}
2032 	return (rc);
2033 }
2034 
2035 #pragma fini(_free_sessionPool)
2036 static void
2037 _free_sessionPool()
2038 {
2039 	int id;
2040 
2041 	(void) mutex_lock(&sessionPoolLock);
2042 	if (sessionPool != NULL) {
2043 		for (id = 0; id < sessionPoolSize; id++)
2044 			_DropConnection(id + CONID_OFFSET, 0, 1);
2045 		free(sessionPool);
2046 		sessionPool = NULL;
2047 		sessionPoolSize = 0;
2048 	}
2049 	(void) mutex_unlock(&sessionPoolLock);
2050 }
2051