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