summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsldap/common/ns_connect.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libsldap/common/ns_connect.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libsldap/common/ns_connect.c')
-rw-r--r--usr/src/lib/libsldap/common/ns_connect.c1935
1 files changed, 1935 insertions, 0 deletions
diff --git a/usr/src/lib/libsldap/common/ns_connect.c b/usr/src/lib/libsldap/common/ns_connect.c
new file mode 100644
index 0000000000..21abb05c77
--- /dev/null
+++ b/usr/src/lib/libsldap/common/ns_connect.c
@@ -0,0 +1,1935 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <synch.h>
+#include <time.h>
+#include <libintl.h>
+#include <thread.h>
+#include <syslog.h>
+#include <sys/mman.h>
+#include <nsswitch.h>
+#include <nss_dbdefs.h>
+#include "solaris-priv.h"
+#include "ns_sldap.h"
+#include "ns_internal.h"
+#include "ns_cache_door.h"
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <unistd.h>
+
+extern unsigned int _sleep(unsigned int);
+extern int ldap_sasl_cram_md5_bind_s(LDAP *, char *, struct berval *,
+ LDAPControl **, LDAPControl **);
+extern int ldapssl_install_gethostbyaddr(LDAP *ld, const char *skip);
+
+static int openConnection(LDAP **, const char *, const ns_cred_t *,
+ int, ns_ldap_error_t **, int, int);
+
+static mutex_t sessionPoolLock = DEFAULTMUTEX;
+
+static Connection **sessionPool = NULL;
+static int sessionPoolSize = 0;
+
+
+static mutex_t nscdLock = DEFAULTMUTEX;
+static int nscdChecked = 0;
+static pid_t checkedPid = -1;
+static int isNscd = 0;
+
+
+/*
+ * Check /proc/PID/psinfo to see if this process is nscd
+ * If it is, treat connection as NS_LDAP_KEEP_CONN, to reduce
+ * constant reconnects for many operations.
+ * A more complete solution is to develop true connection pooling.
+ * However, this is much better than a new connection for every request.
+ */
+static int
+nscd_proc()
+{
+ pid_t my_pid;
+ psinfo_t pinfo;
+ char fname[BUFSIZ];
+ int ret;
+ int fd;
+
+ /* Don't bother checking if this process isn't root. */
+ /* It can't be nscd */
+ if (getuid() != 0)
+ return (0);
+
+ my_pid = getpid();
+ if (nscdChecked && (my_pid == checkedPid)) {
+ return (isNscd);
+ }
+ (void) mutex_lock(&nscdLock);
+ if (nscdChecked && (my_pid == checkedPid)) {
+ (void) mutex_unlock(&nscdLock);
+ return (isNscd);
+ }
+ nscdChecked = 1;
+ checkedPid = my_pid;
+ isNscd = 0;
+ if (snprintf(fname, BUFSIZ, "/proc/%d/psinfo", my_pid) != 0) {
+ if ((fd = open(fname, O_RDONLY)) > 0) {
+ ret = read(fd, &pinfo, sizeof (psinfo_t));
+ (void) close(fd);
+ if (ret == sizeof (psinfo_t) &&
+ (strcmp(pinfo.pr_fname, "nscd") == 0)) {
+ /* process runs as root and is named nscd */
+ /* that's good enough for now */
+ isNscd = 1;
+ }
+ }
+ }
+ (void) mutex_unlock(&nscdLock);
+ return (isNscd);
+}
+
+/*
+ * This function requests a server from the cache manager through
+ * the door functionality
+ */
+
+static int
+__s_api_requestServer(const char *request, const char *server,
+ ns_server_info_t *ret, ns_ldap_error_t **error)
+{
+ union {
+ ldap_data_t s_d;
+ char s_b[DOORBUFFERSIZE];
+ } space;
+ ldap_data_t *sptr;
+ int ndata;
+ int adata;
+ char errstr[MAXERROR];
+ const char *ireq;
+ char *rbuf, *ptr, *rest;
+ char *dptr;
+ char **mptr, **mptr1, **cptr, **cptr1;
+ int mcnt, ccnt;
+ char **servers;
+ int rc;
+
+ if (ret == NULL || error == NULL) {
+ return (NS_LDAP_OP_FAILED);
+ }
+ (void) memset(ret, 0, sizeof (ns_server_info_t));
+ *error = NULL;
+
+ (void) memset(space.s_b, 0, DOORBUFFERSIZE);
+
+ if (request == NULL)
+ ireq = NS_CACHE_NEW;
+ else
+ ireq = request;
+
+ adata = (sizeof (ldap_call_t) + strlen(ireq) +1);
+ if (server != NULL) {
+ adata += strlen(DOORLINESEP) + 1;
+ adata += strlen(server) + 1;
+ }
+ ndata = sizeof (space);
+ space.s_d.ldap_call.ldap_callnumber = GETLDAPSERVER;
+ (void) strcpy(space.s_d.ldap_call.ldap_u.domainname, ireq);
+ if (server != NULL) {
+ (void) strcat(space.s_d.ldap_call.ldap_u.domainname,
+ DOORLINESEP);
+ (void) strcat(space.s_d.ldap_call.ldap_u.domainname, server);
+ }
+ sptr = &space.s_d;
+
+ switch (__ns_ldap_trydoorcall(&sptr, &ndata, &adata)) {
+ case SUCCESS:
+ break;
+ /* this case is for when the $mgr is not running, but ldapclient */
+ /* is trying to initialize things */
+ case NOSERVER:
+ /* get first server from config list unavailable otherwise */
+ servers = NULL;
+ rc = __s_api_getServers(&servers, error);
+ if (rc != NS_LDAP_SUCCESS) {
+ if (servers != NULL) {
+ __s_api_free2dArray(servers);
+ servers = NULL;
+ }
+ return (rc);
+ }
+ if (servers == NULL || servers[0] == NULL) {
+ __s_api_free2dArray(servers);
+ servers = NULL;
+ (void) sprintf(errstr,
+ gettext("No server found in configuration"));
+ MKERROR(LOG_ERR, *error, NS_CONFIG_NODEFAULT,
+ strdup(errstr), NULL);
+ return (NS_LDAP_CONFIG);
+ }
+ ret->server = strdup(servers[0]);
+ if (ret->server == NULL) {
+ __s_api_free2dArray(servers);
+ return (NS_LDAP_MEMORY);
+ }
+ ret->saslMechanisms = NULL;
+ ret->controls = NULL;
+ __s_api_free2dArray(servers);
+ servers = NULL;
+ return (NS_LDAP_SUCCESS);
+ case NOTFOUND:
+ default:
+ return (NS_LDAP_OP_FAILED);
+ }
+
+ /* copy info from door call return structure here */
+ rbuf = space.s_d.ldap_ret.ldap_u.config;
+
+ /* Get the host */
+ ptr = strtok_r(rbuf, DOORLINESEP, &rest);
+ if (ptr == NULL) {
+ (void) sprintf(errstr, gettext("No server returned from "
+ "ldap_cachemgr"));
+ MKERROR(LOG_WARNING, *error, NS_CONFIG_CACHEMGR,
+ strdup(errstr), NULL);
+ return (NS_LDAP_OP_FAILED);
+ }
+ ret->server = strdup(ptr);
+ if (ret->server == NULL) {
+ return (NS_LDAP_MEMORY);
+ }
+
+ /* get the Supported Controls/SASL mechs */
+ mptr = NULL;
+ mcnt = 0;
+ cptr = NULL;
+ ccnt = 0;
+ for (; ; ) {
+ ptr = strtok_r(NULL, DOORLINESEP, &rest);
+ if (ptr == NULL)
+ break;
+ if (strncasecmp(ptr, _SASLMECHANISM,
+ _SASLMECHANISM_LEN) == 0) {
+ dptr = strchr(ptr, '=');
+ if (dptr == NULL)
+ continue;
+ dptr++;
+ mptr1 = (char **)realloc((void *)mptr,
+ sizeof (char *) * (mcnt+2));
+ if (mptr1 == NULL) {
+ __s_api_free2dArray(mptr);
+ if (sptr != &space.s_d) {
+ (void) munmap((char *)sptr, ndata);
+ }
+ __s_api_free2dArray(cptr);
+ free(ret->server);
+ ret->server = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+ mptr = mptr1;
+ mptr[mcnt] = strdup(dptr);
+ if (mptr[mcnt] == NULL) {
+ if (sptr != &space.s_d) {
+ (void) munmap((char *)sptr, ndata);
+ }
+ __s_api_free2dArray(cptr);
+ cptr = NULL;
+ __s_api_free2dArray(mptr);
+ mptr = NULL;
+ free(ret->server);
+ ret->server = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+ mcnt++;
+ mptr[mcnt] = NULL;
+ }
+ if (strncasecmp(ptr, _SUPPORTEDCONTROL,
+ _SUPPORTEDCONTROL_LEN) == 0) {
+ dptr = strchr(ptr, '=');
+ if (dptr == NULL)
+ continue;
+ dptr++;
+ cptr1 = (char **)realloc((void *)cptr,
+ sizeof (char *) * (ccnt+2));
+ if (cptr1 == NULL) {
+ if (sptr != &space.s_d) {
+ (void) munmap((char *)sptr, ndata);
+ }
+ __s_api_free2dArray(cptr);
+ __s_api_free2dArray(mptr);
+ mptr = NULL;
+ free(ret->server);
+ ret->server = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+ cptr = cptr1;
+ cptr[ccnt] = strdup(dptr);
+ if (cptr[ccnt] == NULL) {
+ if (sptr != &space.s_d) {
+ (void) munmap((char *)sptr, ndata);
+ }
+ __s_api_free2dArray(cptr);
+ cptr = NULL;
+ __s_api_free2dArray(mptr);
+ mptr = NULL;
+ free(ret->server);
+ ret->server = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+ ccnt++;
+ cptr[ccnt] = NULL;
+ }
+ }
+ if (mptr != NULL) {
+ ret->saslMechanisms = mptr;
+ }
+ if (cptr != NULL) {
+ ret->controls = cptr;
+ }
+
+
+ /* clean up door call */
+ if (sptr != &space.s_d) {
+ (void) munmap((char *)sptr, ndata);
+ }
+ *error = NULL;
+
+ return (NS_LDAP_SUCCESS);
+}
+
+#ifdef DEBUG
+
+/*
+ * printCred(): prints the credential structure
+ */
+static void
+printCred(FILE *fp, const ns_cred_t *cred)
+{
+ if (fp == NULL) {
+ (void) fprintf(fp, "printCred: fp is NULL\n");
+ return;
+ }
+ if (cred == NULL) {
+ (void) fprintf(fp, "printCred: cred is NULL\n");
+ return;
+ }
+
+ (void) fprintf(fp, "AuthType=%d\n", cred->auth.type);
+ (void) fprintf(fp, "TlsType=%d\n", cred->auth.tlstype);
+ (void) fprintf(fp, "SaslMech=%d\n", cred->auth.saslmech);
+ (void) fprintf(fp, "SaslOpt=%d\n", cred->auth.saslopt);
+ if (cred->hostcertpath)
+ (void) fprintf(fp, "hostCertPath=%s\n", cred->hostcertpath);
+ if (cred->cred.unix_cred.userID)
+ (void) fprintf(fp, "userID=%s\n", cred->cred.unix_cred.userID);
+ if (cred->cred.unix_cred.passwd)
+ (void) fprintf(fp, "passwd=%s\n", cred->cred.unix_cred.passwd);
+}
+
+/*
+ * printConnection(): prints the connection structure
+ */
+static void
+printConnection(FILE *fp, Connection *con)
+{
+ if (fp == NULL || con == NULL)
+ return;
+
+ (void) fprintf(fp, "connectionID=%d\n", con->connectionId);
+ (void) fprintf(fp, "usedBit=%d\n", con->usedBit);
+ (void) fprintf(fp, "threadID=%d\n", con->threadID);
+ if (con->serverAddr) {
+ (void) fprintf(fp, "serverAddr=%s\n", con->serverAddr);
+ }
+ printCred(fp, con->auth);
+ (void) fprintf(fp, "-----------------------------------------------\n");
+ fflush(fp);
+}
+
+#endif /* DEBUG */
+
+
+/*
+ * addConnection(): inserts a connection in the connection list.
+ * It will also sets use bit and the thread Id for the thread
+ * using the connection for the first time.
+ * Returns: -1 = failure, new Connection ID = success
+ */
+static int
+addConnection(Connection *con)
+{
+ int i;
+
+ if (!con)
+ return (-1);
+#ifdef DEBUG
+ (void) fprintf(stderr, "Adding connection thrid=%d\n", con->threadID);
+#endif /* DEBUG */
+ (void) mutex_lock(&sessionPoolLock);
+ if (sessionPool == NULL) {
+ sessionPoolSize = SESSION_CACHE_INC;
+ sessionPool = calloc(sessionPoolSize,
+ sizeof (struct connection **));
+ if (!sessionPool) {
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "Initialized sessionPool\n");
+#endif /* DEBUG */
+ }
+ for (i = 0; (i < sessionPoolSize) && (sessionPool[i] != NULL); ++i)
+ ;
+ if (i == sessionPoolSize) {
+ /* run out of array, need to increase sessionPool */
+ Connection **cl;
+ cl = (Connection **) realloc(sessionPool,
+ (sessionPoolSize + SESSION_CACHE_INC) *
+ sizeof (Connection *));
+ if (!cl) {
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
+ }
+ (void) memset(cl + sessionPoolSize, 0,
+ SESSION_CACHE_INC * sizeof (struct connection *));
+ sessionPool = cl;
+ sessionPoolSize += SESSION_CACHE_INC;
+#ifdef DEBUG
+ (void) fprintf(stderr, "Increased sessionPoolSize to: %d\n",
+ sessionPoolSize);
+#endif /* DEBUG */
+ }
+ sessionPool[i] = con;
+ con->usedBit = B_TRUE;
+ (void) mutex_unlock(&sessionPoolLock);
+ con->connectionId = i + CONID_OFFSET;
+#ifdef DEBUG
+ (void) fprintf(stderr, "Connection added [%d]\n", i);
+ printConnection(stderr, con);
+#endif /* DEBUG */
+ return (i + CONID_OFFSET);
+}
+
+/*
+ * See if the specified session matches a currently available
+ */
+
+static int
+findConnectionById(int flags, const ns_cred_t *auth, ConnectionID cID,
+ Connection **conp)
+{
+ Connection *cp;
+ int id;
+
+ if ((conp == NULL) || (auth == NULL) || cID < CONID_OFFSET)
+ return (-1);
+ *conp = NULL;
+ if (sessionPool == NULL)
+ return (-1);
+ id = cID - CONID_OFFSET;
+ if (id < 0 || id >= sessionPoolSize)
+ return (-1);
+
+ (void) mutex_lock(&sessionPoolLock);
+ if (sessionPool[id] == NULL) {
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
+ }
+ cp = sessionPool[id];
+
+ /*
+ * Make sure the connection has the same type of authentication method
+ */
+ if ((cp->usedBit) ||
+ (cp->auth->auth.type != auth->auth.type) ||
+ (cp->auth->auth.tlstype != auth->auth.tlstype) ||
+ (cp->auth->auth.saslmech != auth->auth.saslmech) ||
+ (cp->auth->auth.saslopt != auth->auth.saslopt)) {
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
+ }
+ if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
+ ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
+ (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
+ (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
+ ((cp->auth->cred.unix_cred.userID == NULL) ||
+ (strcasecmp(cp->auth->cred.unix_cred.userID,
+ auth->cred.unix_cred.userID) != 0))) {
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
+ }
+ /* An existing connection is found but it needs to be reset */
+ if (flags & NS_LDAP_NEW_CONN) {
+ (void) mutex_unlock(&sessionPoolLock);
+ DropConnection(cID, 0);
+ return (-1);
+ }
+ /* found an available connection */
+ cp->usedBit = B_TRUE;
+ (void) mutex_unlock(&sessionPoolLock);
+ cp->threadID = thr_self();
+ *conp = cp;
+ return (cID);
+}
+
+/*
+ * findConnection(): find an available connection from the list
+ * that matches the criteria specified in Connection structure.
+ * If serverAddr is NULL, then find a connection to any server
+ * as long as it matches the rest of the parameters.
+ * Returns: -1 = failure, the Connection ID found = success.
+ */
+static int
+findConnection(const char *serverAddr, const ns_cred_t *auth, Connection **conp)
+{
+ Connection *cp;
+ int i;
+
+ if (auth == NULL || conp == NULL)
+ return (-1);
+ *conp = NULL;
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "Find connection\n");
+ (void) fprintf(stderr, "Looking for ....\n");
+ if (serverAddr && *serverAddr)
+ (void) fprintf(stderr, "serverAddr=%s\n", serverAddr);
+ else
+ (void) fprintf(stderr, "serverAddr=NULL\n");
+ printCred(stderr, auth);
+ fflush(stderr);
+#endif /* DEBUG */
+ if (sessionPool == NULL)
+ return (-1);
+ (void) mutex_lock(&sessionPoolLock);
+ for (i = 0; i < sessionPoolSize; ++i) {
+ if (sessionPool[i] == NULL)
+ continue;
+ cp = sessionPool[i];
+#ifdef DEBUG
+ (void) fprintf(stderr, "checking connection [%d] ....\n", i);
+ printConnection(stderr, cp);
+#endif /* DEBUG */
+ if ((cp->usedBit) ||
+ (cp->auth->auth.type != auth->auth.type) ||
+ (cp->auth->auth.tlstype != auth->auth.tlstype) ||
+ (cp->auth->auth.saslmech != auth->auth.saslmech) ||
+ (cp->auth->auth.saslopt != auth->auth.saslopt) ||
+ (serverAddr && *serverAddr &&
+ (strcasecmp(serverAddr, cp->serverAddr) != 0)))
+ continue;
+ if ((((cp->auth->auth.type == NS_LDAP_AUTH_SASL) &&
+ ((cp->auth->auth.saslmech == NS_LDAP_SASL_CRAM_MD5) ||
+ (cp->auth->auth.saslmech == NS_LDAP_SASL_DIGEST_MD5))) ||
+ (cp->auth->auth.type == NS_LDAP_AUTH_SIMPLE)) &&
+ ((cp->auth->cred.unix_cred.userID == NULL) ||
+ (cp->auth->cred.unix_cred.passwd == NULL) ||
+ ((strcasecmp(cp->auth->cred.unix_cred.userID,
+ auth->cred.unix_cred.userID) != 0)) ||
+ ((strcmp(cp->auth->cred.unix_cred.passwd,
+ auth->cred.unix_cred.passwd) != 0))))
+ continue;
+ /* found an available connection */
+ cp->usedBit = B_TRUE;
+ (void) mutex_unlock(&sessionPoolLock);
+ cp->threadID = thr_self();
+ *conp = cp;
+#ifdef DEBUG
+ (void) fprintf(stderr, "Connection found cID=%d\n", i);
+ fflush(stderr);
+#endif /* DEBUG */
+ return (i + CONID_OFFSET);
+ }
+ (void) mutex_unlock(&sessionPoolLock);
+ return (-1);
+}
+
+/*
+ * Free a Connection structure
+ */
+static void
+freeConnection(Connection *con)
+{
+ if (con == NULL)
+ return;
+ if (con->serverAddr)
+ free(con->serverAddr);
+ if (con->auth)
+ (void) __ns_ldap_freeCred(&(con->auth));
+ if (con->saslMechanisms) {
+ __s_api_free2dArray(con->saslMechanisms);
+ }
+ if (con->controls) {
+ __s_api_free2dArray(con->controls);
+ }
+ free(con);
+}
+
+/*
+ * Find a connection matching the passed in criteria. If an open
+ * connection with that criteria exists use it, otherwise open a
+ * new connection.
+ * Success: returns the pointer to the Connection structure
+ * Failure: returns NULL, error code and message should be in errorp
+ */
+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)
+{
+ Connection *con = NULL;
+ ConnectionID id;
+ char errmsg[MAXERROR];
+ int rc, exit_rc = NS_LDAP_SUCCESS;
+ ns_server_info_t sinfo;
+ char *hReq, *host = NULL;
+ LDAP *ld = NULL;
+ int passwd_mgmt = 0;
+
+ if (conp == NULL || errorp == NULL || auth == NULL)
+ return (NS_LDAP_INVALID_PARAM);
+ *errorp = NULL;
+ *conp = NULL;
+ sinfo.server = NULL;
+ sinfo.controls = NULL;
+ sinfo.saslMechanisms = NULL;
+
+ if ((id = findConnection(serverAddr, auth, &con)) != -1) {
+ /* connection found in cache */
+#ifdef DEBUG
+ (void) fprintf(stderr, "connection found in cache %d\n", id);
+ fflush(stderr);
+#endif /* DEBUG */
+ *cID = id;
+ *conp = con;
+ return (NS_LDAP_SUCCESS);
+ }
+
+ if (serverAddr) {
+ rc = openConnection(&ld, serverAddr, auth, timeoutSec, errorp,
+ fail_if_new_pwd_reqd, passwd_mgmt);
+ if (rc == NS_LDAP_SUCCESS || rc ==
+ NS_LDAP_SUCCESS_WITH_INFO) {
+ exit_rc = rc;
+ rc = __s_api_requestServer(NS_CACHE_NEW, serverAddr,
+ &sinfo, errorp);
+ if (rc != NS_LDAP_SUCCESS || sinfo.server == NULL) {
+ (void) snprintf(errmsg, sizeof (errmsg),
+ gettext("makeConnection: unable to get "
+ "server information for %s"), serverAddr);
+ syslog(LOG_ERR, "libsldap: %s", errmsg);
+ return (NS_LDAP_OP_FAILED);
+ }
+ goto create_con;
+ } else {
+ return (rc);
+ }
+ }
+
+ /* No cached connection, create one */
+ for (; ; ) {
+ if (host == NULL)
+ hReq = NS_CACHE_NEW;
+ else
+ hReq = NS_CACHE_NEXT;
+ rc = __s_api_requestServer(hReq, host, &sinfo, errorp);
+ if ((rc != NS_LDAP_SUCCESS) || (sinfo.server == NULL) ||
+ (host && (strcasecmp(host, sinfo.server) == 0))) {
+ /* Log the error */
+ if (*errorp) {
+ (void) snprintf(errmsg, sizeof (errmsg),
+ "%s: (%s)", gettext("makeConnection: "
+ "unable to make LDAP connection, "
+ "request for a server failed"),
+ (*errorp)->message);
+ syslog(LOG_ERR, "libsldap: %s", errmsg);
+ }
+
+ if (sinfo.server)
+ free(sinfo.server);
+ __s_api_free2dArray(sinfo.saslMechanisms);
+ __s_api_free2dArray(sinfo.controls);
+ if (host)
+ free(host);
+ return (NS_LDAP_OP_FAILED);
+ }
+ if (host)
+ free(host);
+ host = strdup(sinfo.server);
+ if (host == NULL) {
+ free(sinfo.server);
+ __s_api_free2dArray(sinfo.saslMechanisms);
+ __s_api_free2dArray(sinfo.controls);
+ return (NS_LDAP_MEMORY);
+ }
+
+ /* check if server supports password management */
+ passwd_mgmt = __s_api_contain_passwd_control_oid(
+ sinfo.controls);
+ /* make the connection */
+ rc = openConnection(&ld, host, auth, timeoutSec, errorp,
+ fail_if_new_pwd_reqd, passwd_mgmt);
+ /* if success, go to create connection structure */
+ if (rc == NS_LDAP_SUCCESS ||
+ rc == NS_LDAP_SUCCESS_WITH_INFO) {
+ exit_rc = rc;
+ break;
+ }
+
+ /*
+ * If not able to reach the server, inform the ldap
+ * cache manager that the server should be removed
+ * from its server list. Thus, the manager will not
+ * return this server on the next get-server request
+ * and will also reduce the server list refresh TTL,
+ * so that it will find out sooner when the server
+ * is up again.
+ */
+ if (rc == NS_LDAP_INTERNAL && *errorp != NULL) {
+ if ((*errorp)->status == LDAP_CONNECT_ERROR ||
+ (*errorp)->status == LDAP_SERVER_DOWN)
+ __s_api_removeServer(host);
+ }
+
+ /* else, cleanup and go for the next server */
+ if (sinfo.server) {
+ free(sinfo.server);
+ sinfo.server = NULL;
+ }
+ __s_api_free2dArray(sinfo.saslMechanisms);
+ sinfo.saslMechanisms = NULL;
+ __s_api_free2dArray(sinfo.controls);
+ sinfo.controls = NULL;
+ if (*errorp) {
+ /*
+ * If openConnection() failed due to
+ * password policy, or invalid credential,
+ * keep *errorp and exit
+ */
+ if ((*errorp)->pwd_mgmt.status != NS_PASSWD_GOOD ||
+ (*errorp)->status == LDAP_INVALID_CREDENTIALS) {
+ free(host);
+ return (rc);
+ } else {
+ (void) __ns_ldap_freeError(errorp);
+ *errorp = NULL;
+ }
+ }
+ }
+
+create_con:
+ /* we have created ld, setup con structure */
+ if (host)
+ free(host);
+ if ((con = calloc(1, sizeof (Connection))) == NULL) {
+ if (sinfo.server)
+ free(sinfo.server);
+ __s_api_free2dArray(sinfo.saslMechanisms);
+ __s_api_free2dArray(sinfo.controls);
+ /*
+ * If password control attached in **errorp,
+ * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
+ * free the error structure
+ */
+ if (*errorp) {
+ (void) __ns_ldap_freeError(errorp);
+ *errorp = NULL;
+ }
+ return (NS_LDAP_MEMORY);
+ }
+
+ con->serverAddr = sinfo.server;
+ con->saslMechanisms = sinfo.saslMechanisms;
+ con->controls = sinfo.controls;
+
+ con->auth = __ns_ldap_dupAuth(auth);
+ if (con->auth == NULL) {
+ free(con);
+ /*
+ * If password control attached in **errorp,
+ * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
+ * free the error structure
+ */
+ if (*errorp) {
+ (void) __ns_ldap_freeError(errorp);
+ *errorp = NULL;
+ }
+ return (NS_LDAP_MEMORY);
+ }
+
+ con->threadID = thr_self();
+ con->ld = ld;
+ if ((id = addConnection(con)) == -1) {
+ freeConnection(con);
+ /*
+ * If password control attached in **errorp,
+ * e.g. rc == NS_LDAP_SUCCESS_WITH_INFO,
+ * free the error structure
+ */
+ if (*errorp) {
+ (void) __ns_ldap_freeError(errorp);
+ *errorp = NULL;
+ }
+ return (NS_LDAP_MEMORY);
+ }
+#ifdef DEBUG
+ (void) fprintf(stderr, "connection added into cache %d\n", id);
+ fflush(stderr);
+#endif /* DEBUG */
+ *cID = id;
+ *conp = con;
+ return (exit_rc);
+}
+
+/*
+ * Return the specified connection to the pool. If necessary
+ * delete the connection.
+ */
+
+static void
+_DropConnection(ConnectionID cID, int flag, int fini)
+{
+ Connection *cp;
+ int id;
+ int use_mutex = !fini;
+
+ id = cID - CONID_OFFSET;
+ if (id < 0 || id >= sessionPoolSize)
+ return;
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "Dropping connection cID=%d flag=0x%x\n", cID, flag);
+ fflush(stderr);
+#endif /* DEBUG */
+ if (use_mutex)
+ (void) mutex_lock(&sessionPoolLock);
+
+ cp = sessionPool[id];
+ /* sanity check before removing */
+ if (!cp || (!fini && (!cp->usedBit || cp->threadID != thr_self()))) {
+ if (use_mutex)
+ (void) mutex_unlock(&sessionPoolLock);
+ return;
+ }
+
+ if (!fini &&
+ ((flag & NS_LDAP_NEW_CONN) == 0) &&
+ ((flag & NS_LDAP_KEEP_CONN) || nscd_proc())) {
+ /* release Connection (keep alive) */
+ cp->usedBit = B_FALSE;
+ cp->threadID = 0; /* unmark the threadID */
+ if (use_mutex)
+ (void) mutex_unlock(&sessionPoolLock);
+ } else {
+ /* delete Connection (disconnect) */
+ sessionPool[id] = NULL;
+ if (use_mutex)
+ (void) mutex_unlock(&sessionPoolLock);
+ (void) ldap_unbind(cp->ld);
+ freeConnection(cp);
+ }
+}
+
+void
+DropConnection(ConnectionID cID, int flag)
+{
+ _DropConnection(cID, flag, 0);
+}
+
+/*
+ * This routine is called after a bind operation is
+ * done in openConnection() to process the password
+ * management information, if any.
+ *
+ * Input:
+ * bind_type: "simple" or "sasl/DIGEST-MD5"
+ * ldaprc : ldap rc from the ldap bind operation
+ * controls : controls returned by the server
+ * errmsg : error message from the server
+ * fail_if_new_pwd_reqd:
+ * flag indicating if connection should be open
+ * when password needs to change immediately
+ * passwd_mgmt:
+ * flag indicating if server supports password
+ * policy/management
+ *
+ * Output : ns_ldap_error structure, which may contain
+ * password status and number of seconds until
+ * expired
+ *
+ * return rc:
+ * NS_LDAP_EXTERNAL: error, connection should not open
+ * NS_LDAP_SUCCESS_WITH_INFO: OK to open but password info attached
+ * NS_LDAP_SUCCESS: OK to open connection
+ *
+ */
+
+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)
+{
+ char errstr[MAXERROR];
+ LDAPControl **ctrl = NULL;
+ int exit_rc;
+ ns_ldap_passwd_status_t pwd_status = NS_PASSWD_GOOD;
+ int sec_until_exp = 0;
+
+ /*
+ * errmsg may be an empty string,
+ * even if ldaprc is LDAP_SUCCESS,
+ * free the empty string if that's the case
+ */
+ if (errmsg &&
+ (*errmsg == '\0' || ldaprc == LDAP_SUCCESS)) {
+ ldap_memfree(errmsg);
+ errmsg = NULL;
+ }
+
+ if (ldaprc != LDAP_SUCCESS) {
+ /*
+ * try to map ldap rc and error message to
+ * a password status
+ */
+ if (errmsg) {
+ if (passwd_mgmt)
+ pwd_status =
+ __s_api_set_passwd_status(
+ ldaprc, errmsg);
+ ldap_memfree(errmsg);
+ }
+
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "%s bind failed "
+ "- %s"), bind_type, ldap_err2string(ldaprc));
+
+ if (pwd_status != NS_PASSWD_GOOD) {
+ MKERROR_PWD_MGMT(*errorp,
+ ldaprc, strdup(errstr),
+ pwd_status, 0, NULL);
+ } else {
+ MKERROR(LOG_ERR, *errorp, ldaprc, strdup(errstr),
+ NULL);
+ }
+ if (controls)
+ ldap_controls_free(controls);
+
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /*
+ * ldaprc is LDAP_SUCCESS,
+ * process the password management controls, if any
+ */
+ exit_rc = NS_LDAP_SUCCESS;
+ if (controls && passwd_mgmt) {
+ /*
+ * The control with the OID
+ * 2.16.840.1.113730.3.4.4 (or
+ * LDAP_CONTROL_PWEXPIRED, as defined
+ * in the ldap.h header file) is the
+ * expired password control.
+ *
+ * This control is used if the server
+ * is configured to require users to
+ * change their passwords when first
+ * logging in and whenever the
+ * passwords are reset.
+ *
+ * If the user is logging in for the
+ * first time or if the user's
+ * password has been reset, the
+ * server sends this control to
+ * indicate that the client needs to
+ * change the password immediately.
+ *
+ * At this point, the only operation
+ * that the client can perform is to
+ * change the user's password. If the
+ * client requests any other LDAP
+ * operation, the server sends back
+ * an LDAP_UNWILLING_TO_PERFORM
+ * result code with an expired
+ * password control.
+ *
+ * The control with the OID
+ * 2.16.840.1.113730.3.4.5 (or
+ * LDAP_CONTROL_PWEXPIRING, as
+ * defined in the ldap.h header file)
+ * is the password expiration warning
+ * control.
+ *
+ * This control is used if the server
+ * is configured to expire user
+ * passwords after a certain amount
+ * of time.
+ *
+ * The server sends this control back
+ * to the client if the client binds
+ * using a password that will soon
+ * expire. The ldctl_value field of
+ * the LDAPControl structure
+ * specifies the number of seconds
+ * before the password will expire.
+ */
+ for (ctrl = controls; *ctrl; ctrl++) {
+
+ if (strcmp((*ctrl)->ldctl_oid,
+ LDAP_CONTROL_PWEXPIRED) == 0) {
+ /*
+ * if the caller wants this bind
+ * to fail, set up the error info.
+ * If call to this function is
+ * for searching the LDAP directory,
+ * e.g., __ns_ldap_list(),
+ * there's really no sense to
+ * let a connection open and
+ * then fail immediately afterward
+ * on the LDAP search operation with
+ * the LDAP_UNWILLING_TO_PERFORM rc
+ */
+ pwd_status =
+ NS_PASSWD_CHANGE_NEEDED;
+ if (fail_if_new_pwd_reqd) {
+ (void) snprintf(errstr,
+ sizeof (errstr),
+ gettext(
+ "openConnection: "
+ "%s bind "
+ "failed "
+ "- password "
+ "expired. It "
+ " needs to change "
+ "immediately!"),
+ bind_type);
+ MKERROR_PWD_MGMT(*errorp,
+ LDAP_SUCCESS,
+ strdup(errstr),
+ pwd_status,
+ 0,
+ NULL);
+ exit_rc = NS_LDAP_INTERNAL;
+ } else {
+ MKERROR_PWD_MGMT(*errorp,
+ LDAP_SUCCESS,
+ NULL,
+ pwd_status,
+ 0,
+ NULL);
+ exit_rc =
+ NS_LDAP_SUCCESS_WITH_INFO;
+ }
+ break;
+ } else if (strcmp((*ctrl)->ldctl_oid,
+ LDAP_CONTROL_PWEXPIRING) == 0) {
+ pwd_status =
+ NS_PASSWD_ABOUT_TO_EXPIRE;
+ if ((*ctrl)->
+ ldctl_value.bv_len > 0 &&
+ (*ctrl)->
+ ldctl_value.bv_val)
+ sec_until_exp =
+ atoi((*ctrl)->
+ ldctl_value.bv_val);
+ MKERROR_PWD_MGMT(*errorp,
+ LDAP_SUCCESS,
+ NULL,
+ pwd_status,
+ sec_until_exp,
+ NULL);
+ exit_rc =
+ NS_LDAP_SUCCESS_WITH_INFO;
+ break;
+ }
+ }
+ }
+
+ if (controls)
+ ldap_controls_free(controls);
+
+ return (exit_rc);
+}
+
+static int
+ldap_in_hosts_switch()
+{
+ enum __nsw_parse_err pserr;
+ struct __nsw_switchconfig *conf;
+ struct __nsw_lookup *lkp;
+ const char *name;
+ int found = 0;
+
+ conf = __nsw_getconfig("hosts", &pserr);
+ if (conf == NULL) {
+ return (-1);
+ }
+
+ /* check for skip and count other backends */
+ for (lkp = conf->lookups; lkp != NULL; lkp = lkp->next) {
+ name = lkp->service_name;
+ if (strcmp(name, "ldap") == 0) {
+ found = 1;
+ break;
+ }
+ }
+ __nsw_freeconfig(conf);
+ return (found);
+}
+
+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)
+{
+ LDAP *ld = NULL;
+ char *binddn, *passwd;
+ char *digest_md5_name;
+ const char *s;
+ int ldapVersion = LDAP_VERSION3;
+ int derefOption = LDAP_DEREF_ALWAYS;
+ int zero = 0;
+ int rc;
+ char errstr[MAXERROR];
+ int errnum = 0;
+ LDAPMessage *resultMsg;
+ int msgId;
+ int useSSL = 0;
+ struct timeval tv;
+ AuthType_t bindType;
+ int timeoutMilliSec = timeoutSec * 1000;
+ struct berval cred;
+ char *sslServerAddr;
+ char *s1;
+ char *errmsg;
+ LDAPControl **controls;
+ int pwd_rc;
+
+ *errorp = NULL;
+ *ldp = NULL;
+
+ switch (auth->auth.type) {
+ case NS_LDAP_AUTH_NONE:
+ case NS_LDAP_AUTH_SIMPLE:
+ case NS_LDAP_AUTH_SASL:
+ bindType = auth->auth.type;
+ break;
+ case NS_LDAP_AUTH_TLS:
+ useSSL = 1;
+ switch (auth->auth.tlstype) {
+ case NS_LDAP_TLS_NONE:
+ bindType = NS_LDAP_AUTH_NONE;
+ break;
+ case NS_LDAP_TLS_SIMPLE:
+ bindType = NS_LDAP_AUTH_SIMPLE;
+ break;
+ case NS_LDAP_TLS_SASL:
+ bindType = NS_LDAP_AUTH_SASL;
+ break;
+ default:
+ (void) sprintf(errstr,
+ gettext("openConnection: unsupported "
+ "TLS authentication method "
+ "(%d)"), auth->auth.tlstype);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ strdup(errstr), NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+ break;
+ default:
+ (void) sprintf(errstr,
+ gettext("openConnection: unsupported "
+ "authentication method (%d)"), auth->auth.type);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
+ NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ if (useSSL) {
+ const char *hostcertpath;
+ char *alloc_hcp = NULL;
+#ifdef DEBUG
+ (void) fprintf(stderr, "+++TLS transport\n");
+#endif /* DEBUG */
+ hostcertpath = auth->hostcertpath;
+ if (hostcertpath == NULL) {
+ alloc_hcp = __s_get_hostcertpath();
+ hostcertpath = alloc_hcp;
+ }
+
+ if (hostcertpath == NULL)
+ return (NS_LDAP_MEMORY);
+
+ if ((rc = ldapssl_client_init(hostcertpath, NULL)) < 0) {
+ if (alloc_hcp)
+ free(alloc_hcp);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: failed to initialize "
+ "TLS security (%s)"),
+ ldapssl_err2string(rc));
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(errstr), NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+ if (alloc_hcp)
+ free(alloc_hcp);
+
+ /* determine if the host name contains a port number */
+ s = strchr(serverAddr, ']'); /* skip over ipv6 addr */
+ if (s == NULL)
+ s = serverAddr;
+ s = strchr(s, ':');
+ if (s != NULL) {
+ /*
+ * If we do get a port number, we will try stripping
+ * it. At present, referrals will always have a
+ * port number.
+ */
+ sslServerAddr = strdup(serverAddr);
+ if (sslServerAddr == NULL)
+ return (NS_LDAP_MEMORY);
+ s1 = strrchr(sslServerAddr, ':');
+ if (s1 != NULL)
+ *s1 = '\0';
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: cannot use tls with %s. "
+ "Trying %s"),
+ serverAddr, sslServerAddr);
+ syslog(LOG_ERR, "libsldap: %s", errstr);
+ } else
+ sslServerAddr = (char *)serverAddr;
+
+ ld = ldapssl_init(sslServerAddr, LDAPS_PORT, 1);
+
+ if (sslServerAddr != serverAddr)
+ free(sslServerAddr);
+
+ if (ld == NULL ||
+ ldapssl_install_gethostbyaddr(ld, "ldap") != 0) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: failed to connect "
+ "using TLS (%s)"), strerror(errno));
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(errstr), NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+ } else {
+#ifdef DEBUG
+ (void) fprintf(stderr, "+++Unsecure transport\n");
+#endif /* DEBUG */
+ /* Warning message IF cannot connect to host(s) */
+ if ((ld = ldap_init((char *)serverAddr, LDAP_PORT)) == NULL) {
+ char *p = strerror(errno);
+ MKERROR(LOG_WARNING, *errorp, LDAP_CONNECT_ERROR,
+ strdup(p), NULL);
+ return (NS_LDAP_INTERNAL);
+ } else {
+ /* check and avoid gethostname recursion */
+ if (ldap_in_hosts_switch() > 0 &&
+ ! __s_api_isipv4((char *)serverAddr) &&
+ ! __s_api_isipv6((char *)serverAddr)) {
+ /* host: ldap - found, attempt to recover */
+ if (ldap_set_option(ld, LDAP_X_OPT_DNS_SKIPDB,
+ "ldap") != 0) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "unrecoverable gethostname "
+ "recursion detected "
+ "in /etc/nsswitch.conf"));
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_CONNECT_ERROR,
+ strdup(errstr), NULL);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ }
+ }
+ }
+
+ (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &ldapVersion);
+ (void) ldap_set_option(ld, LDAP_OPT_DEREF, &derefOption);
+ /*
+ * set LDAP_OPT_REFERRALS to OFF.
+ * This library will handle the referral itself
+ * based on API flags or configuration file
+ * specification. If this option is not set
+ * to OFF, libldap will never pass the
+ * referral info up to this library
+ */
+ (void) ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ (void) ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &zero);
+ (void) ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &zero);
+ /* setup TCP/IP connect timeout */
+ (void) ldap_set_option(ld, LDAP_X_OPT_CONNECT_TIMEOUT,
+ &timeoutMilliSec);
+ /* retry if LDAP I/O was interrupted */
+ (void) ldap_set_option(ld, LDAP_OPT_RESTART, LDAP_OPT_ON);
+
+ switch (bindType) {
+ case NS_LDAP_AUTH_NONE:
+#ifdef DEBUG
+ (void) fprintf(stderr, "+++Anonymous bind\n");
+#endif /* DEBUG */
+ break;
+ case NS_LDAP_AUTH_SIMPLE:
+ binddn = auth->cred.unix_cred.userID;
+ passwd = auth->cred.unix_cred.passwd;
+ if (passwd == NULL || *passwd == '\0' ||
+ binddn == NULL || *binddn == '\0') {
+ (void) sprintf(errstr, gettext("openConnection: "
+ "missing credentials for Simple bind"));
+ MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
+ strdup(errstr), NULL);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "+++Simple bind\n");
+#endif /* DEBUG */
+ msgId = ldap_simple_bind(ld, binddn, passwd);
+
+ if (msgId == -1) {
+ (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
+ (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: simple bind failed "
+ "- %s"), ldap_err2string(errnum));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
+ NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ tv.tv_sec = timeoutSec;
+ tv.tv_usec = 0;
+ rc = ldap_result(ld, msgId, 0, &tv, &resultMsg);
+
+ if ((rc == -1) || (rc == 0)) {
+ (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER,
+ (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: simple bind failed "
+ "- %s"), ldap_err2string(errnum));
+ (void) ldap_msgfree(resultMsg);
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, errnum, strdup(errstr),
+ NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /*
+ * get ldaprc, controls, and error msg
+ */
+ rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
+ &errmsg, NULL, &controls, 1);
+
+ if (rc != LDAP_SUCCESS) {
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: simple bind failed "
+ "- unable to parse result"));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
+ strdup(errstr), NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /* process the password management info, if any */
+ pwd_rc = process_pwd_mgmt("simple",
+ errnum, controls, errmsg,
+ errorp,
+ fail_if_new_pwd_reqd,
+ passwd_mgmt);
+
+ if (pwd_rc == NS_LDAP_INTERNAL) {
+ (void) ldap_unbind(ld);
+ return (pwd_rc);
+ }
+
+ if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
+ *ldp = ld;
+ return (pwd_rc);
+ }
+
+ break;
+ case NS_LDAP_AUTH_SASL:
+ /* We don't support any sasl options yet */
+ if (auth->auth.saslopt != NS_LDAP_SASLOPT_NONE) {
+ (void) sprintf(errstr,
+ gettext("openConnection: SASL options are "
+ "not supported (%d)"), auth->auth.saslopt);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED,
+ strdup(errstr), NULL);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ binddn = auth->cred.unix_cred.userID;
+ passwd = auth->cred.unix_cred.passwd;
+ if (passwd == NULL || *passwd == '\0' ||
+ binddn == NULL || *binddn == '\0') {
+ (void) sprintf(errstr,
+ gettext("openConnection: missing credentials "
+ "for SASL bind"));
+ MKERROR(LOG_WARNING, *errorp, LDAP_INVALID_CREDENTIALS,
+ strdup(errstr), NULL);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ cred.bv_val = passwd;
+ cred.bv_len = strlen(passwd);
+
+ switch (auth->auth.saslmech) {
+ case NS_LDAP_SASL_CRAM_MD5:
+ /*
+ * NOTE: if iDS changes to support cram_md5,
+ * please add password management code here.
+ * Since ldap_sasl_cram_md5_bind_s does not
+ * return anything that could be used to
+ * extract the ldap rc/errmsg/control to
+ * determine if bind failed due to password
+ * policy, a new cram_md5_bind API will need
+ * to be introduced. See
+ * ldap_x_sasl_digest_md5_bind() and case
+ * NS_LDAP_SASL_DIGEST_MD5 below for details.
+ */
+ if ((rc = ldap_sasl_cram_md5_bind_s(ld, binddn,
+ &cred, NULL, NULL)) != LDAP_SUCCESS) {
+ (void) ldap_get_option(ld,
+ LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "sasl/CRAM-MD5 bind failed - %s"),
+ ldap_err2string(errnum));
+ MKERROR(LOG_WARNING, *errorp, errnum,
+ strdup(errstr), NULL);
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_INTERNAL);
+ }
+ break;
+ case NS_LDAP_SASL_DIGEST_MD5:
+ digest_md5_name = malloc(strlen(binddn) + 5);
+ /* 5 = strlen("dn: ") + 1 */
+ if (digest_md5_name == NULL) {
+ (void) ldap_unbind(ld);
+ return (NS_LDAP_MEMORY);
+ }
+ (void) strcpy(digest_md5_name, "dn: ");
+ (void) strcat(digest_md5_name, binddn);
+
+ tv.tv_sec = timeoutSec;
+ tv.tv_usec = 0;
+ rc = ldap_x_sasl_digest_md5_bind(ld,
+ digest_md5_name, &cred, NULL, NULL,
+ &tv, &resultMsg);
+
+ if (resultMsg == NULL) {
+ free(digest_md5_name);
+ (void) ldap_get_option(ld,
+ LDAP_OPT_ERROR_NUMBER, (void *)&errnum);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "DIGEST-MD5 bind failed - %s"),
+ ldap_err2string(errnum));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, errnum,
+ strdup(errstr), NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /*
+ * get ldaprc, controls, and error msg
+ */
+ rc = ldap_parse_result(ld, resultMsg, &errnum, NULL,
+ &errmsg, NULL, &controls, 1);
+
+ if (rc != LDAP_SUCCESS) {
+ free(digest_md5_name);
+ (void) snprintf(errstr, sizeof (errstr),
+ gettext("openConnection: "
+ "DIGEST-MD5 bind failed "
+ "- unable to parse result"));
+ (void) ldap_unbind(ld);
+ MKERROR(LOG_WARNING, *errorp, NS_LDAP_INTERNAL,
+ strdup(errstr), NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+
+ /* process the password management info, if any */
+ pwd_rc = process_pwd_mgmt("sasl/DIGEST-MD5",
+ errnum, controls, errmsg,
+ errorp,
+ fail_if_new_pwd_reqd,
+ passwd_mgmt);
+
+ if (pwd_rc == NS_LDAP_INTERNAL) {
+ free(digest_md5_name);
+ (void) ldap_unbind(ld);
+ return (pwd_rc);
+ }
+
+ if (pwd_rc == NS_LDAP_SUCCESS_WITH_INFO) {
+ *ldp = ld;
+ return (pwd_rc);
+ }
+
+ free(digest_md5_name);
+ break;
+ default:
+ (void) ldap_unbind(ld);
+ (void) sprintf(errstr,
+ gettext("openConnection: unsupported SASL "
+ "mechanism (%d)"), auth->auth.saslmech);
+ MKERROR(LOG_WARNING, *errorp,
+ LDAP_AUTH_METHOD_NOT_SUPPORTED, strdup(errstr),
+ NULL);
+ return (NS_LDAP_INTERNAL);
+ }
+ }
+
+ *ldp = ld;
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * FUNCTION: __s_api_getDefaultAuth
+ *
+ * Constructs a credential for authentication using the config module.
+ *
+ * RETURN VALUES:
+ *
+ * NS_LDAP_SUCCESS If successful
+ * NS_LDAP_CONFIG If there are any config errors.
+ * NS_LDAP_MEMORY Memory errors.
+ * NS_LDAP_OP_FAILED If there are no more authentication methods so can
+ * not build a new authp.
+ * NS_LDAP_INVALID_PARAM This overloaded return value means that some of the
+ * necessary fields of a cred for a given auth method
+ * are not provided.
+ * INPUT:
+ *
+ * cLevel Currently requested credential level to be tried
+ *
+ * aMethod Currently requested authentication method to be tried
+ *
+ * OUTPUT:
+ *
+ * authp authentication method to use.
+ */
+static int
+__s_api_getDefaultAuth(
+ int *cLevel,
+ ns_auth_t *aMethod,
+ ns_cred_t **authp)
+{
+ void **paramVal = NULL;
+ char *modparamVal = NULL;
+ int getUid = 0;
+ int getPasswd = 0;
+ int getCertpath = 0;
+ int rc = 0;
+ ns_ldap_error_t *errorp = NULL;
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "__s_api_getDefaultAuth START\n");
+#endif
+
+ if (aMethod == NULL) {
+ /* Require an Auth */
+ return (NS_LDAP_INVALID_PARAM);
+
+ }
+
+ /*
+ * Do not differentiate between (proxy/self) at this time, but
+ * reject self credential levels at this time
+ */
+ if (cLevel && *cLevel == NS_LDAP_CRED_SELF)
+ return (NS_LDAP_INVALID_PARAM);
+
+ *authp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t));
+ if ((*authp) == NULL)
+ return (NS_LDAP_MEMORY);
+
+ (*authp)->auth = *aMethod;
+
+ switch (aMethod->type) {
+ case NS_LDAP_AUTH_NONE:
+ return (NS_LDAP_SUCCESS);
+ case NS_LDAP_AUTH_SIMPLE:
+ getUid++;
+ getPasswd++;
+ break;
+ case NS_LDAP_AUTH_SASL:
+ if ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
+ (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)) {
+ getUid++;
+ getPasswd++;
+ } else {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+ break;
+ case NS_LDAP_AUTH_TLS:
+ if ((aMethod->tlstype == NS_LDAP_TLS_SIMPLE) ||
+ ((aMethod->tlstype == NS_LDAP_TLS_SASL) &&
+ ((aMethod->saslmech == NS_LDAP_SASL_DIGEST_MD5) ||
+ (aMethod->saslmech == NS_LDAP_SASL_CRAM_MD5)))) {
+ getUid++;
+ getPasswd++;
+ getCertpath++;
+ } else if (aMethod->tlstype == NS_LDAP_TLS_NONE) {
+ getCertpath++;
+ } else {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+ break;
+ }
+
+ if (getUid) {
+ paramVal = NULL;
+ if ((rc = __ns_ldap_getParam(NS_LDAP_BINDDN_P,
+ &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
+ (void) __ns_ldap_freeCred(authp);
+ (void) __ns_ldap_freeError(&errorp);
+ *authp = NULL;
+ return (rc);
+ }
+
+ if (paramVal == NULL || *paramVal == NULL) {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ (*authp)->cred.unix_cred.userID = strdup((char *)*paramVal);
+ (void) __ns_ldap_freeParam(&paramVal);
+ if ((*authp)->cred.unix_cred.userID == NULL) {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+ }
+ if (getPasswd) {
+ paramVal = NULL;
+ if ((rc = __ns_ldap_getParam(NS_LDAP_BINDPASSWD_P,
+ &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
+ (void) __ns_ldap_freeCred(authp);
+ (void) __ns_ldap_freeError(&errorp);
+ *authp = NULL;
+ return (rc);
+ }
+
+ if (paramVal == NULL || *paramVal == NULL) {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ modparamVal = dvalue((char *)*paramVal);
+ (void) __ns_ldap_freeParam(&paramVal);
+ if (modparamVal == NULL || (strlen((char *)modparamVal) == 0)) {
+ (void) __ns_ldap_freeCred(authp);
+ if (modparamVal != NULL)
+ free(modparamVal);
+ *authp = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ (*authp)->cred.unix_cred.passwd = modparamVal;
+ }
+ if (getCertpath) {
+ paramVal = NULL;
+ if ((rc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
+ &paramVal, &errorp)) != NS_LDAP_SUCCESS) {
+ (void) __ns_ldap_freeCred(authp);
+ (void) __ns_ldap_freeError(&errorp);
+ *authp = NULL;
+ return (rc);
+ }
+
+ if (paramVal == NULL || *paramVal == NULL) {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_INVALID_PARAM);
+ }
+
+ (*authp)->hostcertpath = strdup((char *)*paramVal);
+ (void) __ns_ldap_freeParam(&paramVal);
+ if ((*authp)->hostcertpath == NULL) {
+ (void) __ns_ldap_freeCred(authp);
+ *authp = NULL;
+ return (NS_LDAP_MEMORY);
+ }
+ }
+ return (NS_LDAP_SUCCESS);
+}
+
+/*
+ * FUNCTION: __s_api_getConnection
+ *
+ * Bind to the specified server or one from the server
+ * list and return the pointer.
+ *
+ * This function can rebind or not (NS_LDAP_HARD), it can require a
+ * credential or bind anonymously
+ *
+ * This function follows the DUA configuration schema algorithm
+ *
+ * RETURN VALUES:
+ *
+ * NS_LDAP_SUCCESS A connection was made successfully.
+ * NS_LDAP_SUCCESS_WITH_INFO
+ * A connection was made successfully, but with
+ * password management info in *errorp
+ * NS_LDAP_INVALID_PARAM If any invalid arguments were passed to the function.
+ * NS_LDAP_CONFIG If there are any config errors.
+ * NS_LDAP_MEMORY Memory errors.
+ * NS_LDAP_INTERNAL If there was a ldap error.
+ *
+ * INPUT:
+ *
+ * server Bind to this LDAP server only
+ * flags If NS_LDAP_HARD is set function will not return until it has
+ * a connection unless there is a authentication problem.
+ * If NS_LDAP_NEW_CONN is set the function must force a new
+ * connection to be created
+ * If NS_LDAP_KEEP_CONN is set the connection is to be kept open
+ * auth Credentials for bind. This could be NULL in which case
+ * a default cred built from the config module is used.
+ * sessionId cookie that points to a previous session
+ * fail_if_new_pwd_reqd
+ * a flag indicating this function should fail if the passwd
+ * in auth needs to change immediately
+ *
+ * OUTPUT:
+ *
+ * session pointer to a session with connection information
+ * errorp Set if there are any INTERNAL, or CONFIG error.
+ */
+int
+__s_api_getConnection(
+ const char *server,
+ const int flags,
+ const ns_cred_t *cred, /* credentials for bind */
+ ConnectionID *sessionId,
+ Connection **session,
+ ns_ldap_error_t **errorp,
+ int fail_if_new_pwd_reqd)
+{
+ char errmsg[MAXERROR];
+ ns_auth_t **aMethod = NULL;
+ ns_auth_t **aNext = NULL;
+ int **cLevel = NULL;
+ int **cNext = NULL;
+ int timeoutSec = NS_DEFAULT_BIND_TIMEOUT;
+ int rc;
+ Connection *con = NULL;
+ int sec = 1;
+ ns_cred_t *authp = NULL;
+ ns_cred_t anon;
+ int version = NS_LDAP_V2;
+ void **paramVal = NULL;
+
+ if ((session == NULL) || (sessionId == NULL)) {
+ return (NS_LDAP_INVALID_PARAM);
+ }
+ *session = NULL;
+
+ /* if we already have a session id try to reuse connection */
+ if (*sessionId > 0) {
+ rc = findConnectionById(flags, cred, *sessionId, &con);
+ if (rc == *sessionId && con) {
+ *session = con;
+ return (NS_LDAP_SUCCESS);
+ }
+ *sessionId = 0;
+ }
+
+ /* get profile version number */
+ if ((rc = __ns_ldap_getParam(NS_LDAP_FILE_VERSION_P,
+ &paramVal, errorp)) != NS_LDAP_SUCCESS)
+ return (rc);
+ if (paramVal == NULL) {
+ (void) sprintf(errmsg, gettext("getConnection: no file "
+ "version"));
+ MKERROR(LOG_WARNING, *errorp, NS_CONFIG_FILE, strdup(errmsg),
+ NS_LDAP_CONFIG);
+ return (NS_LDAP_CONFIG);
+ }
+ if (strcasecmp((char *)*paramVal, NS_LDAP_VERSION_1) == 0)
+ version = NS_LDAP_V1;
+ (void) __ns_ldap_freeParam((void ***)&paramVal);
+
+ /* Get the bind timeout value */
+ (void) __ns_ldap_getParam(NS_LDAP_BIND_TIME_P, &paramVal, errorp);
+ if (paramVal != NULL && *paramVal != NULL) {
+ timeoutSec = **((int **)paramVal);
+ (void) __ns_ldap_freeParam(&paramVal);
+ }
+ if (*errorp)
+ (void) __ns_ldap_freeError(errorp);
+
+ if (cred == NULL) {
+ /* Get the authentication method list */
+ if ((rc = __ns_ldap_getParam(NS_LDAP_AUTH_P,
+ (void ***)&aMethod, errorp)) != NS_LDAP_SUCCESS)
+ return (rc);
+ if (aMethod == NULL) {
+ aMethod = (ns_auth_t **)calloc(2, sizeof (ns_auth_t *));
+ if (aMethod == NULL)
+ return (NS_LDAP_MEMORY);
+ aMethod[0] = (ns_auth_t *)calloc(1, sizeof (ns_auth_t));
+ if (aMethod[0] == NULL) {
+ free(aMethod);
+ return (NS_LDAP_MEMORY);
+ }
+ if (version == NS_LDAP_V1)
+ (aMethod[0])->type = NS_LDAP_AUTH_SIMPLE;
+ else {
+ (aMethod[0])->type = NS_LDAP_AUTH_SASL;
+ (aMethod[0])->saslmech =
+ NS_LDAP_SASL_DIGEST_MD5;
+ (aMethod[0])->saslopt = NS_LDAP_SASLOPT_NONE;
+ }
+ }
+
+ /* Get the credential level list */
+ if ((rc = __ns_ldap_getParam(NS_LDAP_CREDENTIAL_LEVEL_P,
+ (void ***)&cLevel, errorp)) != NS_LDAP_SUCCESS) {
+ (void) __ns_ldap_freeParam((void ***)&aMethod);
+ return (rc);
+ }
+ if (cLevel == NULL) {
+ cLevel = (int **)calloc(2, sizeof (int *));
+ if (cLevel == NULL)
+ return (NS_LDAP_MEMORY);
+ cLevel[0] = (int *)calloc(1, sizeof (int));
+ if (cLevel[0] == NULL)
+ return (NS_LDAP_MEMORY);
+ if (version == NS_LDAP_V1)
+ *(cLevel[0]) = NS_LDAP_CRED_PROXY;
+ else
+ *(cLevel[0]) = NS_LDAP_CRED_ANON;
+ }
+ }
+
+ /* setup the anon credential for anonymous connection */
+ (void) memset(&anon, 0, sizeof (ns_cred_t));
+ anon.auth.type = NS_LDAP_AUTH_NONE;
+
+ for (; ; ) {
+ if (cred != NULL) {
+ /* using specified auth method */
+ rc = makeConnection(&con, server, cred,
+ sessionId, timeoutSec, errorp,
+ fail_if_new_pwd_reqd);
+ if (rc == NS_LDAP_SUCCESS ||
+ rc == NS_LDAP_SUCCESS_WITH_INFO) {
+ *session = con;
+ break;
+ }
+ } else {
+ /* for every cred level */
+ for (cNext = cLevel; *cNext != NULL; cNext++) {
+ if (**cNext == NS_LDAP_CRED_ANON) {
+ /* make connection anonymously */
+ rc = makeConnection(&con, server, &anon,
+ sessionId, timeoutSec, errorp,
+ fail_if_new_pwd_reqd);
+ if (rc == NS_LDAP_SUCCESS ||
+ rc ==
+ NS_LDAP_SUCCESS_WITH_INFO) {
+ *session = con;
+ goto done;
+ }
+ continue;
+ }
+ /* for each cred level */
+ for (aNext = aMethod; *aNext != NULL; aNext++) {
+ /* make connection and authenticate */
+ /* with default credentials */
+ authp = NULL;
+ rc = __s_api_getDefaultAuth(*cNext,
+ *aNext, &authp);
+ if (rc != NS_LDAP_SUCCESS) {
+ continue;
+ }
+ rc = makeConnection(&con, server, authp,
+ sessionId, timeoutSec, errorp,
+ fail_if_new_pwd_reqd);
+ (void) __ns_ldap_freeCred(&authp);
+ if (rc == NS_LDAP_SUCCESS ||
+ rc ==
+ NS_LDAP_SUCCESS_WITH_INFO) {
+ *session = con;
+ goto done;
+ }
+ }
+ }
+ }
+ if (flags & NS_LDAP_HARD) {
+ if (sec < LDAPMAXHARDLOOKUPTIME)
+ sec *= 2;
+ _sleep(sec);
+ } else {
+ break;
+ }
+ }
+
+done:
+ (void) __ns_ldap_freeParam((void ***)&aMethod);
+ (void) __ns_ldap_freeParam((void ***)&cLevel);
+ return (rc);
+}
+
+#pragma fini(_free_sessionPool)
+static void
+_free_sessionPool()
+{
+ int id;
+
+ (void) mutex_lock(&sessionPoolLock);
+ if (sessionPool != NULL) {
+ for (id = 0; id < sessionPoolSize; id++)
+ _DropConnection(id + CONID_OFFSET, 0, 1);
+ free(sessionPool);
+ sessionPool = NULL;
+ sessionPoolSize = 0;
+ }
+ (void) mutex_unlock(&sessionPoolLock);
+}