dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libsldap / common / ns_connect.c
blob2447649b3e78306c05c42991934a5ec4629f766d
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
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>
51 #define USE_DEFAULT_PORT 0
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 **);
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);
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);
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);
82 static mutex_t sessionPoolLock = DEFAULTMUTEX;
84 static Connection **sessionPool = NULL;
85 static int sessionPoolSize = 0;
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.
92 #define MAX_SASL_SSF 65535
93 #define MIN_SASL_SSF 0
95 /* Number of hostnames to allocate memory for */
96 #define NUMTOMALLOC 32
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.
104 static
105 ns_ldap_return_code
106 getFirstFromConfig(ns_server_info_t *ret, ns_ldap_error_t **error)
108 char **servers = NULL;
109 ns_ldap_return_code ret_code;
110 char errstr[MAXERROR];
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);
118 return (ret_code);
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);
130 ret->server = strdup(servers[0]);
131 if (ret->server == NULL) {
132 __s_api_free2dArray(servers);
133 return (NS_LDAP_MEMORY);
136 ret->saslMechanisms = NULL;
137 ret->controls = NULL;
139 __s_api_free2dArray(servers);
141 return (NS_LDAP_SUCCESS);
144 /* very similar to __door_getldapconfig() in ns_config.c */
145 static int
146 __door_getadmincred(char **buffer, int *buflen, ns_ldap_error_t **error)
148 return (__door_getconf(buffer, buflen, error, GETADMINCRED));
152 * This function requests Admin credentials from the cache manager through
153 * the door functionality
156 static int
157 requestAdminCred(UnixCred_t **cred, ns_ldap_error_t **error)
159 char *buffer = NULL;
160 int buflen = 0;
161 int ret;
163 *error = NULL;
164 ret = __door_getadmincred(&buffer, &buflen, error);
166 if (ret != NS_LDAP_SUCCESS) {
167 if (*error != NULL && (*error)->message != NULL)
168 syslog(LOG_WARNING, "libsldap: %s", (*error)->message);
169 return (ret);
172 /* now convert from door format */
173 ret = SetDoorInfoToUnixCred(buffer, error, cred);
174 free(buffer);
176 return (ret);
180 * This function requests a server from the cache manager through
181 * the door functionality
185 __s_api_requestServer(const char *request, const char *server,
186 ns_server_info_t *ret, ns_ldap_error_t **error, const char *addrType)
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;
204 if (ret == NULL || error == NULL) {
205 return (NS_LDAP_OP_FAILED);
207 (void) memset(ret, 0, sizeof (ns_server_info_t));
208 *error = NULL;
210 if (request == NULL)
211 ireq = NS_CACHE_NEW;
212 else
213 ireq = request;
216 * In the 'Standalone' mode a server will be obtained
217 * from the local libsldap's list
219 if (__s_api_isStandalone()) {
220 if ((ret_code = __s_api_findRootDSE(ireq,
221 server,
222 addrType,
223 ret,
224 error)) != NS_LDAP_SUCCESS) {
226 * get first server from local list only once
227 * to prevent looping
229 if (strcmp(ireq, NS_CACHE_NEW) != 0)
230 return (ret_code);
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);
240 ret_code = getFirstFromConfig(ret, error);
241 if (ret_code != NS_LDAP_SUCCESS) {
242 return (ret_code);
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);
268 return (NS_LDAP_SUCCESS);
271 (void) memset(space.s_b, 0, DOORBUFFERSIZE);
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;
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);
294 sptr = &space.s_d;
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);
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);
328 return (NS_LDAP_SUCCESS);
329 case NS_CACHE_NOTFOUND:
330 default:
331 return (NS_LDAP_OP_FAILED);
334 /* copy info from door call return structure here */
335 rbuf = space.s_d.ldap_ret.ldap_u.config;
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);
346 ret->server = strdup(ptr);
347 if (ret->server == NULL) {
348 return (NS_LDAP_MEMORY);
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), 0);
358 free(ret->server);
359 ret->server = NULL;
360 return (NS_LDAP_OP_FAILED);
362 ret->serverFQDN = strdup(ptr);
363 if (ret->serverFQDN == NULL) {
364 free(ret->server);
365 ret->server = NULL;
366 return (NS_LDAP_MEMORY);
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 = reallocarray(mptr, mcnt + 2,
386 sizeof (char *));
387 if (mptr1 == NULL) {
388 __s_api_free2dArray(mptr);
389 if (sptr != &space.s_d) {
390 (void) munmap((char *)sptr, ndata);
392 __s_api_free2dArray(cptr);
393 __s_api_free_server_info(ret);
394 return (NS_LDAP_MEMORY);
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);
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);
409 mcnt++;
410 mptr[mcnt] = NULL;
412 if (strncasecmp(ptr, _SUPPORTEDCONTROL,
413 _SUPPORTEDCONTROL_LEN) == 0) {
414 dptr = strchr(ptr, '=');
415 if (dptr == NULL)
416 continue;
417 dptr++;
418 cptr1 = reallocarray(cptr, ccnt + 2,
419 sizeof (char *));
420 if (cptr1 == NULL) {
421 if (sptr != &space.s_d) {
422 (void) munmap((char *)sptr, ndata);
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);
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);
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);
443 ccnt++;
444 cptr[ccnt] = NULL;
447 if (mptr != NULL) {
448 ret->saslMechanisms = mptr;
450 if (cptr != NULL) {
451 ret->controls = cptr;
455 /* clean up door call */
456 if (sptr != &space.s_d) {
457 (void) munmap((char *)sptr, ndata);
459 *error = NULL;
461 return (NS_LDAP_SUCCESS);
465 #ifdef DEBUG
467 * printCred(): prints the credential structure
469 static void
470 printCred(FILE *fp, const ns_cred_t *cred)
472 thread_t t = thr_self();
474 if (cred == NULL) {
475 (void) fprintf(fp, "tid= %d: printCred: cred is NULL\n", t);
476 return;
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);
495 * printConnection(): prints the connection structure
497 static void
498 printConnection(FILE *fp, Connection *con)
500 thread_t t = thr_self();
502 if (con == NULL)
503 return;
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);
512 printCred(fp, con->auth);
514 #endif
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
522 static int
523 addConnection(Connection *con)
525 int i;
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);
541 #ifdef DEBUG
542 (void) fprintf(stderr, "Initialized sessionPool\n");
543 #endif /* DEBUG */
545 for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
547 if (i == sessionPoolSize) {
548 /* run out of array, need to increase sessionPool */
549 Connection **cl;
550 cl = reallocarray(sessionPool,
551 sessionPoolSize + SESSION_CACHE_INC,
552 sizeof (Connection *));
553 if (!cl) {
554 (void) mutex_unlock(&sessionPoolLock);
555 return (-1);
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 */
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);
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.
584 static int
585 findConnection(int flags, const char *serverAddr,
586 const ns_cred_t *auth, Connection **conp)
588 Connection *cp;
589 int i;
590 #ifdef DEBUG
591 thread_t t;
592 #endif /* DEBUG */
594 if (auth == NULL || conp == NULL)
595 return (-1);
596 *conp = NULL;
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.
603 if ((flags & NS_LDAP_NEW_CONN) || (!__s_api_nscd_proc() &&
604 !__s_api_peruser_proc() && !(flags & NS_LDAP_KEEP_CONN)))
605 return (-1);
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;
635 if (__s_api_is_auth_matched(cp->auth, auth) == B_FALSE)
636 continue;
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);
650 (void) mutex_unlock(&sessionPoolLock);
651 return (-1);
655 * Free a Connection structure
657 void
658 __s_api_freeConnection(Connection *con)
660 if (con == NULL)
661 return;
662 free(con->serverAddr);
663 if (con->auth)
664 (void) __ns_ldap_freeCred(&(con->auth));
665 if (con->saslMechanisms) {
666 __s_api_free2dArray(con->saslMechanisms);
668 if (con->controls) {
669 __s_api_free2dArray(con->controls);
671 free(con);
675 * Find a connection matching the passed in criteria. If an open
676 * connection with that criteria exists use it, otherwise open a
677 * new connection.
678 * Success: returns the pointer to the Connection structure
679 * Failure: returns NULL, error code and message should be in errorp
682 static int
683 makeConnection(Connection **conp, const char *serverAddr,
684 const ns_cred_t *auth, ConnectionID *cID, int timeoutSec,
685 ns_ldap_error_t **errorp, int fail_if_new_pwd_reqd,
686 int nopasswd_acct_mgmt, int flags, char ***badsrvrs,
687 ns_conn_user_t *conn_user)
689 Connection *con = NULL;
690 ConnectionID id;
691 char errmsg[MAXERROR];
692 int rc, exit_rc = NS_LDAP_SUCCESS;
693 ns_server_info_t sinfo;
694 char *hReq, *host = NULL;
695 LDAP *ld = NULL;
696 int passwd_mgmt = 0;
697 int totalbad = 0; /* Number of servers contacted unsuccessfully */
698 short memerr = 0; /* Variable for tracking memory allocation */
699 char *serverAddrType = NULL, **bindHost = NULL;
702 if (conp == NULL || errorp == NULL || auth == NULL)
703 return (NS_LDAP_INVALID_PARAM);
704 if (*errorp)
705 (void) __ns_ldap_freeError(errorp);
706 *conp = NULL;
707 (void) memset(&sinfo, 0, sizeof (sinfo));
709 if ((id = findConnection(flags, serverAddr, auth, &con)) != -1) {
710 /* connection found in cache */
711 #ifdef DEBUG
712 (void) fprintf(stderr, "tid= %d: connection found in "
713 "cache %d\n", thr_self(), id);
714 fflush(stderr);
715 #endif /* DEBUG */
716 *cID = id;
717 *conp = con;
718 return (NS_LDAP_SUCCESS);
721 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
722 serverAddrType = NS_CACHE_ADDR_HOSTNAME;
723 bindHost = &sinfo.serverFQDN;
724 } else {
725 serverAddrType = NS_CACHE_ADDR_IP;
726 bindHost = &sinfo.server;
729 if (serverAddr) {
730 if (__s_api_isInitializing()) {
732 * When obtaining the root DSE, connect to the server
733 * passed here through the serverAddr parameter
735 sinfo.server = strdup(serverAddr);
736 if (sinfo.server == NULL)
737 return (NS_LDAP_MEMORY);
738 if (strcmp(serverAddrType,
739 NS_CACHE_ADDR_HOSTNAME) == 0) {
740 rc = __s_api_ip2hostname(sinfo.server,
741 &sinfo.serverFQDN);
742 if (rc != NS_LDAP_SUCCESS) {
743 (void) snprintf(errmsg,
744 sizeof (errmsg),
745 gettext("The %s address "
746 "can not be resolved into "
747 "a host name. Returning "
748 "the address as it is."),
749 serverAddr);
750 MKERROR(LOG_ERR,
751 *errorp,
752 NS_CONFIG_NOTLOADED,
753 strdup(errmsg),
754 NS_LDAP_MEMORY);
755 __s_api_free_server_info(&sinfo);
756 return (NS_LDAP_INTERNAL);
759 } else {
761 * We're given the server address, just use it.
762 * In case of sasl/GSSAPI, serverAddr would need
763 * to be a FQDN. We assume this is the case for now.
765 * Only the server address fields of sinfo structure
766 * are filled in since these are the only relevant
767 * data that we have. Other fields of this structure
768 * (controls, saslMechanisms) are kept to NULL.
770 sinfo.server = strdup(serverAddr);
771 if (sinfo.server == NULL) {
772 return (NS_LDAP_MEMORY);
774 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
775 sinfo.serverFQDN = strdup(serverAddr);
776 if (sinfo.serverFQDN == NULL) {
777 free(sinfo.server);
778 return (NS_LDAP_MEMORY);
782 rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
783 fail_if_new_pwd_reqd, passwd_mgmt, conn_user, flags);
784 if (rc == NS_LDAP_SUCCESS || rc ==
785 NS_LDAP_SUCCESS_WITH_INFO) {
786 exit_rc = rc;
787 goto create_con;
788 } else {
789 if (auth->auth.saslmech == NS_LDAP_SASL_GSSAPI) {
790 (void) snprintf(errmsg, sizeof (errmsg),
791 "%s %s", gettext("makeConnection: "
792 "failed to open connection using "
793 "sasl/GSSAPI to"), *bindHost);
794 } else {
795 (void) snprintf(errmsg, sizeof (errmsg),
796 "%s %s", gettext("makeConnection: "
797 "failed to open connection to"),
798 *bindHost);
800 syslog(LOG_ERR, "libsldap: %s", errmsg);
801 __s_api_free_server_info(&sinfo);
802 return (rc);
806 /* No cached connection, create one */
807 for (; ; ) {
808 if (host == NULL)
809 hReq = NS_CACHE_NEW;
810 else
811 hReq = NS_CACHE_NEXT;
812 rc = __s_api_requestServer(hReq, host, &sinfo, errorp,
813 serverAddrType);
814 if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
815 (host && (strcasecmp(host, sinfo.server) == 0))) {
816 /* Log the error */
817 if (*errorp) {
818 (void) snprintf(errmsg, sizeof (errmsg),
819 "%s: (%s)", gettext("makeConnection: "
820 "unable to make LDAP connection, "
821 "request for a server failed"),
822 (*errorp)->message);
823 syslog(LOG_ERR, "libsldap: %s", errmsg);
826 __s_api_free_server_info(&sinfo);
827 free(host);
828 return (NS_LDAP_OP_FAILED);
830 free(host);
831 host = strdup(sinfo.server);
832 if (host == NULL) {
833 __s_api_free_server_info(&sinfo);
834 return (NS_LDAP_MEMORY);
837 /* check if server supports password management */
838 passwd_mgmt = __s_api_contain_passwd_control_oid(
839 sinfo.controls);
840 /* check if server supports password less account mgmt */
841 if (nopasswd_acct_mgmt &&
842 !__s_api_contain_account_usable_control_oid(
843 sinfo.controls)) {
844 syslog(LOG_WARNING, "libsldap: server %s does not "
845 "provide account information without password",
846 host);
847 free(host);
848 __s_api_free_server_info(&sinfo);
849 return (NS_LDAP_OP_FAILED);
851 /* make the connection */
852 rc = openConnection(&ld, *bindHost, auth, timeoutSec, errorp,
853 fail_if_new_pwd_reqd, passwd_mgmt, conn_user, flags);
854 /* if success, go to create connection structure */
855 if (rc == NS_LDAP_SUCCESS ||
856 rc == NS_LDAP_SUCCESS_WITH_INFO) {
857 exit_rc = rc;
858 break;
862 * If not able to reach the server, inform the ldap
863 * cache manager that the server should be removed
864 * from its server list. Thus, the manager will not
865 * return this server on the next get-server request
866 * and will also reduce the server list refresh TTL,
867 * so that it will find out sooner when the server
868 * is up again.
870 if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
871 if ((*errorp)->status == LDAP_CONNECT_ERROR ||
872 (*errorp)->status == LDAP_SERVER_DOWN) {
873 /* Reset memory allocation error */
874 memerr = 0;
876 * We contacted a server that we could
877 * not either authenticate to or contact.
878 * If it is due to authentication, then
879 * we need to try the server again. So,
880 * do not remove the server yet, but
881 * add it to the bad server list.
882 * The caller routine will remove
883 * the servers if:
884 * a). A good server is found or
885 * b). All the possible methods
886 * are tried without finding
887 * a good server
889 if (*badsrvrs == NULL) {
890 if (!(*badsrvrs = (char **)malloc
891 (sizeof (char *) * NUMTOMALLOC))) {
892 memerr = 1;
894 /* Allocate memory in chunks of NUMTOMALLOC */
895 } else if ((totalbad % NUMTOMALLOC) ==
896 NUMTOMALLOC - 1) {
897 char **tmpptr;
898 if (!(tmpptr = (char **)realloc(
899 *badsrvrs,
900 (sizeof (char *) * NUMTOMALLOC *
901 ((totalbad/NUMTOMALLOC) + 2))))) {
902 memerr = 1;
903 } else {
904 *badsrvrs = tmpptr;
908 * Store host only if there were no unsuccessful
909 * memory allocations above
911 if (!memerr &&
912 !((*badsrvrs)[totalbad++] = strdup(host))) {
913 memerr = 1;
914 totalbad--;
916 (*badsrvrs)[totalbad] = NULL;
920 /* else, cleanup and go for the next server */
921 __s_api_free_server_info(&sinfo);
923 /* Return if we had memory allocation errors */
924 if (memerr)
925 return (NS_LDAP_MEMORY);
926 if (*errorp) {
928 * If openConnection() failed due to
929 * password policy, or invalid credential,
930 * keep *errorp and exit
932 if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
933 (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
934 free(host);
935 return (rc);
936 } else {
937 (void) __ns_ldap_freeError(errorp);
938 *errorp = NULL;
943 create_con:
944 /* we have created ld, setup con structure */
945 free(host);
946 if ((con = calloc(1, sizeof (Connection))) == NULL) {
947 __s_api_free_server_info(&sinfo);
949 * If password control attached in **errorp,
950 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
951 * free the error structure
953 if (*errorp) {
954 (void) __ns_ldap_freeError(errorp);
955 *errorp = NULL;
957 (void) ldap_unbind(ld);
958 return (NS_LDAP_MEMORY);
961 con->serverAddr = sinfo.server; /* Store original format */
962 if (sinfo.serverFQDN != NULL) {
963 free(sinfo.serverFQDN);
964 sinfo.serverFQDN = NULL;
966 con->saslMechanisms = sinfo.saslMechanisms;
967 con->controls = sinfo.controls;
969 con->auth = __ns_ldap_dupAuth(auth);
970 if (con->auth == NULL) {
971 (void) ldap_unbind(ld);
972 __s_api_freeConnection(con);
974 * If password control attached in **errorp,
975 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
976 * free the error structure
978 if (*errorp) {
979 (void) __ns_ldap_freeError(errorp);
980 *errorp = NULL;
982 return (NS_LDAP_MEMORY);
985 con->threadID = thr_self();
986 con->pid = getpid();
988 con->ld = ld;
989 /* add MT connection to the MT connection pool */
990 if (conn_user != NULL && conn_user->conn_mt != NULL) {
991 if (__s_api_conn_mt_add(con, conn_user, errorp) ==
992 NS_LDAP_SUCCESS) {
993 *conp = con;
994 return (exit_rc);
995 } else {
996 (void) ldap_unbind(ld);
997 __s_api_freeConnection(con);
998 return ((*errorp)->status);
1002 /* MT connection not supported or not required case */
1003 if ((id = addConnection(con)) == -1) {
1004 (void) ldap_unbind(ld);
1005 __s_api_freeConnection(con);
1007 * If password control attached in **errorp,
1008 * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
1009 * free the error structure
1011 if (*errorp) {
1012 (void) __ns_ldap_freeError(errorp);
1013 *errorp = NULL;
1015 return (NS_LDAP_MEMORY);
1017 #ifdef DEBUG
1018 (void) fprintf(stderr, "tid= %d: connection added into "
1019 "cache %d\n", thr_self(), id);
1020 fflush(stderr);
1021 #endif /* DEBUG */
1022 *cID = id;
1023 *conp = con;
1024 return (exit_rc);
1028 * Return the specified connection to the pool. If necessary
1029 * delete the connection.
1032 static void
1033 _DropConnection(ConnectionID cID, int flag, int fini)
1035 Connection *cp;
1036 int id;
1037 int use_mutex = !fini;
1038 struct timeval zerotime;
1039 LDAPMessage *res;
1041 zerotime.tv_sec = zerotime.tv_usec = 0L;
1043 id = cID - CONID_OFFSET;
1044 if (id < 0 || id >= sessionPoolSize)
1045 return;
1046 #ifdef DEBUG
1047 (void) fprintf(stderr,
1048 "tid %d: Dropping connection cID=%d flag=0x%x\n",
1049 thr_self(), cID, flag);
1050 fflush(stderr);
1051 #endif /* DEBUG */
1052 if (use_mutex)
1053 (void) mutex_lock(&sessionPoolLock);
1055 cp = sessionPool[id];
1056 /* sanity check before removing */
1057 if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) {
1058 if (use_mutex)
1059 (void) mutex_unlock(&sessionPoolLock);
1060 return;
1063 if (!fini &&
1064 ((flag & NS_LDAP_NEW_CONN) == 0) &&
1065 ((flag & NS_LDAP_KEEP_CONN) || __s_api_nscd_proc() ||
1066 __s_api_peruser_proc())) {
1067 /* release Connection (keep alive) */
1068 cp->usedBit = B_FALSE;
1069 cp->threadID = 0; /* unmark the threadID */
1071 * Do sanity cleanup of remaining results.
1073 while (ldap_result(cp->ld, LDAP_RES_ANY, LDAP_MSG_ALL,
1074 &zerotime, &res) > 0) {
1075 if (res != NULL)
1076 (void) ldap_msgfree(res);
1078 if (use_mutex)
1079 (void) mutex_unlock(&sessionPoolLock);
1080 } else {
1081 /* delete Connection (disconnect) */
1082 sessionPool[id] = NULL;
1083 if (use_mutex)
1084 (void) mutex_unlock(&sessionPoolLock);
1085 (void) ldap_unbind(cp->ld);
1086 __s_api_freeConnection(cp);
1090 void
1091 DropConnection(ConnectionID cID, int flag)
1093 _DropConnection(cID, flag, 0);
1097 * This routine is called after a bind operation is
1098 * done in openConnection() to process the password
1099 * management information, if any.
1101 * Input:
1102 * bind_type: "simple" or "sasl/DIGEST-MD5"
1103 * ldaprc : ldap rc from the ldap bind operation
1104 * controls : controls returned by the server
1105 * errmsg : error message from the server
1106 * fail_if_new_pwd_reqd:
1107 * flag indicating if connection should be open
1108 * when password needs to change immediately
1109 * passwd_mgmt:
1110 * flag indicating if server supports password
1111 * policy/management
1113 * Output : ns_ldap_error structure, which may contain
1114 * password status and number of seconds until
1115 * expired
1117 * return rc:
1118 * NS_LDAP_EXTERNAL: error, connection should not open
1119 * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
1120 * NS_LDAP_SUCCESS: OK to open connection
1124 static int
1125 process_pwd_mgmt(char *bind_type, int ldaprc,
1126 LDAPControl **controls,
1127 char *errmsg, ns_ldap_error_t **errorp,
1128 int fail_if_new_pwd_reqd,
1129 int passwd_mgmt)
1131 char errstr[MAXERROR];
1132 LDAPControl **ctrl = NULL;
1133 int exit_rc;
1134 ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD;
1135 int sec_until_exp = 0;
1138 * errmsg may be an empty string,
1139 * even if ldaprc is LDAP_SUCCESS,
1140 * free the empty string if that's the case
1142 if (errmsg &&
1143 (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
1144 ldap_memfree(errmsg);
1145 errmsg = NULL;
1148 if (ldaprc != LDAP_SUCCESS) {
1150 * try to map ldap rc and error message to
1151 * a password status
1153 if (errmsg) {
1154 if (passwd_mgmt)
1155 pwd_status =
1156 __s_api_set_passwd_status(
1157 ldaprc, errmsg);
1158 ldap_memfree(errmsg);
1161 (void) snprintf(errstr, sizeof (errstr),
1162 gettext("openConnection: "
1163 "%s bind failed "
1164 "- %s"), bind_type, ldap_err2string(ldaprc));
1166 if (pwd_status != NS_PASSWD_GOOD) {
1167 MKERROR_PWD_MGMT(*errorp,
1168 ldaprc, strdup(errstr),
1169 pwd_status, 0, 0);
1170 } else {
1171 MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
1172 NS_LDAP_MEMORY);
1174 if (controls)
1175 ldap_controls_free(controls);
1177 return (NS_LDAP_INTERNAL);
1181 * ldaprc is LDAP_SUCCESS,
1182 * process the password management controls, if any
1184 exit_rc = NS_LDAP_SUCCESS;
1185 if (controls && passwd_mgmt) {
1187 * The control with the OID
1188 * 2.16.840.1.113730.3.4.4 (or
1189 * LDAP_CONTROL_PWEXPIRED, as defined
1190 * in the ldap.h header file) is the
1191 * expired password control.
1193 * This control is used if the server
1194 * is configured to require users to
1195 * change their passwords when first
1196 * logging in and whenever the
1197 * passwords are reset.
1199 * If the user is logging in for the
1200 * first time or if the user's
1201 * password has been reset, the
1202 * server sends this control to
1203 * indicate that the client needs to
1204 * change the password immediately.
1206 * At this point, the only operation
1207 * that the client can perform is to
1208 * change the user's password. If the
1209 * client requests any other LDAP
1210 * operation, the server sends back
1211 * an LDAP_UNWILLING_TO_PERFORM
1212 * result code with an expired
1213 * password control.
1215 * The control with the OID
1216 * 2.16.840.1.113730.3.4.5 (or
1217 * LDAP_CONTROL_PWEXPIRING, as
1218 * defined in the ldap.h header file)
1219 * is the password expiration warning
1220 * control.
1222 * This control is used if the server
1223 * is configured to expire user
1224 * passwords after a certain amount
1225 * of time.
1227 * The server sends this control back
1228 * to the client if the client binds
1229 * using a password that will soon
1230 * expire. The ldctl_value field of
1231 * the LDAPControl structure
1232 * specifies the number of seconds
1233 * before the password will expire.
1235 for (ctrl = controls; *ctrl; ctrl++) {
1237 if (strcmp((*ctrl)->ldctl_oid,
1238 LDAP_CONTROL_PWEXPIRED) == 0) {
1240 * if the caller wants this bind
1241 * to fail, set up the error info.
1242 * If call to this function is
1243 * for searching the LDAP directory,
1244 * e.g., __ns_ldap_list(),
1245 * there's really no sense to
1246 * let a connection open and
1247 * then fail immediately afterward
1248 * on the LDAP search operation with
1249 * the LDAP_UNWILLING_TO_PERFORM rc
1251 pwd_status =
1252 NS_PASSWD_CHANGE_NEEDED;
1253 if (fail_if_new_pwd_reqd) {
1254 (void) snprintf(errstr,
1255 sizeof (errstr),
1256 gettext(
1257 "openConnection: "
1258 "%s bind "
1259 "failed "
1260 "- password "
1261 "expired. It "
1262 " needs to change "
1263 "immediately!"),
1264 bind_type);
1265 MKERROR_PWD_MGMT(*errorp,
1266 LDAP_SUCCESS,
1267 strdup(errstr),
1268 pwd_status,
1271 exit_rc = NS_LDAP_INTERNAL;
1272 } else {
1273 MKERROR_PWD_MGMT(*errorp,
1274 LDAP_SUCCESS,
1275 NULL,
1276 pwd_status,
1279 exit_rc =
1280 NS_LDAP_SUCCESS_WITH_INFO;
1282 break;
1283 } else if (strcmp((*ctrl)->ldctl_oid,
1284 LDAP_CONTROL_PWEXPIRING) == 0) {
1285 pwd_status =
1286 NS_PASSWD_ABOUT_TO_EXPIRE;
1287 if ((*ctrl)->
1288 ldctl_value.bv_len > 0 &&
1289 (*ctrl)->
1290 ldctl_value.bv_val)
1291 sec_until_exp =
1292 atoi((*ctrl)->
1293 ldctl_value.bv_val);
1294 MKERROR_PWD_MGMT(*errorp,
1295 LDAP_SUCCESS,
1296 NULL,
1297 pwd_status,
1298 sec_until_exp,
1300 exit_rc =
1301 NS_LDAP_SUCCESS_WITH_INFO;
1302 break;
1307 if (controls)
1308 ldap_controls_free(controls);
1310 return (exit_rc);
1313 static int
1314 ldap_in_nss_switch(char *db)
1316 enum __nsw_parse_err pserr;
1317 struct __nsw_switchconfig *conf;
1318 struct __nsw_lookup *lkp;
1319 const char *name;
1320 int found = 0;
1322 conf = __nsw_getconfig(db, &pserr);
1323 if (conf == NULL) {
1324 return (-1);
1327 /* check for skip and count other backends */
1328 for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
1329 name = lkp->service_name;
1330 if (strcmp(name, "ldap") == 0) {
1331 found = 1;
1332 break;
1335 (void) __nsw_freeconfig(conf);
1336 return (found);
1339 static int
1340 openConnection(LDAP **ldp, const char *serverAddr, const ns_cred_t *auth,
1341 int timeoutSec, ns_ldap_error_t **errorp,
1342 int fail_if_new_pwd_reqd, int passwd_mgmt,
1343 ns_conn_user_t *conn_user, int flags)
1345 LDAP *ld = NULL;
1346 int ldapVersion = LDAP_VERSION3;
1347 int derefOption = LDAP_DEREF_ALWAYS;
1348 int zero = 0;
1349 int timeoutMilliSec = timeoutSec * 1000;
1350 uint16_t port = USE_DEFAULT_PORT;
1351 char *s;
1352 char errstr[MAXERROR];
1353 int followRef;
1355 ns_ldap_return_code ret_code = NS_LDAP_SUCCESS;
1357 *errorp = NULL;
1358 *ldp = NULL;
1360 /* determine if the host name contains a port number */
1361 s = strchr(serverAddr, ']'); /* skip over ipv6 addr */
1362 s = strchr(s != NULL ? s : serverAddr, ':');
1363 if (s != NULL) {
1364 if (sscanf(s + 1, "%hu", &port) != 1) {
1365 (void) snprintf(errstr,
1366 sizeof (errstr),
1367 gettext("openConnection: cannot "
1368 "convert %s into a valid "
1369 "port number for the "
1370 "%s server. A default value "
1371 "will be used."),
1373 serverAddr);
1374 syslog(LOG_ERR, "libsldap: %s", errstr);
1375 } else {
1376 *s = '\0';
1380 ret_code = createSession(auth,
1381 serverAddr,
1382 port,
1383 timeoutMilliSec,
1384 &ld,
1385 errorp);
1386 if (s != NULL) {
1387 *s = ':';
1389 if (ret_code != NS_LDAP_SUCCESS) {
1390 return (ret_code);
1393 /* check to see if the underlying libsldap supports MT connection */
1394 if (conn_user != NULL) {
1395 int rc;
1397 rc = __s_api_check_libldap_MT_conn_support(conn_user, ld,
1398 errorp);
1399 if (rc != NS_LDAP_SUCCESS) {
1400 (void) ldap_unbind(ld);
1401 return (rc);
1405 (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
1406 (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
1408 * This library will handle the referral itself based on API flags or
1409 * configuration file specification. The LDAP bind operation is an
1410 * exception where we rely on the LDAP library to follow the referal.
1412 * The LDAP follow referral option must be set to OFF for the libldap5
1413 * to pass the referral info up to this library. This option MUST be
1414 * set to OFF after we have performed a sucessful bind. If we are not
1415 * to follow referrals we MUST also set the LDAP follow referral option
1416 * to OFF before we perform an LDAP bind.
1418 ret_code = __s_api_toFollowReferrals(flags, &followRef, errorp);
1419 if (ret_code != NS_LDAP_SUCCESS) {
1420 (void) ldap_unbind(ld);
1421 return (ret_code);
1424 if (followRef)
1425 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
1426 else
1427 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1429 (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
1430 (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
1431 /* setup TCP/IP connect timeout */
1432 (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
1433 &timeoutMilliSec);
1434 /* retry if LDAP I/O was interrupted */
1435 (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
1437 ret_code = performBind(auth,
1439 timeoutSec,
1440 errorp,
1441 fail_if_new_pwd_reqd,
1442 passwd_mgmt);
1444 if (ret_code == NS_LDAP_SUCCESS ||
1445 ret_code == NS_LDAP_SUCCESS_WITH_INFO) {
1447 * Turn off LDAP referral following so that this library can
1448 * process referrals.
1450 (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1451 *ldp = ld;
1454 return (ret_code);
1458 * FUNCTION: __s_api_getDefaultAuth
1460 * Constructs a credential for authentication using the config module.
1462 * RETURN VALUES:
1464 * NS_LDAP_SUCCESS If successful
1465 * NS_LDAP_CONFIG If there are any config errors.
1466 * NS_LDAP_MEMORY Memory errors.
1467 * NS_LDAP_OP_FAILED If there are no more authentication methods so can
1468 * not build a new authp.
1469 * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
1470 * necessary fields of a cred for a given auth method
1471 * are not provided.
1472 * INPUT:
1474 * cLevel Currently requested credential level to be tried
1476 * aMethod Currently requested authentication method to be tried
1478 * getAdmin If non 0, get Admin -i.e., not proxyAgent- DN and password
1480 * OUTPUT:
1482 * authp authentication method to use.
1484 static int
1485 __s_api_getDefaultAuth(
1486 int *cLevel,
1487 ns_auth_t *aMethod,
1488 ns_cred_t **authp,
1489 int getAdmin)
1491 void **paramVal = NULL;
1492 char *modparamVal = NULL;
1493 int getUid = 0;
1494 int getPasswd = 0;
1495 int getCertpath = 0;
1496 int rc = 0;
1497 ns_ldap_error_t *errorp = NULL;
1498 UnixCred_t *AdminCred = NULL;
1500 #ifdef DEBUG
1501 (void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
1502 #endif
1504 if (aMethod == NULL) {
1505 /* Require an Auth */
1506 return (NS_LDAP_INVALID_PARAM);
1510 * credential level "self" can work with auth method sasl/GSSAPI only
1512 if (cLevel && *cLevel == NS_LDAP_CRED_SELF &&
1513 aMethod->saslmech != NS_LDAP_SASL_GSSAPI)
1514 return (NS_LDAP_INVALID_PARAM);
1516 *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
1517 if ((*authp) == NULL)
1518 return (NS_LDAP_MEMORY);
1520 (*authp)->auth = *aMethod;
1522 switch (aMethod->type) {
1523 case NS_LDAP_AUTH_NONE:
1524 return (NS_LDAP_SUCCESS);
1525 case NS_LDAP_AUTH_SIMPLE:
1526 getUid++;
1527 getPasswd++;
1528 break;
1529 case NS_LDAP_AUTH_SASL:
1530 if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
1531 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
1532 getUid++;
1533 getPasswd++;
1534 } else if (aMethod->saslmech != NS_LDAP_SASL_GSSAPI) {
1535 (void) __ns_ldap_freeCred(authp);
1536 return (NS_LDAP_INVALID_PARAM);
1538 break;
1539 case NS_LDAP_AUTH_TLS:
1540 if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
1541 ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
1542 ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
1543 (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
1544 getUid++;
1545 getPasswd++;
1546 getCertpath++;
1547 } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
1548 getCertpath++;
1549 } else {
1550 (void) __ns_ldap_freeCred(authp);
1551 return (NS_LDAP_INVALID_PARAM);
1553 break;
1556 if (getUid) {
1557 paramVal = NULL;
1558 if (getAdmin) {
1560 * Assume AdminCred has been retrieved from
1561 * ldap_cachemgr already. It will not work
1562 * without userID or password. Flags getUid
1563 * and getPasswd should always be set
1564 * together.
1566 AdminCred = calloc(1, sizeof (UnixCred_t));
1567 if (AdminCred == NULL) {
1568 (void) __ns_ldap_freeCred(authp);
1569 return (NS_LDAP_MEMORY);
1572 rc = requestAdminCred(&AdminCred, &errorp);
1573 if (rc != NS_LDAP_SUCCESS) {
1574 (void) __ns_ldap_freeCred(authp);
1575 (void) __ns_ldap_freeUnixCred(&AdminCred);
1576 (void) __ns_ldap_freeError(&errorp);
1577 return (rc);
1580 if (AdminCred->userID == NULL) {
1581 (void) __ns_ldap_freeCred(authp);
1582 (void) __ns_ldap_freeUnixCred(&AdminCred);
1583 return (NS_LDAP_INVALID_PARAM);
1585 (*authp)->cred.unix_cred.userID = AdminCred->userID;
1586 AdminCred->userID = NULL;
1587 } else {
1588 rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
1589 &paramVal, &errorp);
1590 if (rc != NS_LDAP_SUCCESS) {
1591 (void) __ns_ldap_freeCred(authp);
1592 (void) __ns_ldap_freeError(&errorp);
1593 return (rc);
1596 if (paramVal == NULL || *paramVal == NULL) {
1597 (void) __ns_ldap_freeCred(authp);
1598 return (NS_LDAP_INVALID_PARAM);
1601 (*authp)->cred.unix_cred.userID =
1602 strdup((char *)*paramVal);
1603 (void) __ns_ldap_freeParam(&paramVal);
1605 if ((*authp)->cred.unix_cred.userID == NULL) {
1606 (void) __ns_ldap_freeCred(authp);
1607 (void) __ns_ldap_freeUnixCred(&AdminCred);
1608 return (NS_LDAP_MEMORY);
1611 if (getPasswd) {
1612 paramVal = NULL;
1613 if (getAdmin) {
1615 * Assume AdminCred has been retrieved from
1616 * ldap_cachemgr already. It will not work
1617 * without the userID anyway because for
1618 * getting admin credential, flags getUid
1619 * and getPasswd should always be set
1620 * together.
1622 if (AdminCred == NULL || AdminCred->passwd == NULL) {
1623 (void) __ns_ldap_freeCred(authp);
1624 (void) __ns_ldap_freeUnixCred(&AdminCred);
1625 return (NS_LDAP_INVALID_PARAM);
1627 modparamVal = dvalue(AdminCred->passwd);
1628 } else {
1629 rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
1630 &paramVal, &errorp);
1631 if (rc != NS_LDAP_SUCCESS) {
1632 (void) __ns_ldap_freeCred(authp);
1633 (void) __ns_ldap_freeError(&errorp);
1634 return (rc);
1637 if (paramVal == NULL || *paramVal == NULL) {
1638 (void) __ns_ldap_freeCred(authp);
1639 return (NS_LDAP_INVALID_PARAM);
1642 modparamVal = dvalue((char *)*paramVal);
1643 (void) __ns_ldap_freeParam(&paramVal);
1646 if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
1647 (void) __ns_ldap_freeCred(authp);
1648 (void) __ns_ldap_freeUnixCred(&AdminCred);
1649 free(modparamVal);
1650 return (NS_LDAP_INVALID_PARAM);
1653 (*authp)->cred.unix_cred.passwd = modparamVal;
1655 if (getCertpath) {
1656 paramVal = NULL;
1657 if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
1658 &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
1659 (void) __ns_ldap_freeCred(authp);
1660 (void) __ns_ldap_freeUnixCred(&AdminCred);
1661 (void) __ns_ldap_freeError(&errorp);
1662 *authp = NULL;
1663 return (rc);
1666 if (paramVal == NULL || *paramVal == NULL) {
1667 (void) __ns_ldap_freeCred(authp);
1668 (void) __ns_ldap_freeUnixCred(&AdminCred);
1669 *authp = NULL;
1670 return (NS_LDAP_INVALID_PARAM);
1673 (*authp)->hostcertpath = strdup((char *)*paramVal);
1674 (void) __ns_ldap_freeParam(&paramVal);
1675 if ((*authp)->hostcertpath == NULL) {
1676 (void) __ns_ldap_freeCred(authp);
1677 (void) __ns_ldap_freeUnixCred(&AdminCred);
1678 *authp = NULL;
1679 return (NS_LDAP_MEMORY);
1682 (void) __ns_ldap_freeUnixCred(&AdminCred);
1683 return (NS_LDAP_SUCCESS);
1687 * FUNCTION: getConnection
1689 * internal version of __s_api_getConnection()
1691 static int
1692 getConnection(
1693 const char *server,
1694 const int flags,
1695 const ns_cred_t *cred, /* credentials for bind */
1696 ConnectionID *sessionId,
1697 Connection **session,
1698 ns_ldap_error_t **errorp,
1699 int fail_if_new_pwd_reqd,
1700 int nopasswd_acct_mgmt,
1701 ns_conn_user_t *conn_user)
1703 char errmsg[MAXERROR];
1704 ns_auth_t **aMethod = NULL;
1705 ns_auth_t **aNext = NULL;
1706 int **cLevel = NULL;
1707 int **cNext = NULL;
1708 int timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
1709 int rc;
1710 Connection *con = NULL;
1711 int sec = 1;
1712 ns_cred_t *authp = NULL;
1713 ns_cred_t anon;
1714 int version = NS_LDAP_V2, self_gssapi_only = 0;
1715 void **paramVal = NULL;
1716 char **badSrvrs = NULL; /* List of problem hostnames */
1718 if ((session == NULL) || (sessionId == NULL)) {
1719 return (NS_LDAP_INVALID_PARAM);
1721 *session = NULL;
1723 /* reuse MT connection if needed and if available */
1724 if (conn_user != NULL) {
1725 rc = __s_api_conn_mt_get(server, flags, cred, session, errorp,
1726 conn_user);
1727 if (rc != NS_LDAP_NOTFOUND)
1728 return (rc);
1731 /* get profile version number */
1732 if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
1733 &paramVal, errorp)) != NS_LDAP_SUCCESS)
1734 return (rc);
1735 if (paramVal == NULL) {
1736 (void) sprintf(errmsg, gettext("getConnection: no file "
1737 "version"));
1738 MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
1739 NS_LDAP_CONFIG);
1740 return (NS_LDAP_CONFIG);
1742 if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
1743 version = NS_LDAP_V1;
1744 (void) __ns_ldap_freeParam((void ***)&paramVal);
1746 /* Get the bind timeout value */
1747 (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
1748 if (paramVal != NULL && *paramVal != NULL) {
1749 timeoutSec = **((int **)paramVal);
1750 (void) __ns_ldap_freeParam(&paramVal);
1752 if (*errorp)
1753 (void) __ns_ldap_freeError(errorp);
1755 if (cred == NULL) {
1756 /* Get the authentication method list */
1757 if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
1758 (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
1759 return (rc);
1760 if (aMethod == NULL) {
1761 aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
1762 if (aMethod == NULL)
1763 return (NS_LDAP_MEMORY);
1764 aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
1765 if (aMethod[0] == NULL) {
1766 free(aMethod);
1767 return (NS_LDAP_MEMORY);
1769 if (version == NS_LDAP_V1)
1770 (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
1771 else {
1772 (aMethod[0])->type = NS_LDAP_AUTH_SASL;
1773 (aMethod[0])->saslmech =
1774 NS_LDAP_SASL_DIGEST_MD5;
1775 (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
1779 /* Get the credential level list */
1780 if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
1781 (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
1782 (void) __ns_ldap_freeParam((void ***)&aMethod);
1783 return (rc);
1785 if (cLevel == NULL) {
1786 cLevel = (int **)calloc(2, sizeof (int *));
1787 if (cLevel == NULL)
1788 return (NS_LDAP_MEMORY);
1789 cLevel[0] = (int *)calloc(1, sizeof (int));
1790 if (cLevel[0] == NULL)
1791 return (NS_LDAP_MEMORY);
1792 if (version == NS_LDAP_V1)
1793 *(cLevel[0]) = NS_LDAP_CRED_PROXY;
1794 else
1795 *(cLevel[0]) = NS_LDAP_CRED_ANON;
1799 /* setup the anon credential for anonymous connection */
1800 (void) memset(&anon, 0, sizeof (ns_cred_t));
1801 anon.auth.type = NS_LDAP_AUTH_NONE;
1803 for (;;) {
1804 if (cred != NULL) {
1805 /* using specified auth method */
1806 rc = makeConnection(&con, server, cred,
1807 sessionId, timeoutSec, errorp,
1808 fail_if_new_pwd_reqd,
1809 nopasswd_acct_mgmt, flags, &badSrvrs, conn_user);
1810 /* not using bad server if credentials were supplied */
1811 if (badSrvrs && *badSrvrs) {
1812 __s_api_free2dArray(badSrvrs);
1813 badSrvrs = NULL;
1815 if (rc == NS_LDAP_SUCCESS ||
1816 rc == NS_LDAP_SUCCESS_WITH_INFO) {
1817 *session = con;
1818 break;
1820 } else {
1821 self_gssapi_only = __s_api_self_gssapi_only_get();
1822 /* for every cred level */
1823 for (cNext = cLevel; *cNext != NULL; cNext++) {
1824 if (self_gssapi_only &&
1825 **cNext != NS_LDAP_CRED_SELF)
1826 continue;
1827 if (**cNext == NS_LDAP_CRED_ANON) {
1829 * make connection anonymously
1830 * Free the down server list before
1831 * looping through
1833 if (badSrvrs && *badSrvrs) {
1834 __s_api_free2dArray(badSrvrs);
1835 badSrvrs = NULL;
1837 rc = makeConnection(&con, server, &anon,
1838 sessionId, timeoutSec, errorp,
1839 fail_if_new_pwd_reqd,
1840 nopasswd_acct_mgmt, flags,
1841 &badSrvrs, conn_user);
1842 if (rc == NS_LDAP_SUCCESS ||
1843 rc ==
1844 NS_LDAP_SUCCESS_WITH_INFO) {
1845 *session = con;
1846 goto done;
1848 continue;
1850 /* for each cred level */
1851 for (aNext = aMethod; *aNext != NULL; aNext++) {
1852 if (self_gssapi_only &&
1853 (*aNext)->saslmech !=
1854 NS_LDAP_SASL_GSSAPI)
1855 continue;
1857 * self coexists with sasl/GSSAPI only
1858 * and non-self coexists with non-gssapi
1859 * only
1861 if ((**cNext == NS_LDAP_CRED_SELF &&
1862 (*aNext)->saslmech !=
1863 NS_LDAP_SASL_GSSAPI) ||
1864 (**cNext != NS_LDAP_CRED_SELF &&
1865 (*aNext)->saslmech ==
1866 NS_LDAP_SASL_GSSAPI))
1867 continue;
1868 /* make connection and authenticate */
1869 /* with default credentials */
1870 authp = NULL;
1871 rc = __s_api_getDefaultAuth(*cNext,
1872 *aNext, &authp,
1873 flags & NS_LDAP_READ_SHADOW);
1874 if (rc != NS_LDAP_SUCCESS) {
1875 continue;
1878 * Free the down server list before
1879 * looping through
1881 if (badSrvrs && *badSrvrs) {
1882 __s_api_free2dArray(badSrvrs);
1883 badSrvrs = NULL;
1885 rc = makeConnection(&con, server, authp,
1886 sessionId, timeoutSec, errorp,
1887 fail_if_new_pwd_reqd,
1888 nopasswd_acct_mgmt, flags,
1889 &badSrvrs, conn_user);
1890 (void) __ns_ldap_freeCred(&authp);
1891 if (rc == NS_LDAP_SUCCESS ||
1892 rc ==
1893 NS_LDAP_SUCCESS_WITH_INFO) {
1894 *session = con;
1895 goto done;
1900 if (flags & NS_LDAP_HARD) {
1901 if (sec < LDAPMAXHARDLOOKUPTIME)
1902 sec *= 2;
1903 (void) sleep(sec);
1904 } else {
1905 break;
1909 done:
1910 if (self_gssapi_only && rc == NS_LDAP_SUCCESS && *session == NULL) {
1912 * self_gssapi_only is true but no self/sasl/gssapi is
1913 * configured
1915 rc = NS_LDAP_CONFIG;
1918 (void) __ns_ldap_freeParam((void ***)&aMethod);
1919 (void) __ns_ldap_freeParam((void ***)&cLevel);
1921 if (badSrvrs && *badSrvrs) {
1923 * At this point, either we have a successful
1924 * connection or exhausted all the possible auths.
1925 * and creds. Mark the problem servers as down
1926 * so that the problem servers are not contacted
1927 * again until the refresh_ttl expires.
1929 (void) __s_api_removeBadServers(badSrvrs);
1930 __s_api_free2dArray(badSrvrs);
1932 return (rc);
1936 * FUNCTION: __s_api_getConnection
1938 * Bind to the specified server or one from the server
1939 * list and return the pointer.
1941 * This function can rebind or not (NS_LDAP_HARD), it can require a
1942 * credential or bind anonymously
1944 * This function follows the DUA configuration schema algorithm
1946 * RETURN VALUES:
1948 * NS_LDAP_SUCCESS A connection was made successfully.
1949 * NS_LDAP_SUCCESS_WITH_INFO
1950 * A connection was made successfully, but with
1951 * password management info in *errorp
1952 * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
1953 * NS_LDAP_CONFIG If there are any config errors.
1954 * NS_LDAP_MEMORY Memory errors.
1955 * NS_LDAP_INTERNAL If there was a ldap error.
1957 * INPUT:
1959 * server Bind to this LDAP server only
1960 * flags If NS_LDAP_HARD is set function will not return until it has
1961 * a connection unless there is a authentication problem.
1962 * If NS_LDAP_NEW_CONN is set the function must force a new
1963 * connection to be created
1964 * If NS_LDAP_KEEP_CONN is set the connection is to be kept open
1965 * auth Credentials for bind. This could be NULL in which case
1966 * a default cred built from the config module is used.
1967 * sessionId cookie that points to a previous session
1968 * fail_if_new_pwd_reqd
1969 * a flag indicating this function should fail if the passwd
1970 * in auth needs to change immediately
1971 * nopasswd_acct_mgmt
1972 * a flag indicating that makeConnection should check before
1973 * binding if server supports LDAP V3 password less
1974 * account management
1976 * OUTPUT:
1978 * session pointer to a session with connection information
1979 * errorp Set if there are any INTERNAL, or CONFIG error.
1982 __s_api_getConnection(
1983 const char *server,
1984 const int flags,
1985 const ns_cred_t *cred, /* credentials for bind */
1986 ConnectionID *sessionId,
1987 Connection **session,
1988 ns_ldap_error_t **errorp,
1989 int fail_if_new_pwd_reqd,
1990 int nopasswd_acct_mgmt,
1991 ns_conn_user_t *conn_user)
1993 int rc;
1995 rc = getConnection(server, flags, cred, sessionId, session,
1996 errorp, fail_if_new_pwd_reqd, nopasswd_acct_mgmt,
1997 conn_user);
1999 if (rc != NS_LDAP_SUCCESS && rc != NS_LDAP_SUCCESS_WITH_INFO) {
2000 if (conn_user != NULL && conn_user->conn_mt != NULL)
2001 __s_api_conn_mt_remove(conn_user, rc, errorp);
2004 return (rc);
2007 void
2008 __s_api_free_sessionPool()
2010 int id;
2012 (void) mutex_lock(&sessionPoolLock);
2014 if (sessionPool != NULL) {
2015 for (id = 0; id < sessionPoolSize; id++)
2016 _DropConnection(id + CONID_OFFSET, 0, 1);
2017 free(sessionPool);
2018 sessionPool = NULL;
2019 sessionPoolSize = 0;
2021 (void) mutex_unlock(&sessionPoolLock);
2025 * This function initializes a TLS LDAP session. On success LDAP* is returned
2026 * (pointed by *ldp). Otherwise, the function returns an NS error code and
2027 * provide an additional info pointed by *errorp.
2029 static
2030 ns_ldap_return_code
2031 createTLSSession(const ns_cred_t *auth, const char *serverAddr,
2032 uint16_t port, int timeoutMilliSec,
2033 LDAP **ldp, ns_ldap_error_t **errorp)
2035 const char *hostcertpath;
2036 char *alloc_hcp = NULL, errstr[MAXERROR];
2037 int ldap_rc;
2039 #ifdef DEBUG
2040 (void) fprintf(stderr, "tid= %d: +++TLS transport\n",
2041 thr_self());
2042 #endif /* DEBUG */
2044 if (prldap_set_session_option(NULL, NULL,
2045 PRLDAP_OPT_IO_MAX_TIMEOUT,
2046 timeoutMilliSec) != LDAP_SUCCESS) {
2047 (void) snprintf(errstr, sizeof (errstr),
2048 gettext("createTLSSession: failed to initialize "
2049 "TLS security"));
2050 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2051 strdup(errstr), NS_LDAP_MEMORY);
2052 return (NS_LDAP_INTERNAL);
2055 hostcertpath = auth->hostcertpath;
2056 if (hostcertpath == NULL) {
2057 alloc_hcp = __s_get_hostcertpath();
2058 hostcertpath = alloc_hcp;
2061 if (hostcertpath == NULL)
2062 return (NS_LDAP_MEMORY);
2064 if ((ldap_rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
2065 if (alloc_hcp != NULL) {
2066 free(alloc_hcp);
2068 (void) snprintf(errstr, sizeof (errstr),
2069 gettext("createTLSSession: failed to initialize "
2070 "TLS security (%s)"),
2071 ldapssl_err2string(ldap_rc));
2072 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2073 strdup(errstr), NS_LDAP_MEMORY);
2074 return (NS_LDAP_INTERNAL);
2076 free(alloc_hcp);
2078 *ldp = ldapssl_init(serverAddr, port, 1);
2080 if (*ldp == NULL ||
2081 ldapssl_install_gethostbyaddr(*ldp, "ldap") != 0) {
2082 (void) snprintf(errstr, sizeof (errstr),
2083 gettext("createTLSSession: failed to connect "
2084 "using TLS (%s)"), strerror(errno));
2085 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2086 strdup(errstr), NS_LDAP_MEMORY);
2087 return (NS_LDAP_INTERNAL);
2090 return (NS_LDAP_SUCCESS);
2094 * Convert (resolve) hostname to IP address.
2096 * INPUT:
2098 * server - \[IPv6_address\][:port]
2099 * - IPv4_address[:port]
2100 * - hostname[:port]
2102 * newaddr - Buffer to which this function writes resulting address,
2103 * including the port number, if specified in server argument.
2105 * newaddr_size - Size of the newaddr buffer.
2107 * errstr - Buffer to which error string is written if error occurs.
2109 * errstr_size - Size of the errstr buffer.
2111 * OUTPUT:
2113 * Returns 1 for success, 0 in case of error.
2115 * newaddr - See above (INPUT section).
2117 * errstr - See above (INPUT section).
2119 static int
2120 cvt_hostname2ip(char *server, char *newaddr, int newaddr_size,
2121 char *errstr, int errstr_size)
2123 char *s;
2124 unsigned short port = 0;
2125 int err;
2126 char buffer[NSS_BUFLEN_HOSTS];
2127 struct hostent result;
2129 /* Determine if the host name contains a port number. */
2131 /* Skip over IPv6 address. */
2132 s = strchr(server, ']');
2133 s = strchr(s != NULL ? s : server, ':');
2134 if (s != NULL) {
2135 if (sscanf(s + 1, "%hu", &port) != 1) {
2136 /* Address misformatted. No port number after : */
2137 (void) snprintf(errstr, errstr_size, "%s",
2138 gettext("Invalid host:port format"));
2139 return (0);
2140 } else
2141 /* Cut off the :<port> part. */
2142 *s = '\0';
2145 buffer[0] = '\0';
2147 * Resolve hostname and fill in hostent structure.
2149 if (!__s_api_hostname2ip(server, &result, buffer, NSS_BUFLEN_HOSTS,
2150 &err)) {
2152 * The only possible error here could be TRY_AGAIN if buffer was
2153 * not big enough. NSS_BUFLEN_HOSTS should have been enough
2154 * though.
2156 (void) snprintf(errstr, errstr_size, "%s",
2157 gettext("Unable to resolve address."));
2158 return (0);
2162 buffer[0] = '\0';
2164 * Convert the address to string.
2166 if (!inet_ntop(result.h_addrtype, result.h_addr_list[0], buffer,
2167 NSS_BUFLEN_HOSTS)) {
2168 /* There's not much we can do. */
2169 (void) snprintf(errstr, errstr_size, "%s",
2170 gettext("Unable to convert address to string."));
2171 return (0);
2174 /* Put together the address and the port */
2175 if (port > 0) {
2176 switch (result.h_addrtype) {
2177 case AF_INET6:
2178 (void) snprintf(newaddr,
2179 /* [IP]:<port>\0 */
2180 1 + strlen(buffer) + 1 + 1 + 5 + 1,
2181 "[%s]:%hu",
2182 buffer,
2183 port);
2184 break;
2185 /* AF_INET */
2186 default :
2187 (void) snprintf(newaddr,
2188 /* IP:<port>\0 */
2189 strlen(buffer) + 1 + 5 + 1,
2190 "%s:%hu",
2191 buffer,
2192 port);
2193 break;
2195 } else {
2196 (void) strncpy(newaddr, buffer, newaddr_size);
2199 return (1);
2204 * This finction initializes a none-TLS LDAP session. On success LDAP*
2205 * is returned (pointed by *ldp). Otherwise, the function returns
2206 * an NS error code and provides an additional info pointed by *errorp.
2208 static
2209 ns_ldap_return_code
2210 createNonTLSSession(const char *serverAddr,
2211 uint16_t port, int gssapi,
2212 LDAP **ldp, ns_ldap_error_t **errorp)
2214 char errstr[MAXERROR];
2215 char *addr;
2216 int is_ip = 0;
2217 /* [INET6_ADDRSTRLEN]:<port>\0 */
2218 char svraddr[1+INET6_ADDRSTRLEN+1+1+5+1];
2219 #ifdef DEBUG
2220 (void) fprintf(stderr, "tid= %d: +++Unsecure transport\n",
2221 thr_self());
2222 #endif /* DEBUG */
2224 if (gssapi == 0) {
2225 is_ip = (__s_api_isipv4((char *)serverAddr) ||
2226 __s_api_isipv6((char *)serverAddr));
2230 * Let's try to resolve IP address of server.
2232 if (is_ip == 0 && !gssapi && (ldap_in_nss_switch((char *)"hosts") > 0 ||
2233 ldap_in_nss_switch((char *)"ipnodes") > 0)) {
2234 addr = strdup(serverAddr);
2235 if (addr == NULL)
2236 return (NS_LDAP_MEMORY);
2237 svraddr[0] = '\0';
2238 if (cvt_hostname2ip(addr, svraddr, sizeof (svraddr),
2239 errstr, MAXERROR) == 1) {
2240 serverAddr = svraddr;
2241 free(addr);
2242 } else {
2243 free(addr);
2244 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2245 strdup(errstr), NS_LDAP_MEMORY);
2246 return (NS_LDAP_INTERNAL);
2250 /* Warning message IF cannot connect to host(s) */
2251 if ((*ldp = ldap_init((char *)serverAddr, port)) == NULL) {
2252 char *p = strerror(errno);
2253 MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
2254 strdup(p), NS_LDAP_MEMORY);
2255 return (NS_LDAP_INTERNAL);
2258 return (NS_LDAP_SUCCESS);
2262 * This finction initializes an LDAP session.
2264 * INPUT:
2265 * auth - a structure specified an authenticastion method and credentials,
2266 * serverAddr - the address of a server to which a connection
2267 * will be established,
2268 * port - a port being listened by the server,
2269 * timeoutMilliSec - a timeout in milliseconds for the Bind operation.
2271 * OUTPUT:
2272 * ldp - a pointer to an LDAP structure which will be used
2273 * for all the subsequent operations against the server.
2274 * If an error occurs, the function returns an NS error code
2275 * and provides an additional info pointed by *errorp.
2277 static
2278 ns_ldap_return_code
2279 createSession(const ns_cred_t *auth, const char *serverAddr,
2280 uint16_t port, int timeoutMilliSec,
2281 LDAP **ldp, ns_ldap_error_t **errorp)
2283 int useSSL = 0, gssapi = 0;
2284 char errstr[MAXERROR];
2286 switch (auth->auth.type) {
2287 case NS_LDAP_AUTH_NONE:
2288 case NS_LDAP_AUTH_SIMPLE:
2289 case NS_LDAP_AUTH_SASL:
2290 break;
2291 case NS_LDAP_AUTH_TLS:
2292 useSSL = 1;
2293 break;
2294 default:
2295 (void) sprintf(errstr,
2296 gettext("openConnection: unsupported "
2297 "authentication method (%d)"), auth->auth.type);
2298 MKERROR(LOG_WARNING, *errorp,
2299 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2300 NS_LDAP_MEMORY);
2301 return (NS_LDAP_INTERNAL);
2304 if (port == USE_DEFAULT_PORT) {
2305 port = useSSL ? LDAPS_PORT : LDAP_PORT;
2308 if (auth->auth.type == NS_LDAP_AUTH_SASL &&
2309 auth->auth.saslmech == NS_LDAP_SASL_GSSAPI)
2310 gssapi = 1;
2312 if (useSSL)
2313 return (createTLSSession(auth, serverAddr, port,
2314 timeoutMilliSec, ldp, errorp));
2315 else
2316 return (createNonTLSSession(serverAddr, port, gssapi,
2317 ldp, errorp));
2321 * This finction performs a non-SASL bind operation. If an error accures,
2322 * the function returns an NS error code and provides an additional info
2323 * pointed by *errorp.
2325 static
2326 ns_ldap_return_code
2327 doSimpleBind(const ns_cred_t *auth,
2328 LDAP *ld,
2329 int timeoutSec,
2330 ns_ldap_error_t **errorp,
2331 int fail_if_new_pwd_reqd,
2332 int passwd_mgmt)
2334 char *binddn, *passwd, errstr[MAXERROR], *errmsg;
2335 int msgId, errnum = 0, ldap_rc;
2336 ns_ldap_return_code ret_code;
2337 LDAPMessage *resultMsg = NULL;
2338 LDAPControl **controls;
2339 struct timeval tv;
2341 binddn = auth->cred.unix_cred.userID;
2342 passwd = auth->cred.unix_cred.passwd;
2343 if (passwd == NULL || *passwd == '\0' ||
2344 binddn == NULL || *binddn == '\0') {
2345 (void) sprintf(errstr, gettext("openConnection: "
2346 "missing credentials for Simple bind"));
2347 MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
2348 strdup(errstr), NS_LDAP_MEMORY);
2349 (void) ldap_unbind(ld);
2350 return (NS_LDAP_INTERNAL);
2353 #ifdef DEBUG
2354 (void) fprintf(stderr, "tid= %d: +++Simple bind\n",
2355 thr_self());
2356 #endif /* DEBUG */
2357 msgId = ldap_simple_bind(ld, binddn, passwd);
2359 if (msgId == -1) {
2360 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
2361 (void *)&errnum);
2362 (void) snprintf(errstr, sizeof (errstr),
2363 gettext("openConnection: simple bind failed "
2364 "- %s"), ldap_err2string(errnum));
2365 (void) ldap_unbind(ld);
2366 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
2367 NS_LDAP_MEMORY);
2368 return (NS_LDAP_INTERNAL);
2371 tv.tv_sec = timeoutSec;
2372 tv.tv_usec = 0;
2373 ldap_rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
2375 if ((ldap_rc == -1) || (ldap_rc == 0)) {
2376 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
2377 (void *)&errnum);
2378 (void) snprintf(errstr, sizeof (errstr),
2379 gettext("openConnection: simple bind failed "
2380 "- %s"), ldap_err2string(errnum));
2381 (void) ldap_msgfree(resultMsg);
2382 (void) ldap_unbind(ld);
2383 MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
2384 NS_LDAP_MEMORY);
2385 return (NS_LDAP_INTERNAL);
2389 * get ldaprc, controls, and error msg
2391 ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2392 &errmsg, NULL, &controls, 1);
2394 if (ldap_rc != LDAP_SUCCESS) {
2395 (void) snprintf(errstr, sizeof (errstr),
2396 gettext("openConnection: simple bind failed "
2397 "- unable to parse result"));
2398 (void) ldap_unbind(ld);
2399 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2400 strdup(errstr), NS_LDAP_MEMORY);
2401 return (NS_LDAP_INTERNAL);
2404 /* process the password management info, if any */
2405 ret_code = process_pwd_mgmt("simple",
2406 errnum, controls, errmsg,
2407 errorp,
2408 fail_if_new_pwd_reqd,
2409 passwd_mgmt);
2411 if (ret_code == NS_LDAP_INTERNAL) {
2412 (void) ldap_unbind(ld);
2415 return (ret_code);
2419 * This finction performs a SASL bind operation. If an error accures,
2420 * the function returns an NS error code and provides an additional info
2421 * pointed by *errorp.
2423 static
2424 ns_ldap_return_code
2425 doSASLBind(const ns_cred_t *auth,
2426 LDAP *ld,
2427 int timeoutSec,
2428 ns_ldap_error_t **errorp,
2429 int fail_if_new_pwd_reqd,
2430 int passwd_mgmt)
2432 char *binddn, *passwd, *digest_md5_name,
2433 errstr[MAXERROR], *errmsg;
2434 struct berval cred;
2435 int ldap_rc, errnum = 0;
2436 ns_ldap_return_code ret_code;
2437 struct timeval tv;
2438 LDAPMessage *resultMsg;
2439 LDAPControl **controls;
2440 int min_ssf = MIN_SASL_SSF, max_ssf = MAX_SASL_SSF;
2441 ns_sasl_cb_param_t sasl_param;
2443 if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE &&
2444 auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2445 (void) sprintf(errstr,
2446 gettext("openConnection: SASL options are "
2447 "not supported (%d) for non-GSSAPI sasl bind"),
2448 auth->auth.saslopt);
2449 MKERROR(LOG_WARNING, *errorp,
2450 LDAP_AUTH_METHOD_NOT_SUPPORTED,
2451 strdup(errstr), NS_LDAP_MEMORY);
2452 (void) ldap_unbind(ld);
2453 return (NS_LDAP_INTERNAL);
2455 if (auth->auth.saslmech != NS_LDAP_SASL_GSSAPI) {
2456 binddn = auth->cred.unix_cred.userID;
2457 passwd = auth->cred.unix_cred.passwd;
2458 if (passwd == NULL || *passwd == '\0' ||
2459 binddn == NULL || *binddn == '\0') {
2460 (void) sprintf(errstr,
2461 gettext("openConnection: missing credentials "
2462 "for SASL bind"));
2463 MKERROR(LOG_WARNING, *errorp,
2464 LDAP_INVALID_CREDENTIALS,
2465 strdup(errstr), NS_LDAP_MEMORY);
2466 (void) ldap_unbind(ld);
2467 return (NS_LDAP_INTERNAL);
2469 cred.bv_val = passwd;
2470 cred.bv_len = strlen(passwd);
2473 ret_code = NS_LDAP_SUCCESS;
2475 switch (auth->auth.saslmech) {
2476 case NS_LDAP_SASL_CRAM_MD5:
2478 * NOTE: if iDS changes to support cram_md5,
2479 * please add password management code here.
2480 * Since ldap_sasl_cram_md5_bind_s does not
2481 * return anything that could be used to
2482 * extract the ldap rc/errmsg/control to
2483 * determine if bind failed due to password
2484 * policy, a new cram_md5_bind API will need
2485 * to be introduced. See
2486 * ldap_x_sasl_digest_md5_bind() and case
2487 * NS_LDAP_SASL_DIGEST_MD5 below for details.
2489 if ((ldap_rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
2490 &cred, NULL, NULL)) != LDAP_SUCCESS) {
2491 (void) ldap_get_option(ld,
2492 LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2493 (void) snprintf(errstr, sizeof (errstr),
2494 gettext("openConnection: "
2495 "sasl/CRAM-MD5 bind failed - %s"),
2496 ldap_err2string(errnum));
2497 MKERROR(LOG_WARNING, *errorp, errnum,
2498 strdup(errstr), NS_LDAP_MEMORY);
2499 (void) ldap_unbind(ld);
2500 return (NS_LDAP_INTERNAL);
2502 break;
2503 case NS_LDAP_SASL_DIGEST_MD5:
2504 digest_md5_name = malloc(strlen(binddn) + 5);
2505 /* 5 = strlen("dn: ") + 1 */
2506 if (digest_md5_name == NULL) {
2507 (void) ldap_unbind(ld);
2508 return (NS_LDAP_MEMORY);
2510 (void) strcpy(digest_md5_name, "dn: ");
2511 (void) strcat(digest_md5_name, binddn);
2513 tv.tv_sec = timeoutSec;
2514 tv.tv_usec = 0;
2515 ldap_rc = ldap_x_sasl_digest_md5_bind(ld,
2516 digest_md5_name, &cred, NULL, NULL,
2517 &tv, &resultMsg);
2519 if (resultMsg == NULL) {
2520 free(digest_md5_name);
2521 (void) ldap_get_option(ld,
2522 LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
2523 (void) snprintf(errstr, sizeof (errstr),
2524 gettext("openConnection: "
2525 "DIGEST-MD5 bind failed - %s"),
2526 ldap_err2string(errnum));
2527 (void) ldap_unbind(ld);
2528 MKERROR(LOG_WARNING, *errorp, errnum,
2529 strdup(errstr), NS_LDAP_MEMORY);
2530 return (NS_LDAP_INTERNAL);
2534 * get ldaprc, controls, and error msg
2536 ldap_rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
2537 &errmsg, NULL, &controls, 1);
2539 if (ldap_rc != LDAP_SUCCESS) {
2540 free(digest_md5_name);
2541 (void) snprintf(errstr, sizeof (errstr),
2542 gettext("openConnection: "
2543 "DIGEST-MD5 bind failed "
2544 "- unable to parse result"));
2545 (void) ldap_unbind(ld);
2546 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2547 strdup(errstr), NS_LDAP_MEMORY);
2548 return (NS_LDAP_INTERNAL);
2551 /* process the password management info, if any */
2552 ret_code = process_pwd_mgmt("sasl/DIGEST-MD5",
2553 errnum, controls, errmsg,
2554 errorp,
2555 fail_if_new_pwd_reqd,
2556 passwd_mgmt);
2558 if (ret_code == NS_LDAP_INTERNAL) {
2559 (void) ldap_unbind(ld);
2562 free(digest_md5_name);
2563 break;
2564 case NS_LDAP_SASL_GSSAPI:
2565 (void) memset(&sasl_param, 0,
2566 sizeof (ns_sasl_cb_param_t));
2567 sasl_param.authid = NULL;
2568 sasl_param.authzid = "";
2569 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MIN,
2570 (void *)&min_ssf);
2571 (void) ldap_set_option(ld, LDAP_OPT_X_SASL_SSF_MAX,
2572 (void *)&max_ssf);
2574 ldap_rc = ldap_sasl_interactive_bind_s(
2575 ld, NULL, "GSSAPI",
2576 NULL, NULL, LDAP_SASL_INTERACTIVE,
2577 __s_api_sasl_bind_callback,
2578 &sasl_param);
2580 if (ldap_rc != LDAP_SUCCESS) {
2581 (void) snprintf(errstr, sizeof (errstr),
2582 gettext("openConnection: "
2583 "GSSAPI bind failed "
2584 "- %d %s"),
2585 ldap_rc,
2586 ldap_err2string(ldap_rc));
2587 (void) ldap_unbind(ld);
2588 MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
2589 strdup(errstr), NS_LDAP_MEMORY);
2590 return (NS_LDAP_INTERNAL);
2593 break;
2594 default:
2595 (void) ldap_unbind(ld);
2596 (void) sprintf(errstr,
2597 gettext("openConnection: unsupported SASL "
2598 "mechanism (%d)"), auth->auth.saslmech);
2599 MKERROR(LOG_WARNING, *errorp,
2600 LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
2601 NS_LDAP_MEMORY);
2602 return (NS_LDAP_INTERNAL);
2605 return (ret_code);
2609 * This function performs an LDAP Bind operation proceeding
2610 * from a type of the connection specified by auth->auth.type.
2612 * INPUT:
2613 * auth - a structure specified an authenticastion method and credentials,
2614 * ld - a pointer returned by the createSession() function,
2615 * timeoutSec - a timeout in seconds for the Bind operation,
2616 * fail_if_new_pwd_reqd - a flag indicating that the call should fail
2617 * if a new password is required,
2618 * passwd_mgmt - a flag indicating that the server supports
2619 * password management.
2621 * OUTPUT:
2622 * If an error accures, the function returns an NS error code
2623 * and provides an additional info pointed by *errorp.
2625 static
2626 ns_ldap_return_code
2627 performBind(const ns_cred_t *auth,
2628 LDAP *ld,
2629 int timeoutSec,
2630 ns_ldap_error_t **errorp,
2631 int fail_if_new_pwd_reqd,
2632 int passwd_mgmt)
2634 int bindType;
2635 char errstr[MAXERROR];
2637 ns_ldap_return_code (*binder)(const ns_cred_t *auth,
2638 LDAP *ld,
2639 int timeoutSec,
2640 ns_ldap_error_t **errorp,
2641 int fail_if_new_pwd_reqd,
2642 int passwd_mgmt) = NULL;
2644 if (!ld) {
2645 (void) sprintf(errstr,
2646 "performBind: LDAP session "
2647 "is not initialized.");
2648 MKERROR(LOG_WARNING, *errorp,
2649 LDAP_AUTH_METHOD_NOT_SUPPORTED,
2650 strdup(errstr), NS_LDAP_MEMORY);
2651 return (NS_LDAP_INTERNAL);
2654 bindType = auth->auth.type == NS_LDAP_AUTH_TLS ?
2655 auth->auth.tlstype : auth->auth.type;
2657 switch (bindType) {
2658 case NS_LDAP_AUTH_NONE:
2659 #ifdef DEBUG
2660 (void) fprintf(stderr, "tid= %d: +++Anonymous bind\n",
2661 thr_self());
2662 #endif /* DEBUG */
2663 break;
2664 case NS_LDAP_AUTH_SIMPLE:
2665 binder = doSimpleBind;
2666 break;
2667 case NS_LDAP_AUTH_SASL:
2668 binder = doSASLBind;
2669 break;
2670 default:
2671 (void) sprintf(errstr,
2672 gettext("openConnection: unsupported "
2673 "authentication method "
2674 "(%d)"), bindType);
2675 MKERROR(LOG_WARNING, *errorp,
2676 LDAP_AUTH_METHOD_NOT_SUPPORTED,
2677 strdup(errstr), NS_LDAP_MEMORY);
2678 (void) ldap_unbind(ld);
2679 return (NS_LDAP_INTERNAL);
2682 if (binder != NULL) {
2683 return (*binder)(auth,
2685 timeoutSec,
2686 errorp,
2687 fail_if_new_pwd_reqd,
2688 passwd_mgmt);
2691 return (NS_LDAP_SUCCESS);