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