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