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