xref: /illumos-gate/usr/src/lib/libsldap/common/ns_connect.c (revision b5d0e4ebb3481165a26b3df1ad7d6b8dc4cfc027)
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 "ldappr.h"
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <procfs.h>
49 #include <unistd.h>
50 
51 extern unsigned int _sleep(unsigned int);
52 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
53 		LDAPControl **, LDAPControl **);
54 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
55 
56 static int openConnection(LDAP **, const char *, const ns_cred_t *,
57 		int, ns_ldap_error_t **, int, int);
58 static void
59 _DropConnection(ConnectionID cID, int flag, int fini);
60 /*
61  * sessionLock, wait4session, sessionTid
62  * are variables to synchronize the creation/retrieval of a connection.
63  * MTperCon is a flag to enable/disable multiple threads sharing the same
64  * connection.
65  * sessionPoolLock is a mutex lock for the connection pool.
66  * sharedConnNumber is the number of sharable connections in the pool.
67  * sharedConnNumberLock is a mutex for sharedConnNumber.
68  */
69 static mutex_t	sessionLock = DEFAULTMUTEX;
70 static int	wait4session = 0;
71 static thread_t sessionTid = 0;
72 int	MTperConn = 1;
73 static rwlock_t sessionPoolLock = DEFAULTRWLOCK;
74 
75 static Connection **sessionPool = NULL;
76 static int sessionPoolSize = 0;
77 static int sharedConnNumber = 0;
78 static mutex_t sharedConnNumberLock = DEFAULTMUTEX;
79 
80 static int check_nscd_proc(pid_t pid, boolean_t check_uid);
81 
82 /*
83  * SSF values are for SASL integrity & privacy.
84  * JES DS5.2 does not support this feature but DS6 does.
85  * The values between 0 and 65535 can work with both server versions.
86  */
87 #define	MAX_SASL_SSF	65535
88 #define	MIN_SASL_SSF	0
89 
90 /* Number of hostnames to allocate memory for */
91 #define	NUMTOMALLOC	32
92 /*
93  * ns_mtckey is for sharing a ldap connection among multiple
94  * threads; created by ns_ldap_init() in ns_init.c
95  */
96 extern thread_key_t ns_mtckey;
97 
98 /* Per thread LDAP error resides in thread-specific data. */
99 struct ldap_error {
100 	int	le_errno;
101 	char	*le_matched;
102 	char	*le_errmsg;
103 };
104 
105 static struct ldap_error ldap_error_NULL = { LDAP_SUCCESS, NULL, NULL};
106 
107 /* destructor */
108 void
109 ns_tsd_cleanup(void *key) {
110 	struct ldap_error *le = (struct ldap_error *)key;
111 
112 	if (le == NULL)
113 		return;
114 	if (le->le_matched != NULL) {
115 		ldap_memfree(le->le_matched);
116 	}
117 	if (le->le_errmsg != NULL) {
118 		ldap_memfree(le->le_errmsg);
119 	}
120 	free(le);
121 }
122 
123 /* Callback function for allocating a mutex */
124 static void *
125 ns_mutex_alloc(void)
126 {
127 	mutex_t *mutexp = NULL;
128 
129 	if ((mutexp = malloc(sizeof (mutex_t))) != NULL) {
130 		if (mutex_init(mutexp, USYNC_THREAD, NULL) != 0) {
131 			free(mutexp);
132 			mutexp = NULL;
133 		}
134 	}
135 	return (mutexp);
136 }
137 
138 /* Callback function for freeing a mutex */
139 static void
140 ns_mutex_free(void *mutexp)
141 {
142 	(void) mutex_destroy((mutex_t *)mutexp);
143 	free(mutexp);
144 }
145 
146 /*
147  * Function for setting up thread-specific data
148  * where per thread LDAP error is stored
149  */
150 static int
151 tsd_setup()
152 {
153 	void	*tsd;
154 	int	rc;
155 
156 	/* return success if TSD already set */
157 	rc = thr_getspecific(ns_mtckey, &tsd);
158 	if (rc == 0 && tsd != NULL)
159 		return (0);
160 
161 	/* allocate and set TSD */
162 	tsd = (void *) calloc(1, sizeof (struct ldap_error));
163 	if (tsd == NULL)
164 		return (-1);
165 	rc = thr_setspecific(ns_mtckey, tsd);
166 	if (rc != 0) { /* must be ENOMEM */
167 		free(tsd);
168 		return (-1);
169 	}
170 	return (0);
171 
172 
173 }
174 
175 /* Callback function for setting the per thread LDAP error */
176 /*ARGSUSED*/
177 static void
178 set_ld_error(int err, char *matched, char *errmsg, void *dummy)
179 {
180 	struct ldap_error	*le;
181 
182 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
183 		syslog(LOG_ERR, "set_ld_error: thr_getspecific failed. errno"
184 		    " %d", errno);
185 		return;
186 	}
187 
188 	/* play safe, do nothing if TSD pointer is NULL */
189 	if (le == NULL)
190 		return;
191 
192 	le->le_errno = err;
193 	if (le->le_matched != NULL) {
194 		ldap_memfree(le->le_matched);
195 	}
196 	le->le_matched = matched;
197 	if (le->le_errmsg != NULL) {
198 		ldap_memfree(le->le_errmsg);
199 	}
200 	le->le_errmsg = errmsg;
201 }
202 
203 int
204 /* check and allocate the thread-specific data for using a shared connection */
205 __s_api_check_MTC_tsd()
206 {
207 	if (tsd_setup() != 0)
208 		return (NS_LDAP_MEMORY);
209 
210 	return (NS_LDAP_SUCCESS);
211 }
212 
213 /* Callback function for getting the per thread LDAP error */
214 /*ARGSUSED*/
215 static int
216 get_ld_error(char **matched, char **errmsg, void *dummy)
217 {
218 	struct ldap_error	*le;
219 
220 	if (thr_getspecific(ns_mtckey, (void **)&le) != 0) {
221 		syslog(LOG_ERR, "get_ld_error: thr_getspecific failed. errno"
222 		    " %d", errno);
223 		return (errno);
224 	}
225 
226 	/* play safe, return NULL error data, if TSD pointer is NULL */
227 	if (le == NULL)
228 		le = &ldap_error_NULL;
229 
230 	if (matched != NULL) {
231 		*matched = le->le_matched;
232 	}
233 	if (errmsg != NULL) {
234 		*errmsg = le->le_errmsg;
235 	}
236 	return (le->le_errno);
237 }
238 
239 /* Callback function for setting per thread errno */
240 static void
241 set_errno(int err)
242 {
243 	errno = err;
244 }
245 
246 /* Callback function for getting per thread errno */
247 static int
248 get_errno(void)
249 {
250 	return (errno);
251 }
252 
253 /*
254  * set up to allow multiple threads to use the same ldap connection
255  */
256 static int
257 setup_mt_conn(LDAP *ld)
258 {
259 
260 	struct ldap_thread_fns		tfns;
261 	struct ldap_extra_thread_fns	extrafns;
262 	int				rc;
263 
264 	/*
265 	 * Set the function pointers for dealing with mutexes
266 	 * and error information
267 	 */
268 	(void) memset(&tfns, '\0', sizeof (struct ldap_thread_fns));
269 	tfns.ltf_mutex_alloc = (void *(*)(void)) ns_mutex_alloc;
270 	tfns.ltf_mutex_free = (void (*)(void *)) ns_mutex_free;
271 	tfns.ltf_mutex_lock = (int (*)(void *)) mutex_lock;
272 	tfns.ltf_mutex_unlock = (int (*)(void *)) mutex_unlock;
273 	tfns.ltf_get_errno = get_errno;
274 	tfns.ltf_set_errno = set_errno;
275 	tfns.ltf_get_lderrno = get_ld_error;
276 	tfns.ltf_set_lderrno = set_ld_error;
277 	tfns.ltf_lderrno_arg = NULL;
278 
279 	/*
280 	 * Set up this session to use those function pointers
281 	 */
282 	rc = ldap_set_option(ld, LDAP_OPT_THREAD_FN_PTRS,
283 	    (void *) &tfns);
284 	if (rc < 0) {
285 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
286 		"(LDAP_OPT_THREAD_FN_PTRS)");
287 		return (-1);
288 	}
289 
290 	/*
291 	 * Set the function pointers for working with semaphores
292 	 */
293 	(void) memset(&extrafns, '\0',
294 	    sizeof (struct ldap_extra_thread_fns));
295 	extrafns.ltf_threadid_fn = (void * (*)(void))thr_self;
296 	extrafns.ltf_mutex_trylock = NULL;
297 	extrafns.ltf_sema_alloc = NULL;
298 	extrafns.ltf_sema_free = NULL;
299 	extrafns.ltf_sema_wait = NULL;
300 	extrafns.ltf_sema_post = NULL;
301 
302 	/* Set up this session to use those function pointers */
303 	rc = ldap_set_option(ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
304 	    (void *) &extrafns);
305 	if (rc < 0) {
306 		syslog(LOG_WARNING, "libsldap: ldap_set_option "
307 		"(LDAP_OPT_EXTRA_THREAD_FN_PTRS)");
308 		return (-1);
309 	}
310 
311 	return (0);
312 }
313 
314 static void
315 ns_setup_mt_conn_and_tsd(LDAP *ld) {
316 	thread_t t = thr_self();
317 	void *tsd;
318 	/* set up to share this connection among threads */
319 	if (MTperConn == 1) {
320 		if (tsd_setup() == -1) {
321 			syslog(LOG_ERR, "tid= %d: unable "
322 				"to set up TSD\n", t);
323 		} else {
324 			if (setup_mt_conn(ld) == -1) {
325 			/* multiple threads per connection not supported */
326 				syslog(LOG_ERR, "tid= %d: multiple "
327 					"threads per connection not "
328 					"supported\n", t);
329 				(void) thr_getspecific(ns_mtckey, &tsd);
330 				ns_tsd_cleanup(tsd);
331 				(void) thr_setspecific(ns_mtckey, NULL);
332 				MTperConn = 0;
333 			}
334 		}
335 	}
336 }
337 
338 /*
339  * Check name and UID of process, if it is nscd.
340  *
341  * Input:
342  *   pid	: PID of checked process
343  *   check_uid	: check if UID == 0
344  * Output:
345  *   1	: nscd detected
346  *   0	: nscd not confirmed
347  */
348 static int
349 check_nscd_proc(pid_t pid, boolean_t check_uid)
350 {
351 	psinfo_t	pinfo;
352 	char		fname[MAXPATHLEN];
353 	ssize_t		ret;
354 	int		fd;
355 
356 	if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
357 		if ((fd = open(fname,  O_RDONLY)) >= 0) {
358 			ret = read(fd, &pinfo, sizeof (psinfo_t));
359 			(void) close(fd);
360 			if ((ret == sizeof (psinfo_t)) &&
361 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
362 				if (check_uid && (pinfo.pr_uid != 0))
363 					return (0);
364 				return (1);
365 			}
366 		}
367 	}
368 	return (0);
369 }
370 
371 /*
372  * Check if this process is peruser nscd.
373  */
374 int
375 __s_api_peruser_proc(void)
376 {
377 	pid_t		my_ppid;
378 	static mutex_t	nscdLock = DEFAULTMUTEX;
379 	static pid_t	checkedPpid = (pid_t)-1;
380 	static int	isPeruserNscd = 0;
381 
382 	my_ppid = getppid();
383 
384 	/*
385 	 * Already checked before for this process? If yes, return cached
386 	 * response.
387 	 */
388 	if (my_ppid == checkedPpid) {
389 		return (isPeruserNscd);
390 	}
391 
392 	(void) mutex_lock(&nscdLock);
393 
394 	/* Check once more incase another thread has just complete this. */
395 	if (my_ppid == checkedPpid) {
396 		(void) mutex_unlock(&nscdLock);
397 		return (isPeruserNscd);
398 	}
399 
400 	/* Reinitialize to be sure there is no residue after fork. */
401 	isPeruserNscd = 0;
402 
403 	/* Am I the nscd process? */
404 	if (check_nscd_proc(getpid(), B_FALSE)) {
405 		/* Is my parent the nscd process with UID == 0. */
406 		isPeruserNscd = check_nscd_proc(my_ppid, B_TRUE);
407 	}
408 
409 	/* Remeber for whom isPeruserNscd is. */
410 	checkedPpid = my_ppid;
411 
412 	(void) mutex_unlock(&nscdLock);
413 	return (isPeruserNscd);
414 }
415 
416 /*
417  * Check if this process is main nscd.
418  */
419 int
420 __s_api_nscd_proc(void)
421 {
422 	pid_t		my_pid;
423 	static mutex_t	nscdLock = DEFAULTMUTEX;
424 	static pid_t	checkedPid = (pid_t)-1;
425 	static int	isMainNscd = 0;
426 
427 	/*
428 	 * Don't bother checking if this process isn't root, this cannot
429 	 * be main nscd.
430 	 */
431 	if (getuid() != 0)
432 		return (0);
433 
434 	my_pid = getpid();
435 
436 	/*
437 	 * Already checked before for this process? If yes, return cached
438 	 * response.
439 	 */
440 	if (my_pid == checkedPid) {
441 		return (isMainNscd);
442 	}
443 
444 	(void) mutex_lock(&nscdLock);
445 
446 	/* Check once more incase another thread has just done this. */
447 	if (my_pid == checkedPid) {
448 		(void) mutex_unlock(&nscdLock);
449 		return (isMainNscd);
450 	}
451 
452 	/*
453 	 * Am I the nscd process? UID is already checked, not needed from
454 	 * psinfo.
455 	 */
456 	isMainNscd = check_nscd_proc(my_pid, B_FALSE);
457 
458 	/* Remeber for whom isMainNscd is. */
459 	checkedPid = my_pid;
460 
461 	(void) mutex_unlock(&nscdLock);
462 	return (isMainNscd);
463 }
464 
465 /*
466  * This function requests a server from the cache manager through
467  * the door functionality
468  */
469 
470 static int
471 __s_api_requestServer(const char *request, const char *server,
472 	ns_server_info_t *ret, ns_ldap_error_t **error,  const char *addrType)
473 {
474 	union {
475 		ldap_data_t	s_d;
476 		char		s_b[DOORBUFFERSIZE];
477 	} space;
478 	ldap_data_t	*sptr;
479 	int		ndata;
480 	int		adata;
481 	char		errstr[MAXERROR];
482 	const char	*ireq;
483 	char		*rbuf, *ptr, *rest;
484 	char		*dptr;
485 	char		**mptr, **mptr1, **cptr, **cptr1;
486 	int		mcnt, ccnt;
487 	char		**servers;
488 	int		rc, len;
489 
490 	if (ret == NULL || error == NULL) {
491 		return (NS_LDAP_OP_FAILED);
492 	}
493 	(void) memset(ret, 0, sizeof (ns_server_info_t));
494 	*error = NULL;
495 
496 	(void) memset(space.s_b, 0, DOORBUFFERSIZE);
497 
498 	if (request == NULL)
499 		ireq = NS_CACHE_NEW;
500 	else
501 		ireq = request;
502 
503 	adata = (sizeof (ldap_call_t) + strlen(ireq) + strlen(addrType) + 1);
504 	if (server != NULL) {
505 		adata += strlen(DOORLINESEP) + 1;
506 		adata += strlen(server) + 1;
507 	}
508 	ndata = sizeof (space);
509 	len = sizeof (space) - sizeof (space.s_d.ldap_call.ldap_callnumber);
510 	space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
511 	if (strlcpy(space.s_d.ldap_call.ldap_u.domainname, ireq, len) >= len)
512 		return (NS_LDAP_MEMORY);
513 	if (strlcat(space.s_d.ldap_call.ldap_u.domainname, addrType, len) >=
514 	    len)
515 		return (NS_LDAP_MEMORY);
516 	if (server != NULL) {
517 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname,
518 		    DOORLINESEP, len) >= len)
519 			return (NS_LDAP_MEMORY);
520 		if (strlcat(space.s_d.ldap_call.ldap_u.domainname, server,
521 		    len) >= len)
522 			return (NS_LDAP_MEMORY);
523 	}
524 	sptr = &space.s_d;
525 
526 	switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
527 	case SUCCESS:
528 		break;
529 	/* this case is for when the $mgr is not running, but ldapclient */
530 	/* is trying to initialize things */
531 	case NOSERVER:
532 		/* get first server from config list unavailable otherwise */
533 		servers = NULL;
534 		rc = __s_api_getServers(&servers, error);
535 		if (rc != NS_LDAP_SUCCESS) {
536 			if (servers != NULL) {
537 				__s_api_free2dArray(servers);
538 				servers = NULL;
539 			}
540 			return (rc);
541 		}
542 		if (servers == NULL || servers[0] == NULL) {
543 			__s_api_free2dArray(servers);
544 			servers = NULL;
545 			(void) sprintf(errstr,
546 			    gettext("No server found in configuration"));
547 			MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
548 			    strdup(errstr), NULL);
549 			return (NS_LDAP_CONFIG);
550 		}
551 		ret->server = strdup(servers[0]);
552 		if (ret->server == NULL) {
553 			__s_api_free2dArray(servers);
554 			return (NS_LDAP_MEMORY);
555 		}
556 		ret->saslMechanisms = NULL;
557 		ret->controls = NULL;
558 		__s_api_free2dArray(servers);
559 		servers = NULL;
560 		return (NS_LDAP_SUCCESS);
561 	case NOTFOUND:
562 	default:
563 		return (NS_LDAP_OP_FAILED);
564 	}
565 
566 	/* copy info from door call return structure here */
567 	rbuf =  space.s_d.ldap_ret.ldap_u.config;
568 
569 	/* Get the host */
570 	ptr = strtok_r(rbuf, DOORLINESEP, &rest);
571 	if (ptr == NULL) {
572 		(void) sprintf(errstr, gettext("No server returned from "
573 		    "ldap_cachemgr"));
574 		MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
575 		    strdup(errstr), NULL);
576 		return (NS_LDAP_OP_FAILED);
577 	}
578 	ret->server = strdup(ptr);
579 	if (ret->server == NULL) {
580 		return (NS_LDAP_MEMORY);
581 	}
582 	/* Get the host FQDN format */
583 	if (strcmp(addrType, NS_CACHE_ADDR_HOSTNAME) == 0) {
584 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
585 		if (ptr == NULL) {
586 			(void) sprintf(errstr, gettext("No server FQDN format "
587 			    "returned from ldap_cachemgr"));
588 			MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
589 			    strdup(errstr), NULL);
590 			free(ret->server);
591 			ret->server = NULL;
592 			return (NS_LDAP_OP_FAILED);
593 		}
594 		ret->serverFQDN = strdup(ptr);
595 		if (ret->serverFQDN == NULL) {
596 			free(ret->server);
597 			ret->server = NULL;
598 			return (NS_LDAP_MEMORY);
599 		}
600 	}
601 
602 	/* get the Supported Controls/SASL mechs */
603 	mptr = NULL;
604 	mcnt = 0;
605 	cptr = NULL;
606 	ccnt = 0;
607 	for (; ; ) {
608 		ptr = strtok_r(NULL, DOORLINESEP, &rest);
609 		if (ptr == NULL)
610 			break;
611 		if (strncasecmp(ptr, _SASLMECHANISM,
612 		    _SASLMECHANISM_LEN) == 0) {
613 			dptr = strchr(ptr, '=');
614 			if (dptr == NULL)
615 				continue;
616 			dptr++;
617 			mptr1 = (char **)realloc((void *)mptr,
618 			    sizeof (char *) * (mcnt+2));
619 			if (mptr1 == NULL) {
620 				__s_api_free2dArray(mptr);
621 				if (sptr != &space.s_d) {
622 					(void) munmap((char *)sptr, ndata);
623 				}
624 				__s_api_free2dArray(cptr);
625 				__s_api_free_server_info(ret);
626 				return (NS_LDAP_MEMORY);
627 			}
628 			mptr = mptr1;
629 			mptr[mcnt] = strdup(dptr);
630 			if (mptr[mcnt] == NULL) {
631 				if (sptr != &space.s_d) {
632 					(void) munmap((char *)sptr, ndata);
633 				}
634 				__s_api_free2dArray(cptr);
635 				cptr = NULL;
636 				__s_api_free2dArray(mptr);
637 				mptr = NULL;
638 				__s_api_free_server_info(ret);
639 				return (NS_LDAP_MEMORY);
640 			}
641 			mcnt++;
642 			mptr[mcnt] = NULL;
643 		}
644 		if (strncasecmp(ptr, _SUPPORTEDCONTROL,
645 		    _SUPPORTEDCONTROL_LEN) == 0) {
646 			dptr = strchr(ptr, '=');
647 			if (dptr == NULL)
648 				continue;
649 			dptr++;
650 			cptr1 = (char **)realloc((void *)cptr,
651 			    sizeof (char *) * (ccnt+2));
652 			if (cptr1 == NULL) {
653 				if (sptr != &space.s_d) {
654 					(void) munmap((char *)sptr, ndata);
655 				}
656 				__s_api_free2dArray(cptr);
657 				__s_api_free2dArray(mptr);
658 				mptr = NULL;
659 				__s_api_free_server_info(ret);
660 				return (NS_LDAP_MEMORY);
661 			}
662 			cptr = cptr1;
663 			cptr[ccnt] = strdup(dptr);
664 			if (cptr[ccnt] == NULL) {
665 				if (sptr != &space.s_d) {
666 					(void) munmap((char *)sptr, ndata);
667 				}
668 				__s_api_free2dArray(cptr);
669 				cptr = NULL;
670 				__s_api_free2dArray(mptr);
671 				mptr = NULL;
672 				__s_api_free_server_info(ret);
673 				return (NS_LDAP_MEMORY);
674 			}
675 			ccnt++;
676 			cptr[ccnt] = NULL;
677 		}
678 	}
679 	if (mptr != NULL) {
680 		ret->saslMechanisms = mptr;
681 	}
682 	if (cptr != NULL) {
683 		ret->controls = cptr;
684 	}
685 
686 
687 	/* clean up door call */
688 	if (sptr != &space.s_d) {
689 		(void) munmap((char *)sptr, ndata);
690 	}
691 	*error = NULL;
692 
693 	return (NS_LDAP_SUCCESS);
694 }
695 
696 
697 /*
698  * printCred(): prints the credential structure
699  */
700 static void
701 printCred(int pri, const ns_cred_t *cred)
702 {
703 	thread_t	t = thr_self();
704 
705 	if (cred == NULL) {
706 		syslog(LOG_ERR, "tid= %d: printCred: cred is NULL\n", t);
707 		return;
708 	}
709 
710 	syslog(pri, "tid= %d: AuthType=%d", t, cred->auth.type);
711 	syslog(pri, "tid= %d: TlsType=%d", t, cred->auth.tlstype);
712 	syslog(pri, "tid= %d: SaslMech=%d", t, cred->auth.saslmech);
713 	syslog(pri, "tid= %d: SaslOpt=%d", t, cred->auth.saslopt);
714 	if (cred->hostcertpath)
715 		syslog(pri, "tid= %d: hostCertPath=%s\n",
716 		    t, cred->hostcertpath);
717 	if (cred->cred.unix_cred.userID)
718 		syslog(pri, "tid= %d: userID=%s\n",
719 		    t, cred->cred.unix_cred.userID);
720 #ifdef DEBUG
721 	if (cred->cred.unix_cred.passwd)
722 		syslog(pri, "tid= %d: passwd=%s\n",
723 		    t, cred->cred.unix_cred.passwd);
724 #endif
725 }
726 
727 /*
728  * printConnection(): prints the connection structure
729  */
730 static void
731 printConnection(int pri, Connection *con)
732 {
733 	thread_t	t = thr_self();
734 
735 	if (con == NULL)
736 		return;
737 
738 	syslog(pri, "tid= %d: connectionID=%d\n", t, con->connectionId);
739 	syslog(pri, "tid= %d: shared=%d\n", t, con->shared);
740 	syslog(pri, "tid= %d: usedBit=%d\n", t, con->usedBit);
741 	syslog(pri, "tid= %d: threadID=%d\n", t, con->threadID);
742 	if (con->serverAddr) {
743 		syslog(pri, "tid= %d: serverAddr=%s\n",
744 		    t, con->serverAddr);
745 	}
746 	printCred(pri, con->auth);
747 }
748 
749 
750 
751 /*
752  * addConnection(): set up a connection so that it can be shared
753  * among multiple threads and then insert the connection in the
754  * connection list.
755  * Returns: -1 = failure, new Connection ID = success
756  *
757  * This function could exit with sessionLock locked. It will be
758  * be unlocked in __s_api_getConnection() when it exits without getting a
759  * connection.
760  */
761 static int
762 addConnection(Connection *con)
763 {
764 	int i, noMTperC = 0;
765 	thread_t t = thr_self();
766 	struct ldap_thread_fns tfns;
767 	void *tsd;
768 
769 	if (!con)
770 		return (-1);
771 
772 	syslog(LOG_DEBUG, "tid= %d: Adding connection (serverAddr=%s)",
773 	    t, con->serverAddr);
774 
775 	if (MTperConn == 1) {
776 		/*
777 		 * Make sure ld has proper thread functions and tsd
778 		 * is set up.
779 		 */
780 		(void) memset(&tfns, 0, sizeof (struct ldap_thread_fns));
781 		/*
782 		 * ldap_init sets ltf_get_lderrno and ltf_set_lderrno to NULLs.
783 		 * It's supposed to be overwritten by ns_setup_mt_conn_and_tsd.
784 		 */
785 		if (ldap_get_option(con->ld, LDAP_OPT_THREAD_FN_PTRS,
786 		    (void *)&tfns) != 0 ||
787 		    tfns.ltf_get_lderrno != get_ld_error ||
788 		    tfns.ltf_set_lderrno != set_ld_error) {
789 			MTperConn = 0;
790 			noMTperC = 1;
791 		} else {
792 			if (thr_getspecific(ns_mtckey, &tsd) != 0 ||
793 			    tsd == NULL)
794 				noMTperC = 1;
795 		}
796 
797 	} else {
798 		noMTperC = 1;
799 	}
800 
801 	(void) rw_wrlock(&sessionPoolLock);
802 	if (sessionPool == NULL) {
803 		sessionPoolSize = SESSION_CACHE_INC;
804 		sessionPool = calloc(sessionPoolSize,
805 		    sizeof (struct connection **));
806 		if (!sessionPool) {
807 			(void) rw_unlock(&sessionPoolLock);
808 			return (-1);
809 		}
810 
811 		syslog(LOG_DEBUG, "tid= %d: Initialized sessionPool", t);
812 	}
813 	for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
814 		;
815 	if (i == sessionPoolSize) {
816 		/* run out of array, need to increase sessionPool */
817 		Connection **cl;
818 		cl = (Connection **) realloc(sessionPool,
819 		    (sessionPoolSize + SESSION_CACHE_INC) *
820 		    sizeof (Connection *));
821 		if (!cl) {
822 			(void) rw_unlock(&sessionPoolLock);
823 			return (-1);
824 		}
825 		(void) memset(cl + sessionPoolSize, 0,
826 		    SESSION_CACHE_INC * sizeof (struct connection *));
827 		sessionPool = cl;
828 		sessionPoolSize += SESSION_CACHE_INC;
829 		syslog(LOG_DEBUG, "tid: %d: Increased "
830 		    "sessionPoolSize to: %d\n",
831 		    t, sessionPoolSize);
832 	}
833 	sessionPool[i] = con;
834 	if (noMTperC == 0) {
835 		con->shared++;
836 		con->pid = getpid();
837 		(void) mutex_lock(&sharedConnNumberLock);
838 		sharedConnNumber++;
839 		(void) mutex_unlock(&sharedConnNumberLock);
840 	} else
841 		con->usedBit = B_TRUE;
842 
843 	(void) rw_unlock(&sessionPoolLock);
844 
845 	con->connectionId = i + CONID_OFFSET;
846 
847 	syslog(LOG_DEBUG, "tid= %d: Connection added [%d]\n",
848 	    t, i);
849 	printConnection(LOG_DEBUG, con);
850 
851 	/*
852 	 * A connection can be shared now, unlock
853 	 * the session mutex and let other
854 	 * threads try to use this connection or
855 	 * get their own.
856 	 */
857 	if (wait4session != 0 && sessionTid == thr_self()) {
858 		wait4session = 0;
859 		sessionTid = 0;
860 		syslog(LOG_DEBUG, "tid= %d: unlocking sessionLock\n", t);
861 		(void) mutex_unlock(&sessionLock);
862 	}
863 
864 	return (i + CONID_OFFSET);
865 }
866 
867 /*
868  * See if the specified session matches a currently available
869  */
870 
871 static int
872 findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
873 	Connection **conp)
874 {
875 	Connection *cp;
876 	int id;
877 
878 	if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
879 		return (-1);
880 
881 	/*
882 	 * If a new connection is requested, no need to continue.
883 	 * If the process is not nscd and is not requesting keep connections
884 	 * alive, no need to continue.
885 	 */
886 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
887 	    !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
888 		return (-1);
889 
890 	*conp = NULL;
891 	if (sessionPool == NULL)
892 		return (-1);
893 	id = cID - CONID_OFFSET;
894 	if (id < 0 || id >= sessionPoolSize)
895 		return (-1);
896 
897 	(void) rw_rdlock(&sessionPoolLock);
898 	if (sessionPool[id] == NULL) {
899 		(void) rw_unlock(&sessionPoolLock);
900 		return (-1);
901 	}
902 	cp = sessionPool[id];
903 
904 	/*
905 	 * Make sure the connection has the same type of authentication method
906 	 */
907 	if ((cp->usedBit) ||
908 	    (cp->notAvail) ||
909 	    (cp->auth->auth.type != auth->auth.type) ||
910 	    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
911 	    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
912 	    (cp->auth->auth.saslopt != auth->auth.saslopt)) {
913 		(void) rw_unlock(&sessionPoolLock);
914 		return (-1);
915 	}
916 	if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
917 	    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
918 	    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
919 	    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
920 	    ((cp->auth->cred.unix_cred.userID == NULL) ||
921 	    (strcasecmp(cp->auth->cred.unix_cred.userID,
922 	    auth->cred.unix_cred.userID) != 0))) {
923 		(void) rw_unlock(&sessionPoolLock);
924 		return (-1);
925 	}
926 
927 	/* An existing connection is found but it needs to be reset */
928 	if (flags & NS_LDAP_NEW_CONN) {
929 		(void) rw_unlock(&sessionPoolLock);
930 		DropConnection(cID, 0);
931 		return (-1);
932 	}
933 	/* found an available connection */
934 	cp->usedBit = B_TRUE;
935 	(void) rw_unlock(&sessionPoolLock);
936 	cp->threadID = thr_self();
937 	*conp = cp;
938 	return (cID);
939 }
940 
941 /*
942  * findConnection(): find an available connection from the list
943  * that matches the criteria specified in Connection structure.
944  * If serverAddr is NULL, then find a connection to any server
945  * as long as it matches the rest of the parameters.
946  * Returns: -1 = failure, the Connection ID found = success.
947  *
948  * This function could exit with sessionLock locked. It will be
949  * be unlocked in addConnection() when this thread adds the connection
950  * to the pool or in __s_api_getConnection() when it exits without getting a
951  * connection.
952  */
953 #define	TRY_TIMES	10
954 static int
955 findConnection(int flags, const char *serverAddr,
956 	const ns_cred_t *auth, Connection **conp)
957 {
958 	Connection *cp;
959 	int i, j, conn_server_index, up_server_index, drop_conn;
960 	int rc;
961 	int try;
962 	ns_server_info_t sinfo;
963 	ns_ldap_error_t *errorp = NULL;
964 	char **servers;
965 	void **paramVal = NULL;
966 #ifdef DEBUG
967 	thread_t t = thr_self();
968 #endif /* DEBUG */
969 
970 	if (auth == NULL || conp == NULL)
971 		return (-1);
972 	*conp = NULL;
973 
974 	/*
975 	 * If a new connection is requested, no need to continue.
976 	 * If the process is not nscd and is not requesting keep connections
977 	 * alive, no need to continue.
978 	 */
979 	if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
980 	    !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
981 		return (-1);
982 
983 #ifdef DEBUG
984 	(void) fprintf(stderr, "tid= %d: Find connection\n", t);
985 	(void) fprintf(stderr, "tid= %d: Looking for ....\n", t);
986 	if (serverAddr && *serverAddr)
987 		(void) fprintf(stderr, "tid= %d: serverAddr=%s\n",
988 		    t, serverAddr);
989 	else
990 		(void) fprintf(stderr, "tid= %d: serverAddr=NULL\n", t);
991 	printCred(LOG_DEBUG, auth);
992 	fflush(stderr);
993 #endif /* DEBUG */
994 
995 	/*
996 	 * If multiple threads per connection not supported,
997 	 * no sessionPool means no connection
998 	 */
999 	(void) rw_rdlock(&sessionPoolLock);
1000 	if (MTperConn == 0 && sessionPool == NULL) {
1001 		(void) rw_unlock(&sessionPoolLock);
1002 		return (-1);
1003 	}
1004 
1005 	/*
1006 	 * If no sharable connections in cache, then serialize the opening
1007 	 * of connections. Make sure only one is being opened
1008 	 * at a time. Otherwise, we may end up with more
1009 	 * connections than we want (if multiple threads get
1010 	 * here at the same time)
1011 	 */
1012 	(void) mutex_lock(&sharedConnNumberLock);
1013 	if (sessionPool == NULL || (sharedConnNumber == 0 && MTperConn == 1)) {
1014 		(void) mutex_unlock(&sharedConnNumberLock);
1015 		(void) rw_unlock(&sessionPoolLock);
1016 		(void) mutex_lock(&sessionLock);
1017 		(void) mutex_lock(&sharedConnNumberLock);
1018 		if (sessionPool == NULL || (sharedConnNumber == 0 &&
1019 		    MTperConn == 1)) {
1020 			(void) mutex_unlock(&sharedConnNumberLock);
1021 			wait4session = 1;
1022 			sessionTid = thr_self();
1023 #ifdef DEBUG
1024 			(void) fprintf(stderr, "tid= %d: get "
1025 			    "connection ... \n", t);
1026 			fflush(stderr);
1027 #endif /* DEBUG */
1028 			/*
1029 			 * Exit with sessionLock locked. It will be
1030 			 * be unlocked in addConnection() when this
1031 			 * thread adds the connection to the pool or
1032 			 * in __s_api_getConnection() when it exits
1033 			 * without getting a connection.
1034 			 */
1035 			return (-1);
1036 		}
1037 
1038 #ifdef DEBUG
1039 		(void) fprintf(stderr, "tid= %d: shareable connections "
1040 		    "exist\n", t);
1041 		fflush(stderr);
1042 #endif /* DEBUG */
1043 		(void) mutex_unlock(&sharedConnNumberLock);
1044 		/*
1045 		 * There are sharable connections, check to see if
1046 		 * one can be shared.
1047 		 */
1048 		(void) mutex_unlock(&sessionLock);
1049 		(void) rw_rdlock(&sessionPoolLock);
1050 	} else
1051 		(void) mutex_unlock(&sharedConnNumberLock);
1052 
1053 	try = 0;
1054 	check_again:
1055 
1056 	for (i = 0; i < sessionPoolSize; ++i) {
1057 		if (sessionPool[i] == NULL)
1058 			continue;
1059 		cp = sessionPool[i];
1060 #ifdef DEBUG
1061 		(void) fprintf(stderr, "tid= %d: checking connection "
1062 		    "[%d] ....\n", t, i);
1063 		printConnection(LOG_DEBUG, cp);
1064 #endif /* DEBUG */
1065 		if ((cp->usedBit) || (cp->notAvail) ||
1066 		    (cp->auth->auth.type != auth->auth.type) ||
1067 		    (cp->auth->auth.tlstype != auth->auth.tlstype) ||
1068 		    (cp->auth->auth.saslmech != auth->auth.saslmech) ||
1069 		    (cp->auth->auth.saslopt != auth->auth.saslopt) ||
1070 		    (serverAddr && *serverAddr &&
1071 		    (strcasecmp(serverAddr, cp->serverAddr) != 0)))
1072 			continue;
1073 		if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
1074 		    ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
1075 		    (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
1076 		    (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
1077 		    ((cp->auth->cred.unix_cred.userID == NULL) ||
1078 		    (cp->auth->cred.unix_cred.passwd == NULL) ||
1079 		    ((strcasecmp(cp->auth->cred.unix_cred.userID,
1080 		    auth->cred.unix_cred.userID) != 0)) ||
1081 		    ((strcmp(cp->auth->cred.unix_cred.passwd,
1082 		    auth->cred.unix_cred.passwd) != 0))))
1083 				continue;
1084 		if (!(serverAddr && *serverAddr)) {
1085 			/*
1086 			 * Get preferred server list.
1087 			 * When preferred servers are merged with default
1088 			 * servers (S_LDAP_SERVER_P) by __s_api_getServer,
1089 			 * preferred servers are copied sequencially.
1090 			 * The order should be the same as the order retrieved
1091 			 * by __ns_ldap_getParam.
1092 			 */
1093 			if ((rc = __ns_ldap_getParam(NS_LDAP_SERVER_PREF_P,
1094 			    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
1095 				(void) __ns_ldap_freeError(&errorp);
1096 				(void) __ns_ldap_freeParam(&paramVal);
1097 				(void) rw_unlock(&sessionPoolLock);
1098 				return (-1);
1099 			}
1100 			servers = (char **)paramVal;
1101 			/*
1102 			 * Do fallback only if preferred servers are defined.
1103 			 */
1104 			if (servers != NULL) {
1105 				/*
1106 				 * Find the 1st available server
1107 				 */
1108 				rc = __s_api_requestServer(NS_CACHE_NEW, NULL,
1109 				    &sinfo, &errorp, NS_CACHE_ADDR_IP);
1110 				if (rc != NS_LDAP_SUCCESS) {
1111 					/*
1112 					 * Drop the connection.
1113 					 * Pass 1 to fini so it won't be locked
1114 					 * inside _DropConnection
1115 					 */
1116 					_DropConnection(
1117 					    cp->connectionId,
1118 					    NS_LDAP_NEW_CONN, 1);
1119 					(void) rw_unlock(
1120 					    &sessionPoolLock);
1121 					(void) __ns_ldap_freeError(&errorp);
1122 					(void) __ns_ldap_freeParam(
1123 					    (void ***)&servers);
1124 					return (-1);
1125 				}
1126 
1127 				if (sinfo.server) {
1128 					/*
1129 					 * Test if cp->serverAddr is a
1130 					 * preferred server.
1131 					 */
1132 					conn_server_index = -1;
1133 					for (j = 0; servers[j] != NULL; j++) {
1134 						if (strcasecmp(servers[j],
1135 						    cp->serverAddr) == 0) {
1136 							conn_server_index = j;
1137 							break;
1138 						}
1139 					}
1140 					/*
1141 					 * Test if sinfo.server is a preferred
1142 					 * server.
1143 					 */
1144 					up_server_index = -1;
1145 					for (j = 0; servers[j] != NULL; j++) {
1146 						if (strcasecmp(sinfo.server,
1147 						    servers[j]) == 0) {
1148 							up_server_index = j;
1149 							break;
1150 						}
1151 					}
1152 
1153 					/*
1154 					 * The following code is to fall back
1155 					 * to preferred servers if servers
1156 					 * are previously down but are up now.
1157 					 * If cp->serverAddr is a preferred
1158 					 * server, it falls back to the servers
1159 					 * ahead of it. If cp->serverAddr is
1160 					 * not a preferred server, it falls
1161 					 * back to any of preferred servers
1162 					 * returned by ldap_cachemgr.
1163 					 */
1164 					if (conn_server_index >= 0 &&
1165 					    up_server_index >= 0) {
1166 						/*
1167 						 * cp->serverAddr and
1168 						 * sinfo.server are preferred
1169 						 * servers.
1170 						 */
1171 						if (up_server_index ==
1172 						    conn_server_index)
1173 							/*
1174 							 * sinfo.server is the
1175 							 * same as
1176 							 * cp->serverAddr.
1177 							 * Keep the connection.
1178 							 */
1179 							drop_conn = 0;
1180 						else
1181 							/*
1182 							 * 1.
1183 							 * up_server_index <
1184 							 * conn_server_index
1185 							 *
1186 							 * sinfo is ahead of
1187 							 * cp->serverAddr in
1188 							 * Need to fall back.
1189 							 * 2.
1190 							 * up_server_index >
1191 							 * conn_server_index
1192 							 * cp->serverAddr is
1193 							 * down. Drop it.
1194 							 */
1195 							drop_conn = 1;
1196 					} else if (conn_server_index >= 0 &&
1197 					    up_server_index == -1) {
1198 						/*
1199 						 * cp->serverAddr is a preferred
1200 						 * server but sinfo.server is
1201 						 * not. Preferred servers are
1202 						 * ahead of default servers.
1203 						 * This means cp->serverAddr is
1204 						 * down. Drop it.
1205 						 */
1206 						drop_conn = 1;
1207 					} else if (conn_server_index == -1 &&
1208 					    up_server_index >= 0) {
1209 						/*
1210 						 * cp->serverAddr is not a
1211 						 * preferred server but
1212 						 * sinfo.server is.
1213 						 * Fall back.
1214 						 */
1215 						drop_conn = 1;
1216 					} else {
1217 						/*
1218 						 * Both index are -1
1219 						 * cp->serverAddr and
1220 						 * sinfo.server are not
1221 						 * preferred servers.
1222 						 * No fallback.
1223 						 */
1224 						drop_conn = 0;
1225 					}
1226 					if (drop_conn) {
1227 						/*
1228 						 * Drop the connection so the
1229 						 * new conection can fall back
1230 						 * to a new server later.
1231 						 * Pass 1 to fini so it won't
1232 						 * be locked inside
1233 						 * _DropConnection
1234 						 */
1235 						_DropConnection(
1236 						    cp->connectionId,
1237 						    NS_LDAP_NEW_CONN, 1);
1238 						(void) rw_unlock(
1239 						    &sessionPoolLock);
1240 						(void) __ns_ldap_freeParam(
1241 						    (void ***)&servers);
1242 						__s_api_free_server_info(
1243 						    &sinfo);
1244 						return (-1);
1245 					} else {
1246 						/*
1247 						 * Keep the connection
1248 						 */
1249 						(void) __ns_ldap_freeParam(
1250 						    (void ***)&servers);
1251 						__s_api_free_server_info(
1252 						    &sinfo);
1253 					}
1254 				} else {
1255 					(void) rw_unlock(&sessionPoolLock);
1256 					syslog(LOG_WARNING, "libsldap: Null "
1257 					    "sinfo.server from "
1258 					    "__s_api_requestServer");
1259 					(void) __ns_ldap_freeParam(
1260 					    (void ***)&servers);
1261 					return (-1);
1262 				}
1263 			}
1264 		}
1265 
1266 		/* found an available connection */
1267 		if (MTperConn == 0)
1268 			cp->usedBit = B_TRUE;
1269 		else {
1270 			/*
1271 			 * if connection was established in a different
1272 			 * process, drop it and get a new one
1273 			 */
1274 			if (cp->pid != getpid()) {
1275 				(void) rw_unlock(&sessionPoolLock);
1276 				DropConnection(cp->connectionId,
1277 				    NS_LDAP_NEW_CONN);
1278 
1279 				goto get_conn;
1280 			}
1281 			/* allocate TSD for per thread ldap error */
1282 			rc = tsd_setup();
1283 
1284 			/* if we got TSD, this connection is shared */
1285 			if (rc != -1)
1286 				cp->shared++;
1287 			else if (cp->shared == 0) {
1288 				cp->usedBit = B_TRUE;
1289 				cp->threadID = thr_self();
1290 				(void) rw_unlock(&sessionPoolLock);
1291 				return (-1);
1292 			}
1293 		}
1294 		(void) rw_unlock(&sessionPoolLock);
1295 
1296 		*conp = cp;
1297 #ifdef DEBUG
1298 		(void) fprintf(stderr, "tid= %d: Connection found "
1299 		    "cID=%d, shared =%d\n", t, i, cp->shared);
1300 		fflush(stderr);
1301 #endif /* DEBUG */
1302 		return (i + CONID_OFFSET);
1303 	}
1304 
1305 	get_conn:
1306 
1307 	(void) rw_unlock(&sessionPoolLock);
1308 
1309 	/*
1310 	 * If multiple threads per connection not supported,
1311 	 * we are done, just return -1 to tell the caller to
1312 	 * proceed with opening a connection
1313 	 */
1314 	if (MTperConn == 0)
1315 		return (-1);
1316 
1317 	/*
1318 	 * No connection can be shared, test to see if
1319 	 * one is being opened. If trylock returns
1320 	 * EBUSY then it is, so wait until the opening
1321 	 * is done and try to see if the new connection
1322 	 * can be shared.
1323 	 */
1324 	rc = mutex_trylock(&sessionLock);
1325 	if (rc == EBUSY) {
1326 		(void) mutex_lock(&sessionLock);
1327 		(void) mutex_unlock(&sessionLock);
1328 		(void) rw_rdlock(&sessionPoolLock);
1329 #ifdef DEBUG
1330 		(void) fprintf(stderr, "tid= %d: check session "
1331 		    "pool again\n", t);
1332 		fflush(stderr);
1333 #endif /* DEBUG */
1334 		if (try < TRY_TIMES) {
1335 			try++;
1336 			goto check_again;
1337 		} else {
1338 			syslog(LOG_WARNING, "libsldap: mutex_trylock "
1339 			    "%d times. Stop.", TRY_TIMES);
1340 			(void) rw_unlock(&sessionPoolLock);
1341 			return (-1);
1342 		}
1343 	} else if (rc == 0) {
1344 		/*
1345 		 * No connection can be shared, none being opened,
1346 		 * exit with sessionLock locked to open one. The
1347 		 * mutex will be unlocked in addConnection() when
1348 		 * this thread adds the new connection to the pool
1349 		 * or in __s_api_getConnection() when it exits
1350 		 * without getting a connection.
1351 		 */
1352 		wait4session = 1;
1353 		sessionTid = thr_self();
1354 #ifdef DEBUG
1355 		(void) fprintf(stderr, "tid= %d: no connection found, "
1356 		    "none being opened, get connection ...\n", t);
1357 		fflush(stderr);
1358 #endif /* DEBUG */
1359 		return (-1);
1360 	} else {
1361 		syslog(LOG_WARNING, "libsldap: mutex_trylock unexpected "
1362 		    "error %d", rc);
1363 		return (-1);
1364 	}
1365 }
1366 
1367 /*
1368  * Free a Connection structure
1369  */
1370 static void
1371 freeConnection(Connection *con)
1372 {
1373 	if (con == NULL)
1374 		return;
1375 	if (con->serverAddr)
1376 		free(con->serverAddr);
1377 	if (con->auth)
1378 		(void) __ns_ldap_freeCred(&(con->auth));
1379 	if (con->saslMechanisms) {
1380 		__s_api_free2dArray(con->saslMechanisms);
1381 	}
1382 	if (con->controls) {
1383 		__s_api_free2dArray(con->controls);
1384 	}
1385 	free(con);
1386 }
1387 
1388 /*
1389  * Find a connection matching the passed in criteria.  If an open
1390  * connection with that criteria exists use it, otherwise open a
1391  * new connection.
1392  * Success: returns the pointer to the Connection structure
1393  * Failure: returns NULL, error code and message should be in errorp
1394  */
1395 
1396 static int
1397 makeConnection(Connection **conp, const char *serverAddr,
1398 	const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
1399 	ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
1400 	int nopasswd_acct_mgmt, int flags, char ***badsrvrs)
1401 {
1402 	Connection *con = NULL;
1403 	ConnectionID id;
1404 	char errmsg[MAXERROR];
1405 	int rc, exit_rc = NS_LDAP_SUCCESS;
1406 	ns_server_info_t sinfo;
1407 	char *hReq, *host = NULL;
1408 	LDAP *ld = NULL;
1409 	int passwd_mgmt = 0;
1410 	int totalbad = 0; /* Number of servers contacted unsuccessfully */
1411 	short	memerr = 0; /* Variable for tracking memory allocation */
1412 	char *serverAddrType = NULL, **bindHost = NULL;
1413 
1414 
1415 	if (conp == NULL || errorp == NULL || auth == NULL)
1416 		return (NS_LDAP_INVALID_PARAM);
1417 	*errorp = NULL;
1418 	*conp = NULL;
1419 	(void) memset(&sinfo, 0, sizeof (sinfo));
1420 
1421 	if ((wait4session == 0 || sessionTid != thr_self()) &&
1422 	    (id = findConnection(flags, serverAddr, auth, &con)) != -1) {
1423 		/* connection found in cache */
1424 #ifdef DEBUG
1425 		(void) fprintf(stderr, "tid= %d: connection found in "
1426 		    "cache %d\n", thr_self(), id);
1427 		fflush(stderr);
1428 #endif /* DEBUG */
1429 		*cID = id;
1430 		*conp = con;
1431 		return (NS_LDAP_SUCCESS);
1432 	}
1433 
1434 	if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
1435 		serverAddrType = NS_CACHE_ADDR_HOSTNAME;
1436 		bindHost = &sinfo.serverFQDN;
1437 	} else {
1438 		serverAddrType = NS_CACHE_ADDR_IP;
1439 		bindHost = &sinfo.server;
1440 	}
1441 
1442 	if (serverAddr) {
1443 		/*
1444 		 * We're given the server address, just use it.
1445 		 * In case of sasl/GSSAPI, serverAddr would need to be a FQDN.
1446 		 * We assume this is the case for now.
1447 		 *
1448 		 * Only the server address fields of sinfo structure are filled
1449 		 * in since these are the only relevant data that we have. Other
1450 		 * fields of this structure (controls, saslMechanisms) are
1451 		 * kept to NULL.
1452 		 */
1453 		sinfo.server = strdup(serverAddr);
1454 		if (sinfo.server == NULL)  {
1455 			return (NS_LDAP_MEMORY);
1456 		}
1457 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
1458 			sinfo.serverFQDN = strdup(serverAddr);
1459 			if (sinfo.serverFQDN == NULL) {
1460 				free(sinfo.server);
1461 				return (NS_LDAP_MEMORY);
1462 			}
1463 		}
1464 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
1465 		    fail_if_new_pwd_reqd, passwd_mgmt);
1466 		if (rc == NS_LDAP_SUCCESS || rc ==
1467 		    NS_LDAP_SUCCESS_WITH_INFO) {
1468 			exit_rc = rc;
1469 			goto create_con;
1470 		} else {
1471 			if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
1472 				(void) snprintf(errmsg, sizeof (errmsg),
1473 				    "%s %s", gettext("makeConnection: "
1474 				    "failed to open connection using "
1475 				    "sasl/GSSAPI to"), *bindHost);
1476 			} else {
1477 				(void) snprintf(errmsg, sizeof (errmsg),
1478 				    "%s %s", gettext("makeConnection: "
1479 				    "failed to open connection to"),
1480 				    *bindHost);
1481 			}
1482 			syslog(LOG_ERR, "libsldap: %s", errmsg);
1483 			__s_api_free_server_info(&sinfo);
1484 			return (rc);
1485 		}
1486 	}
1487 
1488 	/* No cached connection, create one */
1489 	for (; ; ) {
1490 		if (host == NULL)
1491 			hReq = NS_CACHE_NEW;
1492 		else
1493 			hReq = NS_CACHE_NEXT;
1494 		rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
1495 		    serverAddrType);
1496 		if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
1497 		    (host && (strcasecmp(host, sinfo.server) == 0))) {
1498 			/* Log the error */
1499 			if (*errorp) {
1500 				(void) snprintf(errmsg, sizeof (errmsg),
1501 				"%s: (%s)", gettext("makeConnection: "
1502 				"unable to make LDAP connection, "
1503 				"request for a server failed"),
1504 				    (*errorp)->message);
1505 				syslog(LOG_ERR, "libsldap: %s", errmsg);
1506 			}
1507 
1508 			__s_api_free_server_info(&sinfo);
1509 			if (host)
1510 				free(host);
1511 			return (NS_LDAP_OP_FAILED);
1512 		}
1513 		if (host)
1514 			free(host);
1515 		host = strdup(sinfo.server);
1516 		if (host == NULL) {
1517 			__s_api_free_server_info(&sinfo);
1518 			return (NS_LDAP_MEMORY);
1519 		}
1520 
1521 		/* check if server supports password management */
1522 		passwd_mgmt = __s_api_contain_passwd_control_oid(
1523 		    sinfo.controls);
1524 		/* check if server supports password less account mgmt */
1525 		if (nopasswd_acct_mgmt &&
1526 		    !__s_api_contain_account_usable_control_oid(
1527 		    sinfo.controls)) {
1528 			syslog(LOG_WARNING, "libsldap: server %s does not "
1529 			    "provide account information without password",
1530 			    host);
1531 			free(host);
1532 			__s_api_free_server_info(&sinfo);
1533 			return (NS_LDAP_OP_FAILED);
1534 		}
1535 		/* make the connection */
1536 		rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
1537 		    fail_if_new_pwd_reqd, passwd_mgmt);
1538 		/* if success, go to create connection structure */
1539 		if (rc == NS_LDAP_SUCCESS ||
1540 		    rc == NS_LDAP_SUCCESS_WITH_INFO) {
1541 			exit_rc = rc;
1542 			break;
1543 		}
1544 
1545 		/*
1546 		 * If not able to reach the server, inform the ldap
1547 		 * cache manager that the server should be removed
1548 		 * from its server list. Thus, the manager will not
1549 		 * return this server on the next get-server request
1550 		 * and will also reduce the server list refresh TTL,
1551 		 * so that it will find out sooner when the server
1552 		 * is up again.
1553 		 */
1554 		if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
1555 			if ((*errorp)->status == LDAP_CONNECT_ERROR ||
1556 			    (*errorp)->status == LDAP_SERVER_DOWN) {
1557 				/* Reset memory allocation error */
1558 				memerr = 0;
1559 				/*
1560 				 * We contacted a server that we could
1561 				 * not either authenticate to or contact.
1562 				 * If it is due to authentication, then
1563 				 * we need to try the server again. So,
1564 				 * do not remove the server yet, but
1565 				 * add it to the bad server list.
1566 				 * The caller routine will remove
1567 				 * the servers if:
1568 				 *	a). A good server is found or
1569 				 *	b). All the possible methods
1570 				 *	    are tried without finding
1571 				 *	    a good server
1572 				 */
1573 				if (*badsrvrs == NULL) {
1574 					if (!(*badsrvrs = (char **)malloc
1575 					    (sizeof (char *) * NUMTOMALLOC))) {
1576 						memerr = 1;
1577 					}
1578 				/* Allocate memory in chunks of NUMTOMALLOC */
1579 				} else if ((totalbad % NUMTOMALLOC) ==
1580 				    NUMTOMALLOC - 1) {
1581 					char **tmpptr;
1582 					if (!(tmpptr = (char **)realloc(
1583 					    *badsrvrs,
1584 					    (sizeof (char *) * NUMTOMALLOC *
1585 					    ((totalbad/NUMTOMALLOC) + 2))))) {
1586 						memerr = 1;
1587 					} else {
1588 						*badsrvrs = tmpptr;
1589 					}
1590 				}
1591 				/*
1592 				 * Store host only if there were no unsuccessful
1593 				 * memory allocations above
1594 				 */
1595 				if (!memerr &&
1596 				    !((*badsrvrs)[totalbad++] = strdup(host))) {
1597 					memerr = 1;
1598 					totalbad--;
1599 				}
1600 				(*badsrvrs)[totalbad] = NULL;
1601 			}
1602 		}
1603 
1604 		/* else, cleanup and go for the next server */
1605 		__s_api_free_server_info(&sinfo);
1606 
1607 		/* Return if we had memory allocation errors */
1608 		if (memerr)
1609 			return (NS_LDAP_MEMORY);
1610 		if (*errorp) {
1611 			/*
1612 			 * If openConnection() failed due to
1613 			 * password policy, or invalid credential,
1614 			 * keep *errorp and exit
1615 			 */
1616 			if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
1617 			    (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
1618 				free(host);
1619 				return (rc);
1620 			} else {
1621 				(void) __ns_ldap_freeError(errorp);
1622 				*errorp = NULL;
1623 			}
1624 		}
1625 	}
1626 
1627 create_con:
1628 	/* we have created ld, setup con structure */
1629 	if (host)
1630 		free(host);
1631 	if ((con = calloc(1, sizeof (Connection))) == NULL) {
1632 		__s_api_free_server_info(&sinfo);
1633 		/*
1634 		 * If password control attached in **errorp,
1635 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1636 		 * free the error structure
1637 		 */
1638 		if (*errorp) {
1639 			(void) __ns_ldap_freeError(errorp);
1640 			*errorp = NULL;
1641 		}
1642 		(void) ldap_unbind(ld);
1643 		return (NS_LDAP_MEMORY);
1644 	}
1645 
1646 	con->serverAddr = sinfo.server; /* Store original format */
1647 	if (sinfo.serverFQDN != NULL) {
1648 		free(sinfo.serverFQDN);
1649 		sinfo.serverFQDN = NULL;
1650 	}
1651 	con->saslMechanisms = sinfo.saslMechanisms;
1652 	con->controls = sinfo.controls;
1653 
1654 	con->auth = __ns_ldap_dupAuth(auth);
1655 	if (con->auth == NULL) {
1656 		(void) ldap_unbind(ld);
1657 		freeConnection(con);
1658 		/*
1659 		 * If password control attached in **errorp,
1660 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1661 		 * free the error structure
1662 		 */
1663 		if (*errorp) {
1664 			(void) __ns_ldap_freeError(errorp);
1665 			*errorp = NULL;
1666 		}
1667 		return (NS_LDAP_MEMORY);
1668 	}
1669 
1670 	con->threadID = thr_self();
1671 	con->pid = getpid();
1672 
1673 	con->ld = ld;
1674 	if ((id = addConnection(con)) == -1) {
1675 		(void) ldap_unbind(ld);
1676 		freeConnection(con);
1677 		/*
1678 		 * If password control attached in **errorp,
1679 		 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1680 		 * free the error structure
1681 		 */
1682 		if (*errorp) {
1683 			(void) __ns_ldap_freeError(errorp);
1684 			*errorp = NULL;
1685 		}
1686 		return (NS_LDAP_MEMORY);
1687 	}
1688 #ifdef DEBUG
1689 	(void) fprintf(stderr, "tid= %d: connection added into "
1690 	    "cache %d\n", thr_self(), id);
1691 	fflush(stderr);
1692 #endif /* DEBUG */
1693 	*cID = id;
1694 	*conp = con;
1695 	return (exit_rc);
1696 }
1697 
1698 /*
1699  * Return the specified connection to the pool.  If necessary
1700  * delete the connection.
1701  */
1702 
1703 static void
1704 _DropConnection(ConnectionID cID, int flag, int fini)
1705 {
1706 	Connection *cp;
1707 	int id;
1708 	int use_lock = !fini;
1709 	struct timeval	zerotime;
1710 	LDAPMessage	*res;
1711 
1712 	zerotime.tv_sec = zerotime.tv_usec = 0L;
1713 
1714 #ifdef DEBUG
1715 	thread_t t = thr_self();
1716 #endif /* DEBUG */
1717 
1718 	id = cID - CONID_OFFSET;
1719 	if (id < 0 || id >= sessionPoolSize)
1720 		return;
1721 #ifdef DEBUG
1722 	(void) fprintf(stderr, "tid= %d: "
1723 	    "Dropping connection cID=%d flag=0x%x, fini = %d\n",
1724 	    t, cID, flag, fini);
1725 	fflush(stderr);
1726 #endif /* DEBUG */
1727 	if (use_lock)
1728 		(void) rw_wrlock(&sessionPoolLock);
1729 
1730 	cp = sessionPool[id];
1731 	/* sanity check before removing */
1732 	if (!cp || (!fini && !cp->shared && !cp->usedBit)) {
1733 #ifdef DEBUG
1734 		if (cp == NULL)
1735 			(void) fprintf(stderr, "tid= %d: no "
1736 			    "need to remove (fini = %d, cp = %p)\n", t,
1737 			    fini, cp);
1738 		else
1739 			(void) fprintf(stderr, "tid= %d: no "
1740 			    "need to remove (fini = %d, cp = %p, shared = %d)"
1741 			    "\n", t, fini, cp, cp->shared);
1742 		fflush(stderr);
1743 #endif /* DEBUG */
1744 		if (use_lock)
1745 			(void) rw_unlock(&sessionPoolLock);
1746 		return;
1747 	}
1748 
1749 	if (!fini &&
1750 	    ((flag & NS_LDAP_NEW_CONN) == 0) && !cp->notAvail &&
1751 	    ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc() ||
1752 	    __s_api_peruser_proc())) {
1753 #ifdef DEBUG
1754 		(void) fprintf(stderr, "tid= %d: keep alive (fini = %d "
1755 		    "shared = %d)\n", t, fini, cp->shared);
1756 #endif /* DEBUG */
1757 		/* release Connection (keep alive) */
1758 		if (cp->shared)
1759 			cp->shared--;
1760 		cp->usedBit = B_FALSE;
1761 		cp->threadID = 0;	/* unmark the threadID */
1762 		/*
1763 		 * Do sanity cleanup of remaining results if connection is not
1764 		 * shared by any requests.
1765 		 */
1766 		if (cp->shared <= 0) {
1767 			while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL,
1768 			    &zerotime, &res) > 0) {
1769 				if (res != NULL)
1770 					(void) ldap_msgfree(res);
1771 			}
1772 		}
1773 		if (use_lock)
1774 			(void) rw_unlock(&sessionPoolLock);
1775 	} else {
1776 		/* delete Connection (disconnect) */
1777 		if (cp->shared > 0) {
1778 #ifdef DEBUG
1779 		(void) fprintf(stderr, "tid= %d: Connection no "
1780 		    "longer available (fini = %d, shared = %d)\n",
1781 		    t, fini, cp->shared);
1782 		fflush(stderr);
1783 #endif /* DEBUG */
1784 			cp->shared--;
1785 			/*
1786 			 * Mark this connection not available and decrement
1787 			 * sharedConnNumber. There could be multiple threads
1788 			 * sharing this connection so decrement
1789 			 * sharedConnNumber only once per connection.
1790 			 */
1791 			if (cp->notAvail == 0) {
1792 				cp->notAvail = 1;
1793 				(void) mutex_lock(&sharedConnNumberLock);
1794 				sharedConnNumber--;
1795 				(void) mutex_unlock(&sharedConnNumberLock);
1796 			}
1797 		}
1798 
1799 		if (cp->shared <= 0) {
1800 #ifdef DEBUG
1801 			(void) fprintf(stderr, "tid= %d: unbind "
1802 			    "(fini = %d, shared = %d)\n",
1803 			    t, fini, cp->shared);
1804 			fflush(stderr);
1805 #endif /* DEBUG */
1806 			sessionPool[id] = NULL;
1807 			(void) ldap_unbind(cp->ld);
1808 			freeConnection(cp);
1809 		}
1810 
1811 		if (use_lock)
1812 			(void) rw_unlock(&sessionPoolLock);
1813 	}
1814 }
1815 
1816 void
1817 DropConnection(ConnectionID cID, int flag)
1818 {
1819 	_DropConnection(cID, flag, 0);
1820 }
1821 
1822 /*
1823  * This routine is called after a bind operation is
1824  * done in openConnection() to process the password
1825  * management information, if any.
1826  *
1827  * Input:
1828  *   bind_type: "simple" or "sasl/DIGEST-MD5"
1829  *   ldaprc   : ldap rc from the ldap bind operation
1830  *   controls : controls returned by the server
1831  *   errmsg   : error message from the server
1832  *   fail_if_new_pwd_reqd:
1833  *              flag indicating if connection should be open
1834  *              when password needs to change immediately
1835  *   passwd_mgmt:
1836  *              flag indicating if server supports password
1837  *              policy/management
1838  *
1839  * Output     : ns_ldap_error structure, which may contain
1840  *              password status and number of seconds until
1841  *              expired
1842  *
1843  * return rc:
1844  * NS_LDAP_EXTERNAL: error, connection should not open
1845  * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
1846  * NS_LDAP_SUCCESS: OK to open connection
1847  *
1848  */
1849 
1850 static int
1851 process_pwd_mgmt(char *bind_type, int ldaprc,
1852 		LDAPControl **controls,
1853 		char *errmsg, ns_ldap_error_t **errorp,
1854 		int fail_if_new_pwd_reqd,
1855 		int passwd_mgmt)
1856 {
1857 	char		errstr[MAXERROR];
1858 	LDAPControl	**ctrl = NULL;
1859 	int		exit_rc;
1860 	ns_ldap_passwd_status_t	pwd_status = NS_PASSWD_GOOD;
1861 	int		sec_until_exp = 0;
1862 
1863 	/*
1864 	 * errmsg may be an empty string,
1865 	 * even if ldaprc is LDAP_SUCCESS,
1866 	 * free the empty string if that's the case
1867 	 */
1868 	if (errmsg &&
1869 	    (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
1870 		ldap_memfree(errmsg);
1871 		errmsg = NULL;
1872 	}
1873 
1874 	if (ldaprc != LDAP_SUCCESS) {
1875 		/*
1876 		 * try to map ldap rc and error message to
1877 		 * a password status
1878 		 */
1879 		if (errmsg) {
1880 			if (passwd_mgmt)
1881 				pwd_status =
1882 				    __s_api_set_passwd_status(
1883 				    ldaprc, errmsg);
1884 			ldap_memfree(errmsg);
1885 		}
1886 
1887 		(void) snprintf(errstr, sizeof (errstr),
1888 		    gettext("openConnection: "
1889 		    "%s bind failed "
1890 		    "- %s"), bind_type, ldap_err2string(ldaprc));
1891 
1892 		if (pwd_status != NS_PASSWD_GOOD) {
1893 			MKERROR_PWD_MGMT(*errorp,
1894 			    ldaprc, strdup(errstr),
1895 			    pwd_status, 0, NULL);
1896 		} else {
1897 			MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
1898 			    NULL);
1899 		}
1900 		if (controls)
1901 			ldap_controls_free(controls);
1902 
1903 		return (NS_LDAP_INTERNAL);
1904 	}
1905 
1906 	/*
1907 	 * ldaprc is LDAP_SUCCESS,
1908 	 * process the password management controls, if any
1909 	 */
1910 	exit_rc = NS_LDAP_SUCCESS;
1911 	if (controls && passwd_mgmt) {
1912 		/*
1913 		 * The control with the OID
1914 		 * 2.16.840.1.113730.3.4.4 (or
1915 		 * LDAP_CONTROL_PWEXPIRED, as defined
1916 		 * in the ldap.h header file) is the
1917 		 * expired password control.
1918 		 *
1919 		 * This control is used if the server
1920 		 * is configured to require users to
1921 		 * change their passwords when first
1922 		 * logging in and whenever the
1923 		 * passwords are reset.
1924 		 *
1925 		 * If the user is logging in for the
1926 		 * first time or if the user's
1927 		 * password has been reset, the
1928 		 * server sends this control to
1929 		 * indicate that the client needs to
1930 		 * change the password immediately.
1931 		 *
1932 		 * At this point, the only operation
1933 		 * that the client can perform is to
1934 		 * change the user's password. If the
1935 		 * client requests any other LDAP
1936 		 * operation, the server sends back
1937 		 * an LDAP_UNWILLING_TO_PERFORM
1938 		 * result code with an expired
1939 		 * password control.
1940 		 *
1941 		 * The control with the OID
1942 		 * 2.16.840.1.113730.3.4.5 (or
1943 		 * LDAP_CONTROL_PWEXPIRING, as
1944 		 * defined in the ldap.h header file)
1945 		 * is the password expiration warning
1946 		 * control.
1947 		 *
1948 		 * This control is used if the server
1949 		 * is configured to expire user
1950 		 * passwords after a certain amount
1951 		 * of time.
1952 		 *
1953 		 * The server sends this control back
1954 		 * to the client if the client binds
1955 		 * using a password that will soon
1956 		 * expire.  The ldctl_value field of
1957 		 * the LDAPControl structure
1958 		 * specifies the number of seconds
1959 		 * before the password will expire.
1960 		 */
1961 		for (ctrl = controls; *ctrl; ctrl++) {
1962 
1963 			if (strcmp((*ctrl)->ldctl_oid,
1964 			    LDAP_CONTROL_PWEXPIRED) == 0) {
1965 				/*
1966 				 * if the caller wants this bind
1967 				 * to fail, set up the error info.
1968 				 * If call to this function is
1969 				 * for searching the LDAP directory,
1970 				 * e.g., __ns_ldap_list(),
1971 				 * there's really no sense to
1972 				 * let a connection open and
1973 				 * then fail immediately afterward
1974 				 * on the LDAP search operation with
1975 				 * the LDAP_UNWILLING_TO_PERFORM rc
1976 				 */
1977 				pwd_status =
1978 				    NS_PASSWD_CHANGE_NEEDED;
1979 				if (fail_if_new_pwd_reqd) {
1980 					(void) snprintf(errstr,
1981 					    sizeof (errstr),
1982 					    gettext(
1983 					    "openConnection: "
1984 					    "%s bind "
1985 					    "failed "
1986 					    "- password "
1987 					    "expired. It "
1988 					    " needs to change "
1989 					    "immediately!"),
1990 					    bind_type);
1991 					MKERROR_PWD_MGMT(*errorp,
1992 					    LDAP_SUCCESS,
1993 					    strdup(errstr),
1994 					    pwd_status,
1995 					    0,
1996 					    NULL);
1997 					exit_rc = NS_LDAP_INTERNAL;
1998 				} else {
1999 					MKERROR_PWD_MGMT(*errorp,
2000 					    LDAP_SUCCESS,
2001 					    NULL,
2002 					    pwd_status,
2003 					    0,
2004 					    NULL);
2005 					exit_rc =
2006 					    NS_LDAP_SUCCESS_WITH_INFO;
2007 				}
2008 				break;
2009 			} else if (strcmp((*ctrl)->ldctl_oid,
2010 			    LDAP_CONTROL_PWEXPIRING) == 0) {
2011 				pwd_status =
2012 				    NS_PASSWD_ABOUT_TO_EXPIRE;
2013 				if ((*ctrl)->
2014 				    ldctl_value.bv_len > 0 &&
2015 				    (*ctrl)->
2016 				    ldctl_value.bv_val)
2017 					sec_until_exp =
2018 					    atoi((*ctrl)->
2019 					    ldctl_value.bv_val);
2020 				MKERROR_PWD_MGMT(*errorp,
2021 				    LDAP_SUCCESS,
2022 				    NULL,
2023 				    pwd_status,
2024 				    sec_until_exp,
2025 				    NULL);
2026 				exit_rc =
2027 				    NS_LDAP_SUCCESS_WITH_INFO;
2028 				break;
2029 			}
2030 		}
2031 	}
2032 
2033 	if (controls)
2034 		ldap_controls_free(controls);
2035 
2036 	return (exit_rc);
2037 }
2038 
2039 static int
2040 ldap_in_hosts_switch()
2041 {
2042 	enum __nsw_parse_err		pserr;
2043 	struct __nsw_switchconfig	*conf;
2044 	struct __nsw_lookup		*lkp;
2045 	const char			*name;
2046 	int				found = 0;
2047 
2048 	conf = __nsw_getconfig("hosts", &pserr);
2049 	if (conf == NULL) {
2050 		return (-1);
2051 	}
2052 
2053 	/* check for skip and count other backends */
2054 	for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
2055 		name = lkp->service_name;
2056 		if (strcmp(name, "ldap") == 0) {
2057 			found = 1;
2058 			break;
2059 		}
2060 	}
2061 	__nsw_freeconfig(conf);
2062 	return (found);
2063 }
2064 
2065 static int
2066 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
2067 	int timeoutSec, ns_ldap_error_t **errorp,
2068 	int fail_if_new_pwd_reqd, int passwd_mgmt)
2069 {
2070 	LDAP		*ld = NULL;
2071 	char		*binddn, *passwd;
2072 	char		*digest_md5_name;
2073 	const char	*s;
2074 	int		ldapVersion = LDAP_VERSION3;
2075 	int		derefOption = LDAP_DEREF_ALWAYS;
2076 	int		zero = 0;
2077 	int		rc;
2078 	char		errstr[MAXERROR];
2079 	int		errnum = 0;
2080 	LDAPMessage	*resultMsg;
2081 	int		msgId;
2082 	int		useSSL = 0, port = 0;
2083 	struct timeval	tv;
2084 	AuthType_t	bindType;
2085 	int		timeoutMilliSec = timeoutSec * 1000;
2086 	struct berval	cred;
2087 	char		*sslServerAddr;
2088 	char		*s1;
2089 	char		*errmsg, *end = NULL;
2090 	LDAPControl	**controls;
2091 	int		pwd_rc, min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
2092 	ns_sasl_cb_param_t	sasl_param;
2093 
2094 	*errorp = NULL;
2095 	*ldp = NULL;
2096 
2097 	switch (auth->auth.type) {
2098 		case NS_LDAP_AUTH_NONE:
2099 		case NS_LDAP_AUTH_SIMPLE:
2100 		case NS_LDAP_AUTH_SASL:
2101 			bindType = auth->auth.type;
2102 			break;
2103 		case NS_LDAP_AUTH_TLS:
2104 			useSSL = 1;
2105 			switch (auth->auth.tlstype) {
2106 				case NS_LDAP_TLS_NONE:
2107 					bindType = NS_LDAP_AUTH_NONE;
2108 					break;
2109 				case NS_LDAP_TLS_SIMPLE:
2110 					bindType = NS_LDAP_AUTH_SIMPLE;
2111 					break;
2112 				case NS_LDAP_TLS_SASL:
2113 					bindType = NS_LDAP_AUTH_SASL;
2114 					break;
2115 				default:
2116 					(void) sprintf(errstr,
2117 					gettext("openConnection: unsupported "
2118 					    "TLS authentication method "
2119 					    "(%d)"), auth->auth.tlstype);
2120 					MKERROR(LOG_WARNING, *errorp,
2121 					    LDAP_AUTH_METHOD_NOT_SUPPORTED,
2122 					    strdup(errstr), NULL);
2123 					return (NS_LDAP_INTERNAL);
2124 			}
2125 			break;
2126 		default:
2127 			(void) sprintf(errstr,
2128 			    gettext("openConnection: unsupported "
2129 			    "authentication method (%d)"), auth->auth.type);
2130 			MKERROR(LOG_WARNING, *errorp,
2131 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2132 			    NULL);
2133 			return (NS_LDAP_INTERNAL);
2134 	}
2135 
2136 	if (useSSL) {
2137 		const char	*hostcertpath;
2138 		char		*alloc_hcp = NULL;
2139 #ifdef DEBUG
2140 		(void) fprintf(stderr, "tid= %d: +++TLS transport\n",
2141 		    thr_self());
2142 #endif /* DEBUG */
2143 
2144 		if (prldap_set_session_option(NULL, NULL,
2145 		    PRLDAP_OPT_IO_MAX_TIMEOUT,
2146 		    timeoutMilliSec) != LDAP_SUCCESS) {
2147 			(void) snprintf(errstr, sizeof (errstr),
2148 			    gettext("openConnection: failed to initialize "
2149 			    "TLS security"));
2150 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2151 			    strdup(errstr), NULL);
2152 			return (NS_LDAP_INTERNAL);
2153 		}
2154 
2155 		hostcertpath = auth->hostcertpath;
2156 		if (hostcertpath == NULL) {
2157 			alloc_hcp = __s_get_hostcertpath();
2158 			hostcertpath = alloc_hcp;
2159 		}
2160 
2161 		if (hostcertpath == NULL)
2162 			return (NS_LDAP_MEMORY);
2163 
2164 		if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
2165 			if (alloc_hcp)
2166 				free(alloc_hcp);
2167 			(void) snprintf(errstr, sizeof (errstr),
2168 			    gettext("openConnection: failed to initialize "
2169 			    "TLS security (%s)"),
2170 			    ldapssl_err2string(rc));
2171 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2172 			    strdup(errstr), NULL);
2173 			return (NS_LDAP_INTERNAL);
2174 		}
2175 		if (alloc_hcp)
2176 			free(alloc_hcp);
2177 
2178 		/* determine if the host name contains a port number */
2179 		s = strchr(serverAddr, ']');	/* skip over ipv6 addr */
2180 		if (s == NULL)
2181 			s = serverAddr;
2182 		s = strchr(s, ':');
2183 		if (s != NULL) {
2184 			/*
2185 			 * If we do get a port number, we will try stripping
2186 			 * it. At present, referrals will always have a
2187 			 * port number.
2188 			 */
2189 			sslServerAddr = strdup(serverAddr);
2190 			if (sslServerAddr == NULL)
2191 				return (NS_LDAP_MEMORY);
2192 			s1 = strrchr(sslServerAddr, ':');
2193 			if (s1 != NULL)
2194 				*s1 = '\0';
2195 			(void) snprintf(errstr, sizeof (errstr),
2196 			    gettext("openConnection: cannot use tls with %s. "
2197 			    "Trying %s"),
2198 			    serverAddr, sslServerAddr);
2199 			syslog(LOG_ERR, "libsldap: %s", errstr);
2200 		} else
2201 			sslServerAddr = (char *)serverAddr;
2202 
2203 		ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
2204 
2205 		if (sslServerAddr != serverAddr)
2206 			free(sslServerAddr);
2207 
2208 		if (ld == NULL ||
2209 		    ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
2210 			(void) snprintf(errstr, sizeof (errstr),
2211 			    gettext("openConnection: failed to connect "
2212 			    "using TLS (%s)"), strerror(errno));
2213 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2214 			    strdup(errstr), NULL);
2215 			return (NS_LDAP_INTERNAL);
2216 		}
2217 	} else {
2218 #ifdef DEBUG
2219 		(void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
2220 		    thr_self());
2221 #endif /* DEBUG */
2222 		port = LDAP_PORT;
2223 		if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI &&
2224 		    (end = strchr(serverAddr, ':')) != NULL) {
2225 			/*
2226 			 * The IP is converted to hostname so it's a
2227 			 * hostname:port up to this point.
2228 			 *
2229 			 * libldap passes hostname:port to the sasl layer.
2230 			 * The ldap service principal is constructed as
2231 			 * ldap/hostname:port@REALM. Kerberos authentication
2232 			 * will fail. So it needs to be parsed to construct
2233 			 * a valid principal ldap/hostname@REALM.
2234 			 *
2235 			 * For useSSL case above, it already parses port so
2236 			 * no need to parse serverAddr
2237 			 */
2238 			*end = '\0';
2239 			port = atoi(end + 1);
2240 		}
2241 
2242 		/* Warning message IF cannot connect to host(s) */
2243 		if ((ld = ldap_init((char *)serverAddr, port)) == NULL) {
2244 			char *p = strerror(errno);
2245 			MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2246 			    strdup(p), NULL);
2247 			if (end)
2248 				*end = ':';
2249 			return (NS_LDAP_INTERNAL);
2250 		} else {
2251 			if (end)
2252 				*end = ':';
2253 			/* check and avoid gethostname recursion */
2254 			if (ldap_in_hosts_switch() > 0 &&
2255 			    ! __s_api_isipv4((char *)serverAddr) &&
2256 			    ! __s_api_isipv6((char *)serverAddr)) {
2257 				/* host: ldap - found, attempt to recover */
2258 				if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
2259 				    "ldap") != 0) {
2260 					(void) snprintf(errstr, sizeof (errstr),
2261 					    gettext("openConnection: "
2262 					    "unrecoverable gethostname "
2263 					    "recursion detected "
2264 					    "in /etc/nsswitch.conf"));
2265 					MKERROR(LOG_WARNING, *errorp,
2266 					    LDAP_CONNECT_ERROR,
2267 					    strdup(errstr), NULL);
2268 					(void) ldap_unbind(ld);
2269 					return (NS_LDAP_INTERNAL);
2270 				}
2271 			}
2272 		}
2273 	}
2274 
2275 	ns_setup_mt_conn_and_tsd(ld);
2276 	(void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
2277 	(void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
2278 	/*
2279 	 * set LDAP_OPT_REFERRALS to OFF.
2280 	 * This library will handle the referral itself
2281 	 * based on API flags or configuration file
2282 	 * specification. If this option is not set
2283 	 * to OFF, libldap will never pass the
2284 	 * referral info up to this library
2285 	 */
2286 	(void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
2287 	(void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
2288 	(void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
2289 	/* setup TCP/IP connect timeout */
2290 	(void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
2291 	    &timeoutMilliSec);
2292 	/* retry if LDAP I/O was interrupted */
2293 	(void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
2294 
2295 	switch (bindType) {
2296 	case NS_LDAP_AUTH_NONE:
2297 #ifdef DEBUG
2298 		(void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
2299 		    thr_self());
2300 #endif /* DEBUG */
2301 		break;
2302 	case NS_LDAP_AUTH_SIMPLE:
2303 		binddn = auth->cred.unix_cred.userID;
2304 		passwd = auth->cred.unix_cred.passwd;
2305 		if (passwd == NULL || *passwd == '\0' ||
2306 		    binddn == NULL || *binddn == '\0') {
2307 			(void) sprintf(errstr, gettext("openConnection: "
2308 			    "missing credentials for Simple bind"));
2309 			MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
2310 			    strdup(errstr), NULL);
2311 			(void) ldap_unbind(ld);
2312 			return (NS_LDAP_INTERNAL);
2313 		}
2314 
2315 #ifdef DEBUG
2316 		(void) fprintf(stderr, "tid= %d: +++Simple bind\n",
2317 		    thr_self());
2318 #endif /* DEBUG */
2319 		msgId = ldap_simple_bind(ld, binddn, passwd);
2320 
2321 		if (msgId == -1) {
2322 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
2323 			    (void *)&errnum);
2324 			(void) snprintf(errstr, sizeof (errstr),
2325 			    gettext("openConnection: simple bind failed "
2326 			    "- %s"), ldap_err2string(errnum));
2327 			(void) ldap_unbind(ld);
2328 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
2329 			    NULL);
2330 			return (NS_LDAP_INTERNAL);
2331 		}
2332 
2333 		tv.tv_sec = timeoutSec;
2334 		tv.tv_usec = 0;
2335 		rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
2336 
2337 		if ((rc == -1) || (rc == 0)) {
2338 			(void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
2339 			    (void *)&errnum);
2340 			(void) snprintf(errstr, sizeof (errstr),
2341 			    gettext("openConnection: simple bind failed "
2342 			    "- %s"), ldap_err2string(errnum));
2343 			(void) ldap_msgfree(resultMsg);
2344 			(void) ldap_unbind(ld);
2345 			MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
2346 			    NULL);
2347 			return (NS_LDAP_INTERNAL);
2348 		}
2349 
2350 		/*
2351 		 * get ldaprc, controls, and error msg
2352 		 */
2353 		rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2354 		    &errmsg, NULL, &controls, 1);
2355 
2356 		if (rc != LDAP_SUCCESS) {
2357 			(void) snprintf(errstr, sizeof (errstr),
2358 			    gettext("openConnection: simple bind failed "
2359 			    "- unable to parse result"));
2360 			(void) ldap_unbind(ld);
2361 			MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2362 			    strdup(errstr), NULL);
2363 			return (NS_LDAP_INTERNAL);
2364 		}
2365 
2366 		/* process the password management info, if any */
2367 		pwd_rc = process_pwd_mgmt("simple",
2368 		    errnum, controls, errmsg,
2369 		    errorp,
2370 		    fail_if_new_pwd_reqd,
2371 		    passwd_mgmt);
2372 
2373 		if (pwd_rc == NS_LDAP_INTERNAL) {
2374 			(void) ldap_unbind(ld);
2375 			return (pwd_rc);
2376 		}
2377 
2378 		if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
2379 			*ldp = ld;
2380 			return (pwd_rc);
2381 		}
2382 
2383 		break;
2384 	case NS_LDAP_AUTH_SASL:
2385 		if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
2386 		    auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2387 			(void) sprintf(errstr,
2388 			    gettext("openConnection: SASL options are "
2389 			    "not supported (%d) for non-GSSAPI sasl bind"),
2390 			    auth->auth.saslopt);
2391 			MKERROR(LOG_WARNING, *errorp,
2392 			    LDAP_AUTH_METHOD_NOT_SUPPORTED,
2393 			    strdup(errstr), NULL);
2394 			(void) ldap_unbind(ld);
2395 			return (NS_LDAP_INTERNAL);
2396 		}
2397 		if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2398 			binddn = auth->cred.unix_cred.userID;
2399 			passwd = auth->cred.unix_cred.passwd;
2400 			if (passwd == NULL || *passwd == '\0' ||
2401 			    binddn == NULL || *binddn == '\0') {
2402 				(void) sprintf(errstr,
2403 				    gettext("openConnection: missing "
2404 				    "credentials for SASL bind"));
2405 				MKERROR(LOG_WARNING, *errorp,
2406 				    LDAP_INVALID_CREDENTIALS,
2407 				    strdup(errstr), NULL);
2408 				(void) ldap_unbind(ld);
2409 				return (NS_LDAP_INTERNAL);
2410 			}
2411 			cred.bv_val = passwd;
2412 			cred.bv_len = strlen(passwd);
2413 		}
2414 
2415 		switch (auth->auth.saslmech) {
2416 		case NS_LDAP_SASL_CRAM_MD5:
2417 			/*
2418 			 * NOTE: if iDS changes to support cram_md5,
2419 			 * please add password management code here.
2420 			 * Since ldap_sasl_cram_md5_bind_s does not
2421 			 * return anything that could be used to
2422 			 * extract the ldap rc/errmsg/control to
2423 			 * determine if bind failed due to password
2424 			 * policy, a new cram_md5_bind API will need
2425 			 * to be introduced. See
2426 			 * ldap_x_sasl_digest_md5_bind() and case
2427 			 * NS_LDAP_SASL_DIGEST_MD5 below for details.
2428 			 */
2429 			if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
2430 			    &cred, NULL, NULL)) != LDAP_SUCCESS) {
2431 				(void) ldap_get_option(ld,
2432 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2433 				(void) snprintf(errstr, sizeof (errstr),
2434 				    gettext("openConnection: "
2435 				    "sasl/CRAM-MD5 bind failed - %s"),
2436 				    ldap_err2string(errnum));
2437 				MKERROR(LOG_WARNING, *errorp, errnum,
2438 				    strdup(errstr), NULL);
2439 				(void) ldap_unbind(ld);
2440 				return (NS_LDAP_INTERNAL);
2441 			}
2442 			break;
2443 		case NS_LDAP_SASL_DIGEST_MD5:
2444 			digest_md5_name = malloc(strlen(binddn) + 5);
2445 			/* 5 = strlen("dn: ") + 1 */
2446 			if (digest_md5_name == NULL) {
2447 				(void) ldap_unbind(ld);
2448 				return (NS_LDAP_MEMORY);
2449 			}
2450 			(void) strcpy(digest_md5_name, "dn: ");
2451 			(void) strcat(digest_md5_name, binddn);
2452 
2453 			tv.tv_sec = timeoutSec;
2454 			tv.tv_usec = 0;
2455 			rc = ldap_x_sasl_digest_md5_bind(ld,
2456 			    digest_md5_name, &cred, NULL, NULL,
2457 			    &tv, &resultMsg);
2458 
2459 			if (resultMsg == NULL) {
2460 				free(digest_md5_name);
2461 				(void) ldap_get_option(ld,
2462 				    LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2463 				(void) snprintf(errstr, sizeof (errstr),
2464 				    gettext("openConnection: "
2465 				    "DIGEST-MD5 bind failed - %s"),
2466 				    ldap_err2string(errnum));
2467 				(void) ldap_unbind(ld);
2468 				MKERROR(LOG_WARNING, *errorp, errnum,
2469 				    strdup(errstr), NULL);
2470 				return (NS_LDAP_INTERNAL);
2471 			}
2472 
2473 			/*
2474 			 * get ldaprc, controls, and error msg
2475 			 */
2476 			rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2477 			    &errmsg, NULL, &controls, 1);
2478 
2479 			if (rc != LDAP_SUCCESS) {
2480 				free(digest_md5_name);
2481 				(void) snprintf(errstr, sizeof (errstr),
2482 				    gettext("openConnection: "
2483 				    "DIGEST-MD5 bind failed "
2484 				    "- unable to parse result"));
2485 				(void) ldap_unbind(ld);
2486 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2487 				    strdup(errstr), NULL);
2488 				return (NS_LDAP_INTERNAL);
2489 			}
2490 
2491 			/* process the password management info, if any */
2492 			pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
2493 			    errnum, controls, errmsg,
2494 			    errorp,
2495 			    fail_if_new_pwd_reqd,
2496 			    passwd_mgmt);
2497 
2498 			if (pwd_rc == NS_LDAP_INTERNAL) {
2499 				free(digest_md5_name);
2500 				(void) ldap_unbind(ld);
2501 				return (pwd_rc);
2502 			}
2503 
2504 			if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
2505 				*ldp = ld;
2506 				return (pwd_rc);
2507 			}
2508 
2509 			free(digest_md5_name);
2510 			break;
2511 		case NS_LDAP_SASL_GSSAPI:
2512 			if (sasl_gssapi_inited == 0) {
2513 				rc = __s_api_sasl_gssapi_init();
2514 				if (rc != NS_LDAP_SUCCESS) {
2515 					(void) snprintf(errstr, sizeof (errstr),
2516 					    gettext("openConnection: "
2517 					    "GSSAPI initialization "
2518 					    "failed"));
2519 					(void) ldap_unbind(ld);
2520 					MKERROR(LOG_WARNING, *errorp, rc,
2521 					    strdup(errstr), NULL);
2522 					return (rc);
2523 				}
2524 			}
2525 			(void) memset(&sasl_param, 0,
2526 			    sizeof (ns_sasl_cb_param_t));
2527 			sasl_param.authid = NULL;
2528 			sasl_param.authzid = "";
2529 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
2530 			    (void *)&min_ssf);
2531 			(void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
2532 			    (void *)&max_ssf);
2533 
2534 			rc = ldap_sasl_interactive_bind_s(
2535 			    ld, NULL, "GSSAPI",
2536 			    NULL, NULL, LDAP_SASL_INTERACTIVE,
2537 			    __s_api_sasl_bind_callback,
2538 			    &sasl_param);
2539 
2540 			if (rc != LDAP_SUCCESS) {
2541 				(void) snprintf(errstr, sizeof (errstr),
2542 				    gettext("openConnection: "
2543 				    "GSSAPI bind failed "
2544 				    "- %d %s"), rc, ldap_err2string(rc));
2545 				(void) ldap_unbind(ld);
2546 				MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2547 				    strdup(errstr), NULL);
2548 				return (NS_LDAP_INTERNAL);
2549 			}
2550 
2551 			break;
2552 		default:
2553 			(void) ldap_unbind(ld);
2554 			(void) sprintf(errstr,
2555 			    gettext("openConnection: unsupported SASL "
2556 			    "mechanism (%d)"), auth->auth.saslmech);
2557 			MKERROR(LOG_WARNING, *errorp,
2558 			    LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2559 			    NULL);
2560 			return (NS_LDAP_INTERNAL);
2561 		}
2562 	}
2563 
2564 	*ldp = ld;
2565 	return (NS_LDAP_SUCCESS);
2566 }
2567 
2568 /*
2569  * FUNCTION:	__s_api_getDefaultAuth
2570  *
2571  *	Constructs a credential for authentication using the config module.
2572  *
2573  * RETURN VALUES:
2574  *
2575  * NS_LDAP_SUCCESS	If successful
2576  * NS_LDAP_CONFIG	If there are any config errors.
2577  * NS_LDAP_MEMORY	Memory errors.
2578  * NS_LDAP_OP_FAILED	If there are no more authentication methods so can
2579  *			not build a new authp.
2580  * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
2581  *			necessary fields of a cred for a given auth method
2582  *			are not provided.
2583  * INPUT:
2584  *
2585  * cLevel	Currently requested credential level to be tried
2586  *
2587  * aMethod	Currently requested authentication method to be tried
2588  *
2589  * OUTPUT:
2590  *
2591  * authp		authentication method to use.
2592  */
2593 static int
2594 __s_api_getDefaultAuth(
2595 	int	*cLevel,
2596 	ns_auth_t *aMethod,
2597 	ns_cred_t **authp)
2598 {
2599 	void		**paramVal = NULL;
2600 	char		*modparamVal = NULL;
2601 	int		getUid = 0;
2602 	int		getPasswd = 0;
2603 	int		getCertpath = 0;
2604 	int		rc = 0;
2605 	ns_ldap_error_t	*errorp = NULL;
2606 
2607 #ifdef DEBUG
2608 	(void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
2609 #endif
2610 
2611 	if (aMethod == NULL) {
2612 		/* Require an Auth */
2613 		return (NS_LDAP_INVALID_PARAM);
2614 
2615 	}
2616 	/*
2617 	 * credential level "self" can work with auth method sasl/GSSAPI only
2618 	 */
2619 	if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
2620 	    aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
2621 		return (NS_LDAP_INVALID_PARAM);
2622 
2623 	*authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
2624 	if ((*authp) == NULL)
2625 		return (NS_LDAP_MEMORY);
2626 
2627 	(*authp)->auth = *aMethod;
2628 
2629 	switch (aMethod->type) {
2630 		case NS_LDAP_AUTH_NONE:
2631 			return (NS_LDAP_SUCCESS);
2632 		case NS_LDAP_AUTH_SIMPLE:
2633 			getUid++;
2634 			getPasswd++;
2635 			break;
2636 		case NS_LDAP_AUTH_SASL:
2637 			if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
2638 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
2639 				getUid++;
2640 				getPasswd++;
2641 			} else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
2642 				(void) __ns_ldap_freeCred(authp);
2643 				*authp = NULL;
2644 				return (NS_LDAP_INVALID_PARAM);
2645 			}
2646 			break;
2647 		case NS_LDAP_AUTH_TLS:
2648 			if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
2649 			    ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
2650 			    ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
2651 			    (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
2652 				getUid++;
2653 				getPasswd++;
2654 				getCertpath++;
2655 			} else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
2656 				getCertpath++;
2657 			} else {
2658 				(void) __ns_ldap_freeCred(authp);
2659 				*authp = NULL;
2660 				return (NS_LDAP_INVALID_PARAM);
2661 			}
2662 			break;
2663 	}
2664 
2665 	if (getUid) {
2666 		paramVal = NULL;
2667 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
2668 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2669 			(void) __ns_ldap_freeCred(authp);
2670 			(void) __ns_ldap_freeError(&errorp);
2671 			*authp = NULL;
2672 			return (rc);
2673 		}
2674 
2675 		if (paramVal == NULL || *paramVal == NULL) {
2676 			(void) __ns_ldap_freeCred(authp);
2677 			*authp = NULL;
2678 			return (NS_LDAP_INVALID_PARAM);
2679 		}
2680 
2681 		(*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
2682 		(void) __ns_ldap_freeParam(&paramVal);
2683 		if ((*authp)->cred.unix_cred.userID == NULL) {
2684 			(void) __ns_ldap_freeCred(authp);
2685 			*authp = NULL;
2686 			return (NS_LDAP_MEMORY);
2687 		}
2688 	}
2689 	if (getPasswd) {
2690 		paramVal = NULL;
2691 		if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
2692 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2693 			(void) __ns_ldap_freeCred(authp);
2694 			(void) __ns_ldap_freeError(&errorp);
2695 			*authp = NULL;
2696 			return (rc);
2697 		}
2698 
2699 		if (paramVal == NULL || *paramVal == NULL) {
2700 			(void) __ns_ldap_freeCred(authp);
2701 			*authp = NULL;
2702 			return (NS_LDAP_INVALID_PARAM);
2703 		}
2704 
2705 		modparamVal = dvalue((char *)*paramVal);
2706 		(void) __ns_ldap_freeParam(&paramVal);
2707 		if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
2708 			(void) __ns_ldap_freeCred(authp);
2709 			if (modparamVal != NULL)
2710 				free(modparamVal);
2711 			*authp = NULL;
2712 			return (NS_LDAP_INVALID_PARAM);
2713 		}
2714 
2715 		(*authp)->cred.unix_cred.passwd = modparamVal;
2716 	}
2717 	if (getCertpath) {
2718 		paramVal = NULL;
2719 		if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
2720 		    &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
2721 			(void) __ns_ldap_freeCred(authp);
2722 			(void) __ns_ldap_freeError(&errorp);
2723 			*authp = NULL;
2724 			return (rc);
2725 		}
2726 
2727 		if (paramVal == NULL || *paramVal == NULL) {
2728 			(void) __ns_ldap_freeCred(authp);
2729 			*authp = NULL;
2730 			return (NS_LDAP_INVALID_PARAM);
2731 		}
2732 
2733 		(*authp)->hostcertpath = strdup((char *)*paramVal);
2734 		(void) __ns_ldap_freeParam(&paramVal);
2735 		if ((*authp)->hostcertpath == NULL) {
2736 			(void) __ns_ldap_freeCred(authp);
2737 			*authp = NULL;
2738 			return (NS_LDAP_MEMORY);
2739 		}
2740 	}
2741 	return (NS_LDAP_SUCCESS);
2742 }
2743 
2744 /*
2745  * FUNCTION:	__s_api_getConnection
2746  *
2747  *	Bind to the specified server or one from the server
2748  *	list and return the pointer.
2749  *
2750  *	This function can rebind or not (NS_LDAP_HARD), it can require a
2751  *	credential or bind anonymously
2752  *
2753  *	This function follows the DUA configuration schema algorithm
2754  *
2755  * RETURN VALUES:
2756  *
2757  * NS_LDAP_SUCCESS	A connection was made successfully.
2758  * NS_LDAP_SUCCESS_WITH_INFO
2759  * 			A connection was made successfully, but with
2760  *			password management info in *errorp
2761  * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
2762  * NS_LDAP_CONFIG	If there are any config errors.
2763  * NS_LDAP_MEMORY	Memory errors.
2764  * NS_LDAP_INTERNAL	If there was a ldap error.
2765  *
2766  * INPUT:
2767  *
2768  * server	Bind to this LDAP server only
2769  * flags	If NS_LDAP_HARD is set function will not return until it has
2770  *		a connection unless there is a authentication problem.
2771  *		If NS_LDAP_NEW_CONN is set the function must force a new
2772  *              connection to be created
2773  *		If NS_LDAP_KEEP_CONN is set the connection is to be kept open
2774  * auth		Credentials for bind. This could be NULL in which case
2775  *		a default cred built from the config module is used.
2776  * sessionId	cookie that points to a previous session
2777  * fail_if_new_pwd_reqd
2778  *		a flag indicating this function should fail if the passwd
2779  *		in auth needs to change immediately
2780  * nopasswd_acct_mgmt
2781  *		a flag indicating that makeConnection should check before
2782  *		binding if server supports LDAP V3 password less
2783  *		account management
2784  *
2785  * OUTPUT:
2786  *
2787  * session	pointer to a session with connection information
2788  * errorp	Set if there are any INTERNAL, or CONFIG error.
2789  */
2790 int
2791 __s_api_getConnection(
2792 	const char *server,
2793 	const int flags,
2794 	const ns_cred_t *cred,		/* credentials for bind */
2795 	ConnectionID *sessionId,
2796 	Connection **session,
2797 	ns_ldap_error_t **errorp,
2798 	int fail_if_new_pwd_reqd,
2799 	int nopasswd_acct_mgmt)
2800 {
2801 	char		errmsg[MAXERROR];
2802 	ns_auth_t	**aMethod = NULL;
2803 	ns_auth_t	**aNext = NULL;
2804 	int		**cLevel = NULL;
2805 	int		**cNext = NULL;
2806 	int		timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
2807 	int		rc;
2808 	Connection	*con = NULL;
2809 	int		sec = 1;
2810 	ns_cred_t 	*authp = NULL;
2811 	ns_cred_t	anon;
2812 	int		version = NS_LDAP_V2, self_gssapi_only = 0;
2813 	void		**paramVal = NULL;
2814 	char		**badSrvrs = NULL; /* List of problem hostnames */
2815 
2816 	if ((session == NULL) || (sessionId == NULL)) {
2817 		return (NS_LDAP_INVALID_PARAM);
2818 	}
2819 	*session = NULL;
2820 
2821 	/* if we already have a session id try to reuse connection */
2822 	if (*sessionId > 0) {
2823 		rc = findConnectionById(flags, cred, *sessionId, &con);
2824 		if (rc == *sessionId && con) {
2825 			*session = con;
2826 			return (NS_LDAP_SUCCESS);
2827 		}
2828 		*sessionId = 0;
2829 	}
2830 
2831 	/* get profile version number */
2832 	if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
2833 	    &paramVal, errorp)) != NS_LDAP_SUCCESS)
2834 		return (rc);
2835 	if (paramVal == NULL) {
2836 		(void) sprintf(errmsg, gettext("getConnection: no file "
2837 		    "version"));
2838 		MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
2839 		    NS_LDAP_CONFIG);
2840 		return (NS_LDAP_CONFIG);
2841 	}
2842 	if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
2843 		version = NS_LDAP_V1;
2844 	(void) __ns_ldap_freeParam((void ***)&paramVal);
2845 
2846 	/* Get the bind timeout value */
2847 	(void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
2848 	if (paramVal != NULL && *paramVal != NULL) {
2849 		timeoutSec = **((int **)paramVal);
2850 		(void) __ns_ldap_freeParam(&paramVal);
2851 	}
2852 	if (*errorp)
2853 		(void) __ns_ldap_freeError(errorp);
2854 
2855 	if (cred == NULL) {
2856 		/* Get the authentication method list */
2857 		if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
2858 		    (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
2859 			return (rc);
2860 		if (aMethod == NULL) {
2861 			aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
2862 			if (aMethod == NULL)
2863 				return (NS_LDAP_MEMORY);
2864 			aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
2865 			if (aMethod[0] == NULL) {
2866 				free(aMethod);
2867 				return (NS_LDAP_MEMORY);
2868 			}
2869 			if (version == NS_LDAP_V1)
2870 				(aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
2871 			else {
2872 				(aMethod[0])->type = NS_LDAP_AUTH_SASL;
2873 				(aMethod[0])->saslmech =
2874 				    NS_LDAP_SASL_DIGEST_MD5;
2875 				(aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
2876 			}
2877 		}
2878 
2879 		/* Get the credential level list */
2880 		if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
2881 		    (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
2882 			(void) __ns_ldap_freeParam((void ***)&aMethod);
2883 			return (rc);
2884 		}
2885 		if (cLevel == NULL) {
2886 			cLevel = (int **)calloc(2, sizeof (int *));
2887 			if (cLevel == NULL)
2888 				return (NS_LDAP_MEMORY);
2889 			cLevel[0] = (int *)calloc(1, sizeof (int));
2890 			if (cLevel[0] == NULL)
2891 				return (NS_LDAP_MEMORY);
2892 			if (version == NS_LDAP_V1)
2893 				*(cLevel[0]) = NS_LDAP_CRED_PROXY;
2894 			else
2895 				*(cLevel[0]) = NS_LDAP_CRED_ANON;
2896 		}
2897 	}
2898 
2899 	/* setup the anon credential for anonymous connection */
2900 	(void) memset(&anon, 0, sizeof (ns_cred_t));
2901 	anon.auth.type = NS_LDAP_AUTH_NONE;
2902 
2903 	for (; ; ) {
2904 		if (cred != NULL) {
2905 			/* using specified auth method */
2906 			rc = makeConnection(&con, server, cred,
2907 			    sessionId, timeoutSec, errorp,
2908 			    fail_if_new_pwd_reqd,
2909 			    nopasswd_acct_mgmt, flags, &badSrvrs);
2910 			/* not using bad server if credentials were supplied */
2911 			if (badSrvrs && *badSrvrs) {
2912 				__s_api_free2dArray(badSrvrs);
2913 				badSrvrs = NULL;
2914 			}
2915 			if (rc == NS_LDAP_SUCCESS ||
2916 			    rc == NS_LDAP_SUCCESS_WITH_INFO) {
2917 				*session = con;
2918 				break;
2919 			}
2920 		} else {
2921 			self_gssapi_only = __s_api_self_gssapi_only_get();
2922 			/* for every cred level */
2923 			for (cNext = cLevel; *cNext != NULL; cNext++) {
2924 				if (self_gssapi_only &&
2925 				    **cNext != NS_LDAP_CRED_SELF)
2926 					continue;
2927 				if (**cNext == NS_LDAP_CRED_ANON) {
2928 					/*
2929 					 * make connection anonymously
2930 					 * Free the down server list before
2931 					 * looping through
2932 					 */
2933 					if (badSrvrs && *badSrvrs) {
2934 						__s_api_free2dArray(badSrvrs);
2935 						badSrvrs = NULL;
2936 					}
2937 					rc = makeConnection(&con, server, &anon,
2938 					    sessionId, timeoutSec, errorp,
2939 					    fail_if_new_pwd_reqd,
2940 					    nopasswd_acct_mgmt, flags,
2941 					    &badSrvrs);
2942 					if (rc == NS_LDAP_SUCCESS ||
2943 					    rc ==
2944 					    NS_LDAP_SUCCESS_WITH_INFO) {
2945 						*session = con;
2946 						goto done;
2947 					}
2948 					continue;
2949 				}
2950 				/* for each cred level */
2951 				for (aNext = aMethod; *aNext != NULL; aNext++) {
2952 					if (self_gssapi_only &&
2953 					    (*aNext)->saslmech !=
2954 					    NS_LDAP_SASL_GSSAPI)
2955 						continue;
2956 					/*
2957 					 * self coexists with sasl/GSSAPI only
2958 					 * and non-self coexists with non-gssapi
2959 					 * only
2960 					 */
2961 					if ((**cNext == NS_LDAP_CRED_SELF &&
2962 					    (*aNext)->saslmech !=
2963 					    NS_LDAP_SASL_GSSAPI) ||
2964 					    (**cNext != NS_LDAP_CRED_SELF &&
2965 					    (*aNext)->saslmech ==
2966 					    NS_LDAP_SASL_GSSAPI))
2967 						continue;
2968 					/* make connection and authenticate */
2969 					/* with default credentials */
2970 					authp = NULL;
2971 					rc = __s_api_getDefaultAuth(*cNext,
2972 					    *aNext, &authp);
2973 					if (rc != NS_LDAP_SUCCESS) {
2974 						continue;
2975 					}
2976 					/*
2977 					 * Free the down server list before
2978 					 * looping through
2979 					 */
2980 					if (badSrvrs && *badSrvrs) {
2981 						__s_api_free2dArray(badSrvrs);
2982 						badSrvrs = NULL;
2983 					}
2984 					rc = makeConnection(&con, server, authp,
2985 					    sessionId, timeoutSec, errorp,
2986 					    fail_if_new_pwd_reqd,
2987 					    nopasswd_acct_mgmt, flags,
2988 					    &badSrvrs);
2989 					(void) __ns_ldap_freeCred(&authp);
2990 					if (rc == NS_LDAP_SUCCESS ||
2991 					    rc ==
2992 					    NS_LDAP_SUCCESS_WITH_INFO) {
2993 						*session = con;
2994 						goto done;
2995 					}
2996 				}
2997 			}
2998 		}
2999 		if (flags & NS_LDAP_HARD) {
3000 			if (sec < LDAPMAXHARDLOOKUPTIME)
3001 				sec *= 2;
3002 			_sleep(sec);
3003 		} else {
3004 			break;
3005 		}
3006 	}
3007 
3008 done:
3009 	/*
3010 	 * If unable to get a connection, and this is
3011 	 * the thread opening the shared connection,
3012 	 * unlock the session mutex and let other
3013 	 * threads try to get their own connection.
3014 	 */
3015 	if (wait4session != 0 && sessionTid == thr_self()) {
3016 		wait4session = 0;
3017 		sessionTid = 0;
3018 #ifdef DEBUG
3019 		(void) fprintf(stderr, "tid= %d: __s_api_getConnection: "
3020 		    "unlocking sessionLock \n", thr_self());
3021 		fflush(stderr);
3022 #endif /* DEBUG */
3023 		(void) mutex_unlock(&sessionLock);
3024 	}
3025 	if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
3026 		/*
3027 		 * self_gssapi_only is true but no self/sasl/gssapi is
3028 		 * configured
3029 		 */
3030 		rc = NS_LDAP_CONFIG;
3031 	}
3032 
3033 	(void) __ns_ldap_freeParam((void ***)&aMethod);
3034 	(void) __ns_ldap_freeParam((void ***)&cLevel);
3035 
3036 	if (badSrvrs && *badSrvrs) {
3037 		/*
3038 		 * At this point, either we have a successful
3039 		 * connection or exhausted all the possible auths.
3040 		 * and creds. Mark the problem servers as down
3041 		 * so that the problem servers are not contacted
3042 		 * again until the refresh_ttl expires.
3043 		 */
3044 		(void) __s_api_removeBadServers(badSrvrs);
3045 		__s_api_free2dArray(badSrvrs);
3046 	}
3047 	return (rc);
3048 }
3049 
3050 #pragma fini(_free_sessionPool)
3051 static void
3052 _free_sessionPool()
3053 {
3054 	int id;
3055 
3056 	(void) rw_wrlock(&sessionPoolLock);
3057 	if (sessionPool != NULL) {
3058 		for (id = 0; id < sessionPoolSize; id++)
3059 			_DropConnection(id + CONID_OFFSET, 0, 1);
3060 		free(sessionPool);
3061 		sessionPool = NULL;
3062 		sessionPoolSize = 0;
3063 	}
3064 	(void) rw_unlock(&sessionPoolLock);
3065 }
3066