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