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