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 
53 static ns_ldap_return_code performBind(const ns_cred_t *,
54 					LDAP *,
55 					int,
56 					ns_ldap_error_t **,
57 					int,
58 					int);
59 static 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 
66 extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
67 		LDAPControl **, LDAPControl **);
68 extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
69 
70 extern int __door_getconf(char **buffer, int *buflen,
71 		ns_ldap_error_t **error, int callnumber);
72 extern int __ns_ldap_freeUnixCred(UnixCred_t **credp);
73 extern int SetDoorInfoToUnixCred(char *buffer,
74 		ns_ldap_error_t **errorp,
75 		UnixCred_t **cred);
76 
77 static int openConnection(LDAP **, const char *, const ns_cred_t *,
78 		int, ns_ldap_error_t **, int, int, ns_conn_user_t *, int);
79 static void
80 _DropConnection(ConnectionID cID, int flag, int fini);
81 
82 static mutex_t sessionPoolLock = DEFAULTMUTEX;
83 
84 static Connection **sessionPool = NULL;
85 static 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  */
104 static
105 ns_ldap_return_code
getFirstFromConfig(ns_server_info_t * ret,ns_ldap_error_t ** error)106 getFirstFromConfig(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 */
145 static int
__door_getadmincred(char ** buffer,int * buflen,ns_ldap_error_t ** error)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 
156 static int
requestAdminCred(UnixCred_t ** cred,ns_ldap_error_t ** error)157 requestAdminCred(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 
184 int
__s_api_requestServer(const char * request,const char * server,ns_server_info_t * ret,ns_ldap_error_t ** error,const char * addrType)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  */
469 static void
printCred(FILE * fp,const ns_cred_t * cred)470 printCred(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  */
497 static void
printConnection(FILE * fp,Connection * con)498 printConnection(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  */
522 static int
addConnection(Connection * con)523 addConnection(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  */
584 static int
findConnection(int flags,const char * serverAddr,const ns_cred_t * auth,Connection ** conp)585 findConnection(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  */
657 void
__s_api_freeConnection(Connection * con)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 
683 static int
makeConnection(Connection ** conp,const char * serverAddr,const ns_cred_t * auth,ConnectionID * cID,int timeoutSec,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int nopasswd_acct_mgmt,int flags,char *** badsrvrs,ns_conn_user_t * conn_user)684 makeConnection(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 
946 create_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 
1036 static void
_DropConnection(ConnectionID cID,int flag,int fini)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 
1094 void
DropConnection(ConnectionID cID,int flag)1095 DropConnection(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 
1128 static int
process_pwd_mgmt(char * bind_type,int ldaprc,LDAPControl ** controls,char * errmsg,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int passwd_mgmt)1129 process_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 
1317 static int
ldap_in_nss_switch(char * db)1318 ldap_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 
1343 static int
openConnection(LDAP ** ldp,const char * serverAddr,const ns_cred_t * auth,int timeoutSec,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int passwd_mgmt,ns_conn_user_t * conn_user,int flags)1344 openConnection(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  */
1488 static int
__s_api_getDefaultAuth(int * cLevel,ns_auth_t * aMethod,ns_cred_t ** authp,int getAdmin)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  */
1696 static int
getConnection(const char * server,const int flags,const ns_cred_t * cred,ConnectionID * sessionId,Connection ** session,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int nopasswd_acct_mgmt,ns_conn_user_t * conn_user)1697 getConnection(
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 
1914 done:
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  */
1986 int
__s_api_getConnection(const char * server,const int flags,const ns_cred_t * cred,ConnectionID * sessionId,Connection ** session,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int nopasswd_acct_mgmt,ns_conn_user_t * conn_user)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 
2012 void
__s_api_free_sessionPool()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  */
2034 static
2035 ns_ldap_return_code
createTLSSession(const ns_cred_t * auth,const char * serverAddr,uint16_t port,int timeoutMilliSec,LDAP ** ldp,ns_ldap_error_t ** errorp)2036 createTLSSession(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  */
2125 static int
cvt_hostname2ip(char * server,char * newaddr,int newaddr_size,char * errstr,int errstr_size)2126 cvt_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  */
2214 static
2215 ns_ldap_return_code
createNonTLSSession(const char * serverAddr,uint16_t port,int gssapi,LDAP ** ldp,ns_ldap_error_t ** errorp)2216 createNonTLSSession(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  */
2283 static
2284 ns_ldap_return_code
createSession(const ns_cred_t * auth,const char * serverAddr,uint16_t port,int timeoutMilliSec,LDAP ** ldp,ns_ldap_error_t ** errorp)2285 createSession(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  */
2331 static
2332 ns_ldap_return_code
doSimpleBind(const ns_cred_t * auth,LDAP * ld,int timeoutSec,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int passwd_mgmt)2333 doSimpleBind(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  */
2429 static
2430 ns_ldap_return_code
doSASLBind(const ns_cred_t * auth,LDAP * ld,int timeoutSec,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int passwd_mgmt)2431 doSASLBind(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  */
2631 static
2632 ns_ldap_return_code
performBind(const ns_cred_t * auth,LDAP * ld,int timeoutSec,ns_ldap_error_t ** errorp,int fail_if_new_pwd_reqd,int passwd_mgmt)2633 performBind(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