summaryrefslogtreecommitdiff
path: root/server/ldap-nss.c
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2006-11-01 09:23:10 +0000
committerArthur de Jong <arthur@arthurdejong.org>2006-11-01 09:23:10 +0000
commit3fcd73560c9d83dd9c816f3a2d86d1b01a7af8ab (patch)
tree28509cef8d55d480d7965f54b61b82835f48dae2 /server/ldap-nss.c
parent909352b52a35508ee8d76afed7b8003900a9ff41 (diff)
move some remaining files into the server/ directory
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/libnss_ldapd@39 ef36b2f9-881f-0410-afb5-c4e39611909c
Diffstat (limited to 'server/ldap-nss.c')
-rw-r--r--server/ldap-nss.c3998
1 files changed, 3998 insertions, 0 deletions
diff --git a/server/ldap-nss.c b/server/ldap-nss.c
new file mode 100644
index 0000000..f5d4539
--- /dev/null
+++ b/server/ldap-nss.c
@@ -0,0 +1,3998 @@
+/*
+ ldap-nss.c - main file for NSS interface
+ This file is part of the nss_ldap library.
+
+ Copyright (C) 1997-2006 Luke Howard
+ Copyright (C) 2006 Arthur de Jong
+
+ The nss_ldap library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The nss_ldap library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the nss_ldap library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ $Id$
+*/
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <stdio.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <errno.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_LBER_H
+#include <lber.h>
+#endif
+#ifdef HAVE_LDAP_H
+#include <ldap.h>
+#endif
+#ifdef HAVE_LDAP_SSL_H
+#include <ldap_ssl.h>
+#endif
+#ifdef HAVE_GSSLDAP_H
+#include <gssldap.h>
+#endif
+#ifdef HAVE_GSSSASL_H
+#include <gsssasl.h>
+#endif
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#if defined(HAVE_THREAD_H)
+#include <thread.h>
+#elif defined(HAVE_PTHREAD_H)
+#include <pthread.h>
+#endif
+/* Try to handle systems with both SASL libraries installed */
+#if defined(HAVE_SASL_SASL_H) && defined(HAVE_SASL_AUXPROP_REQUEST)
+#include <sasl/sasl.h>
+#elif defined(HAVE_SASL_H)
+#include <sasl.h>
+#endif
+#ifdef HAVE_GSSAPI_H
+#include <gssapi.h>
+#elif defined(HAVE_GSSAPI_GSSAPI_KRB5_H)
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#endif
+
+#include "ldap-nss.h"
+#include "util.h"
+#include "dnsconfig.h"
+#include "pagectrl.h"
+
+#if defined(HAVE_THREAD_H)
+#ifdef HAVE_PTHREAD_ATFORK
+#undef HAVE_PTHREAD_ATFORK
+#endif
+#endif
+
+/* how many messages to retrieve results for */
+#ifndef LDAP_MSG_ONE
+#define LDAP_MSG_ONE 0x00
+#endif
+#ifndef LDAP_MSG_ALL
+#define LDAP_MSG_ALL 0x01
+#endif
+#ifndef LDAP_MSG_RECEIVED
+#define LDAP_MSG_RECEIVED 0x02
+#endif
+
+#ifdef HAVE_LDAP_LD_FREE
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+extern int ldap_ld_free (LDAP * ld, int close, LDAPControl **,
+ LDAPControl **);
+#else
+extern int ldap_ld_free (LDAP * ld, int close);
+#endif /* OPENLDAP 2.x */
+#endif /* HAVE_LDAP_LD_FREE */
+
+NSS_LDAP_DEFINE_LOCK (__lock);
+
+/*
+ * the configuration is read by the first call to do_open().
+ * Pointers to elements of the list are passed around but should not
+ * be freed.
+ */
+static char __configbuf[NSS_LDAP_CONFIG_BUFSIZ];
+static struct ldap_config *__config = NULL;
+
+#ifdef HAVE_SIGACTION
+static struct sigaction __stored_handler;
+static int __sigaction_retval = -1;
+#else
+static void (*__sigpipe_handler) (int) = SIG_DFL;
+#endif /* HAVE_SIGACTION */
+
+/*
+ * Global LDAP session.
+ */
+static struct ldap_session __session = { NULL, NULL, 0, LS_UNINITIALIZED };
+
+#if defined(HAVE_PTHREAD_ATFORK) || defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+static pthread_once_t __once = PTHREAD_ONCE_INIT;
+#endif
+
+#ifdef LBER_OPT_LOG_PRINT_FILE
+static FILE *__debugfile;
+#endif /* LBER_OPT_LOG_PRINT_FILE */
+
+#ifndef HAVE_PTHREAD_ATFORK
+/*
+ * Process ID that opened the session.
+ */
+static pid_t __pid = -1;
+#endif
+static uid_t __euid = -1;
+
+#ifdef HAVE_LDAPSSL_CLIENT_INIT
+static int __ssl_initialized = 0;
+#endif /* HAVE_LDAPSSL_CLIENT_INIT */
+
+#if defined(HAVE_PTHREAD_ATFORK) || defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+/*
+ * Prepare for fork(); lock mutex.
+ */
+static void do_atfork_prepare (void);
+
+/*
+ * Forked in parent, unlock mutex.
+ */
+static void do_atfork_parent (void);
+
+/*
+ * Forked in child; close LDAP socket, unlock mutex.
+ */
+static void do_atfork_child (void);
+
+/*
+ * Install handlers for atfork, called once.
+ */
+static void do_atfork_setup (void);
+#endif
+
+/*
+ * Close the global session, sending an unbind.
+ */
+static void do_close (void);
+
+/*
+ * Close the global session without sending an unbind.
+ */
+static void do_close_no_unbind (void);
+
+/*
+ * Disable keepalive on a LDAP connection's socket.
+ */
+static void do_set_sockopts (void);
+
+/*
+ * TLS routines: set global SSL session options.
+ */
+#if defined(HAVE_LDAP_START_TLS_S) || defined(HAVE_LDAP_START_TLS) || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS))
+static int do_ssl_options (struct ldap_config * cfg);
+static int do_start_tls (struct ldap_session * session);
+#endif
+
+/*
+ * Function to be braced by reconnect harness. Used so we
+ * can apply the reconnect code to both asynchronous and
+ * synchronous searches.
+ */
+typedef int (*search_func_t) (const char *, int, const char *,
+ const char **, int, void *);
+
+static enum nss_status
+do_map_error (int rc)
+{
+ enum nss_status stat;
+
+ switch (rc)
+ {
+ case LDAP_SUCCESS:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ stat = NSS_STATUS_SUCCESS;
+ break;
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ case LDAP_UNDEFINED_TYPE:
+ case LDAP_INAPPROPRIATE_MATCHING:
+ case LDAP_CONSTRAINT_VIOLATION:
+ case LDAP_TYPE_OR_VALUE_EXISTS:
+ case LDAP_INVALID_SYNTAX:
+ case LDAP_NO_SUCH_OBJECT:
+ case LDAP_ALIAS_PROBLEM:
+ case LDAP_INVALID_DN_SYNTAX:
+ case LDAP_IS_LEAF:
+ case LDAP_ALIAS_DEREF_PROBLEM:
+ case LDAP_FILTER_ERROR:
+ stat = NSS_STATUS_NOTFOUND;
+ break;
+ case LDAP_SERVER_DOWN:
+ case LDAP_TIMEOUT:
+ case LDAP_UNAVAILABLE:
+ case LDAP_BUSY:
+#ifdef LDAP_CONNECT_ERROR
+ case LDAP_CONNECT_ERROR:
+#endif /* LDAP_CONNECT_ERROR */
+ case LDAP_LOCAL_ERROR:
+ case LDAP_INVALID_CREDENTIALS:
+ default:
+ stat = NSS_STATUS_UNAVAIL;
+ break;
+ }
+ return stat;
+}
+
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) ||defined (HAVE_SASL_SASL_H))
+static int
+do_sasl_interact (LDAP * ld, unsigned flags, void *defaults, void *_interact)
+{
+ char *authzid = (char *) defaults;
+ sasl_interact_t *interact = (sasl_interact_t *) _interact;
+
+ while (interact->id != SASL_CB_LIST_END)
+ {
+ if (interact->id == SASL_CB_USER)
+ {
+ if (authzid != NULL)
+ {
+ interact->result = authzid;
+ interact->len = strlen (authzid);
+ }
+ else if (interact->defresult != NULL)
+ {
+ interact->result = interact->defresult;
+ interact->len = strlen (interact->defresult);
+ }
+ else
+ {
+ interact->result = "";
+ interact->len = 0;
+ }
+#if SASL_VERSION_MAJOR < 2
+ interact->result = strdup (interact->result);
+ if (interact->result == NULL)
+ {
+ return LDAP_NO_MEMORY;
+ }
+#endif /* SASL_VERSION_MAJOR < 2 */
+ }
+ else
+ {
+ return LDAP_PARAM_ERROR;
+ }
+ interact++;
+ }
+ return LDAP_SUCCESS;
+}
+#endif
+
+static int
+do_bind (LDAP * ld, int timelimit, const char *dn, const char *pw,
+ int with_sasl)
+{
+ int rc;
+ int msgid;
+ struct timeval tv;
+ LDAPMessage *result;
+
+ debug("==> do_bind");
+
+ /*
+ * set timelimit in ld for select() call in ldap_pvt_connect()
+ * function implemented in libldap2's os-ip.c
+ */
+ tv.tv_sec = timelimit;
+ tv.tv_usec = 0;
+
+#if (defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))) || defined(HAVE_LDAP_GSS_BIND)
+ if (!with_sasl)
+ {
+#endif
+ msgid = ldap_simple_bind (ld, dn, pw);
+
+ if (msgid < 0)
+ {
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
+ if (ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &rc) !=
+ LDAP_SUCCESS)
+ {
+ rc = LDAP_UNAVAILABLE;
+ }
+#else
+ rc = ld->ld_errno;
+#endif /* LDAP_OPT_ERROR_NUMBER */
+ /* Notify if we failed. */
+ syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not connect to any LDAP server as %s - %s",
+ dn, ldap_err2string (rc));
+ debug ("<== do_bind");
+
+ return rc;
+ }
+
+ rc = ldap_result (ld, msgid, 0, &tv, &result);
+ if (rc > 0)
+ {
+ debug ("<== do_bind");
+ return ldap_result2error (ld, result, 1);
+ }
+
+ /* took too long */
+ if (rc == 0)
+ {
+ ldap_abandon (ld, msgid);
+ }
+#if (defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))) || defined(HAVE_LDAP_GSS_BIND)
+ }
+ else
+ {
+#ifdef HAVE_LDAP_GSS_BIND
+ return ldap_gss_bind (ld, dn, pw, GSSSASL_NO_SECURITY_LAYER,
+ LDAP_SASL_GSSAPI);
+#else
+#ifdef CONFIGURE_KRB5_CCNAME
+#ifndef CONFIGURE_KRB5_CCNAME_GSSAPI
+ char tmpbuf[256];
+ static char envbuf[256];
+#endif
+ char *ccname;
+ const char *oldccname = NULL;
+ int retval;
+#endif /* CONFIGURE_KRB5_CCNAME */
+
+ if (__config->ldc_sasl_secprops != NULL)
+ {
+ rc =
+ ldap_set_option (ld, LDAP_OPT_X_SASL_SECPROPS,
+ (void *) __config->ldc_sasl_secprops);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug ("do_bind: unable to set SASL security properties");
+ return rc;
+ }
+ }
+
+#ifdef CONFIGURE_KRB5_CCNAME
+ /* Set default Kerberos ticket cache for SASL-GSSAPI */
+ /* There are probably race conditions here XXX */
+ if (__config->ldc_krb5_ccname != NULL)
+ {
+ ccname = __config->ldc_krb5_ccname;
+#ifdef CONFIGURE_KRB5_CCNAME_ENV
+ oldccname = getenv ("KRB5CCNAME");
+ if (oldccname != NULL)
+ {
+ strncpy (tmpbuf, oldccname, sizeof (tmpbuf));
+ tmpbuf[sizeof (tmpbuf) - 1] = '\0';
+ }
+ else
+ {
+ tmpbuf[0] = '\0';
+ }
+ oldccname = tmpbuf;
+ snprintf (envbuf, sizeof (envbuf), "KRB5CCNAME=%s", ccname);
+ putenv (envbuf);
+#elif defined(CONFIGURE_KRB5_CCNAME_GSSAPI)
+ if (gss_krb5_ccache_name (&retval, ccname, &oldccname) !=
+ GSS_S_COMPLETE)
+ {
+ debug ("do_bind: unable to set default credential cache");
+ return -1;
+ }
+#endif
+ }
+#endif /* CONFIGURE_KRB5_CCNAME */
+
+ rc = ldap_sasl_interactive_bind_s (ld, dn, "GSSAPI", NULL, NULL,
+ LDAP_SASL_QUIET,
+ do_sasl_interact, (void *) pw);
+
+#ifdef CONFIGURE_KRB5_CCNAME
+ /* Restore default Kerberos ticket cache. */
+ if (oldccname != NULL)
+ {
+#ifdef CONFIGURE_KRB5_CCNAME_ENV
+ snprintf (envbuf, sizeof (envbuf), "KRB5CCNAME=%s", oldccname);
+ putenv (envbuf);
+#elif defined(CONFIGURE_KRB5_CCNAME_GSSAPI)
+ if (gss_krb5_ccache_name (&retval, oldccname, NULL) !=
+ GSS_S_COMPLETE)
+ {
+ debug ("do_bind: unable to restore default credential cache");
+ return -1;
+ }
+#endif
+ }
+#endif /* CONFIGURE_KRB5_CCNAME */
+
+ return rc;
+#endif /* HAVE_LDAP_GSS_BIND */
+ }
+#endif
+
+ debug ("<== do_bind");
+
+ return -1;
+}
+
+/*
+ * Rebind functions.
+ */
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+static int
+do_rebind (LDAP * ld, LDAP_CONST char *url, ber_tag_t request,
+ ber_int_t msgid, void *arg)
+#else
+static int
+do_rebind (LDAP * ld, LDAP_CONST char *url, int request, ber_int_t msgid)
+#endif
+{
+ char *who, *cred;
+ int timelimit;
+ int with_sasl = 0;
+
+ if (geteuid () == 0 && __session.ls_config->ldc_rootbinddn)
+ {
+ who = __session.ls_config->ldc_rootbinddn;
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))
+ with_sasl = __session.ls_config->ldc_rootusesasl;
+ if (with_sasl)
+ {
+ cred = __session.ls_config->ldc_rootsaslid;
+ }
+ else
+ {
+#endif
+ cred = __session.ls_config->ldc_rootbindpw;
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))
+ }
+#endif
+ }
+ else
+ {
+ who = __session.ls_config->ldc_binddn;
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))
+ with_sasl = __session.ls_config->ldc_usesasl;
+ if (with_sasl)
+ {
+ cred = __session.ls_config->ldc_saslid;
+ }
+ else
+ {
+#endif
+ cred = __session.ls_config->ldc_bindpw;
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))
+ }
+#endif
+ }
+
+ timelimit = __session.ls_config->ldc_bind_timelimit;
+
+#ifdef HAVE_LDAP_START_TLS_S
+ if (__session.ls_config->ldc_ssl_on == SSL_START_TLS)
+ {
+ int version;
+
+ if (ldap_get_option
+ (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION,
+ &version) == LDAP_OPT_SUCCESS)
+ {
+ if (version < LDAP_VERSION3)
+ {
+ version = LDAP_VERSION3;
+ ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION,
+ &version);
+ }
+ }
+
+ if (do_start_tls (&__session) == LDAP_SUCCESS)
+ {
+ debug ("TLS startup succeeded");
+ }
+ else
+ {
+ debug ("TLS startup failed");
+ return NSS_STATUS_UNAVAIL;
+ }
+ }
+#endif /* HAVE_LDAP_START_TLS_S */
+
+ return do_bind (ld, timelimit, who, cred, with_sasl);
+}
+#else
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+static int
+do_rebind (LDAP * ld, char **whop, char **credp, int *methodp,
+ int freeit, void *arg)
+#elif LDAP_SET_REBIND_PROC_ARGS == 2
+static int
+do_rebind (LDAP * ld, char **whop, char **credp, int *methodp, int freeit)
+#endif
+{
+ if (freeit)
+ {
+ if (*whop != NULL)
+ free (*whop);
+ if (*credp != NULL)
+ free (*credp);
+ }
+
+ *whop = *credp = NULL;
+ if (geteuid () == 0 && __session.ls_config->ldc_rootbinddn)
+ {
+ *whop = strdup (__session.ls_config->ldc_rootbinddn);
+ if (__session.ls_config->ldc_rootbindpw != NULL)
+ *credp = strdup (__session.ls_config->ldc_rootbindpw);
+ }
+ else
+ {
+ if (__session.ls_config->ldc_binddn != NULL)
+ *whop = strdup (__session.ls_config->ldc_binddn);
+ if (__session.ls_config->ldc_bindpw != NULL)
+ *credp = strdup (__session.ls_config->ldc_bindpw);
+ }
+
+ *methodp = LDAP_AUTH_SIMPLE;
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+static void
+_nss_ldap_block_sigpipe (void)
+{
+#ifdef HAVE_SIGACTION
+ struct sigaction new_handler;
+
+ memset (&new_handler, 0, sizeof (new_handler));
+#if 0
+ /* XXX need to test for sa_sigaction, not on all platforms */
+ new_handler.sa_sigaction = NULL;
+#endif
+ new_handler.sa_handler = SIG_IGN;
+ sigemptyset (&new_handler.sa_mask);
+ new_handler.sa_flags = 0;
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * Patch for Debian Bug 130006:
+ * ignore SIGPIPE for all LDAP operations.
+ *
+ * The following bug was reintroduced in nss_ldap-213 and is fixed here:
+ * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=84344
+ *
+ * See:
+ * http://www.gnu.org/software/libc/manual/html_node/Signal-and-Sigaction.html
+ * for more details.
+ */
+#ifdef HAVE_SIGACTION
+ __sigaction_retval = sigaction (SIGPIPE, &new_handler, &__stored_handler);
+#elif defined(HAVE_SIGSET)
+ __sigpipe_handler = sigset (SIGPIPE, SIG_IGN);
+#else
+ __sigpipe_handler = signal (SIGPIPE, SIG_IGN);
+#endif /* HAVE_SIGSET */
+}
+
+static void
+_nss_ldap_unblock_sigpipe (void)
+{
+#ifdef HAVE_SIGACTION
+ if (__sigaction_retval == 0)
+ (void) sigaction (SIGPIPE, &__stored_handler, NULL);
+#else
+ if (__sigpipe_handler != SIG_ERR && __sigpipe_handler != SIG_IGN)
+ {
+#ifdef HAVE_SIGSET
+ (void) sigset (SIGPIPE, __sigpipe_handler);
+#else
+ (void) signal (SIGPIPE, __sigpipe_handler);
+#endif /* HAVE_SIGSET */
+ }
+#endif /* HAVE_SIGACTION */
+}
+
+#if defined(HAVE_PTHREAD_ATFORK) || defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+static void
+do_atfork_prepare (void)
+{
+ debug ("==> do_atfork_prepare");
+ NSS_LDAP_LOCK (__lock);
+ debug ("<== do_atfork_prepare");
+}
+
+static void
+do_atfork_parent (void)
+{
+ debug ("==> do_atfork_parent");
+ NSS_LDAP_UNLOCK (__lock);
+ debug ("<== do_atfork_parent");
+}
+
+static void
+do_atfork_child (void)
+{
+ debug ("==> do_atfork_child");
+ _nss_ldap_block_sigpipe();
+ do_close_no_unbind ();
+ _nss_ldap_unblock_sigpipe();
+ NSS_LDAP_UNLOCK (__lock);
+ debug ("<== do_atfork_child");
+}
+
+static void
+do_atfork_setup (void)
+{
+ debug ("==> do_atfork_setup");
+
+#ifdef HAVE_PTHREAD_ATFORK
+ (void) pthread_atfork (do_atfork_prepare, do_atfork_parent,
+ do_atfork_child);
+#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+ (void) __libc_atfork (do_atfork_prepare, do_atfork_parent, do_atfork_child);
+#endif
+
+ debug ("<== do_atfork_setup");
+}
+#endif
+
+/*
+ * Acquires global lock, blocks SIGPIPE.
+ */
+void
+_nss_ldap_enter (void)
+{
+ debug ("==> _nss_ldap_enter");
+
+ NSS_LDAP_LOCK (__lock);
+ _nss_ldap_block_sigpipe();
+
+ debug ("<== _nss_ldap_enter");
+}
+
+/*
+ * Releases global mutex, releases SIGPIPE.
+ */
+void
+_nss_ldap_leave (void)
+{
+ debug ("==> _nss_ldap_leave");
+
+ _nss_ldap_unblock_sigpipe();
+ NSS_LDAP_UNLOCK (__lock);
+
+ debug ("<== _nss_ldap_leave");
+}
+
+static void
+do_set_sockopts (void)
+{
+/*
+ * Netscape SSL-enabled LDAP library does not
+ * return the real socket.
+ */
+#ifndef HAVE_LDAPSSL_CLIENT_INIT
+ int sd = -1;
+
+ debug ("==> do_set_sockopts");
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_DESC)
+ if (ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, &sd) == 0)
+#else
+ if ((sd = __session.ls_conn->ld_sb.sb_sd) > 0)
+#endif /* LDAP_OPT_DESC */
+ {
+ int off = 0;
+ NSS_LDAP_SOCKLEN_T socknamelen = sizeof (struct sockaddr_storage);
+ NSS_LDAP_SOCKLEN_T peernamelen = sizeof (struct sockaddr_storage);
+
+ (void) setsockopt (sd, SOL_SOCKET, SO_KEEPALIVE, (void *) &off,
+ sizeof (off));
+ (void) fcntl (sd, F_SETFD, FD_CLOEXEC);
+ /*
+ * NSS modules shouldn't open file descriptors that the program/utility
+ * linked against NSS doesn't know about. The LDAP library opens a
+ * connection to the LDAP server transparently. There's an edge case
+ * where a daemon might fork a child and, being written well, closes
+ * all its file descriptors. This will close the socket descriptor
+ * being used by the LDAP library! Worse, the daemon might open many
+ * files and sockets, eventually opening a descriptor with the same number
+ * as that originally used by the LDAP library. The only way to know that
+ * this isn't "our" socket descriptor is to save the local and remote
+ * sockaddr_in structures for later comparison.
+ */
+ (void) getsockname (sd, (struct sockaddr *) &__session.ls_sockname,
+ &socknamelen);
+ (void) getpeername (sd, (struct sockaddr *) &__session.ls_peername,
+ &peernamelen);
+ }
+ debug ("<== do_set_sockopts");
+#endif /* HAVE_LDAPSSL_CLIENT_INIT */
+
+ return;
+}
+
+/*
+ * Closes connection to the LDAP server.
+ * This assumes that we have exclusive access to __session.ls_conn,
+ * either by some other function having acquired a lock, or by
+ * using a thread safe libldap.
+ */
+static void
+do_close (void)
+{
+#if defined(DEBUG) || defined(DEBUG_SOCKETS)
+ int sd = -1;
+#endif
+
+ debug ("==> do_close");
+
+ if (__session.ls_conn != NULL)
+ {
+#if defined(DEBUG) || defined(DEBUG_SOCKETS)
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_DESC)
+ ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, &sd);
+#else
+ sd = __session.ls_conn->ld_sb.sb_sd;
+#endif /* LDAP_OPT_DESC */
+ syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: closing connection %p fd %d",
+ (void *)__session.ls_conn, sd);
+#endif /* DEBUG */
+
+ ldap_unbind (__session.ls_conn);
+ __session.ls_conn = NULL;
+ __session.ls_state = LS_UNINITIALIZED;
+ }
+
+ debug ("<== do_close");
+}
+
+static int
+do_sockaddr_isequal (struct sockaddr_storage *_s1,
+ NSS_LDAP_SOCKLEN_T _slen1,
+ struct sockaddr_storage *_s2,
+ NSS_LDAP_SOCKLEN_T _slen2)
+{
+ int ret;
+
+ if (_s1->ss_family != _s2->ss_family)
+ return 0;
+
+ if (_slen1 != _slen2)
+ return 0;
+
+ ret = 0;
+
+ switch (_s1->ss_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *s1 = (struct sockaddr_in *) _s1;
+ struct sockaddr_in *s2 = (struct sockaddr_in *) _s2;
+
+ ret = (s1->sin_port == s2->sin_port &&
+ memcmp (&s1->sin_addr, &s2->sin_addr, sizeof(struct in_addr)) == 0);
+ break;
+ }
+ case AF_UNIX:
+ {
+ struct sockaddr_un *s1 = (struct sockaddr_un *) _s1;
+ struct sockaddr_un *s2 = (struct sockaddr_un *) _s2;
+
+ ret = (memcmp (s1->sun_path, s2->sun_path,
+ _slen1 - sizeof (_s1->ss_family)) == 0);
+ break;
+ }
+#ifdef INET6
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *s1 = (struct sockaddr_in6 *) _s1;
+ struct sockaddr_in6 *s2 = (struct sockaddr_in6 *) _s2;
+
+ ret = (s1->sin6_port == s2->sin6_port &&
+ memcmp (&s1->sin6_addr, &s2->sin6_addr, sizeof(struct in6_addr)) == 0 &&
+ s1->sin6_scope_id == s2->sin6_scope_id);
+ break;
+ }
+#endif
+ default:
+ ret = (memcmp (_s1, _s2, _slen1) == 0);
+ break;
+ }
+
+ return ret;
+}
+
+static int
+do_get_our_socket(int *sd)
+{
+ /*
+ * Before freeing the LDAP context or closing the socket descriptor,
+ * we must ensure that it is *our* socket descriptor. See the much
+ * lengthier description of this at the end of do_open () where the
+ * values __session.ls_sockname and __session.ls_peername are saved.
+ * With HAVE_LDAPSSL_CLIENT_INIT this returns 0 if the socket has
+ * been closed or reopened, and sets *sd to the ldap socket
+ * descriptor.. Returns 1 in all other cases.
+ */
+
+ int isOurSocket = 1;
+
+#ifndef HAVE_LDAPSSL_CLIENT_INIT
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_DESC)
+ if (ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, sd) == 0)
+#else
+ if ((*sd = __session.ls_conn->ld_sb.sb_sd) > 0)
+#endif /* LDAP_OPT_DESC */
+ {
+ struct sockaddr_storage sockname;
+ struct sockaddr_storage peername;
+ NSS_LDAP_SOCKLEN_T socknamelen = sizeof (sockname);
+ NSS_LDAP_SOCKLEN_T peernamelen = sizeof (peername);
+
+ if (getsockname (*sd, (struct sockaddr *) &sockname, &socknamelen) != 0 ||
+ getpeername (*sd, (struct sockaddr *) &peername, &peernamelen) != 0)
+ {
+ isOurSocket = 0;
+ }
+ else
+ {
+ isOurSocket = do_sockaddr_isequal (&__session.ls_sockname,
+ socknamelen,
+ &sockname,
+ socknamelen);
+ if (isOurSocket)
+ {
+ isOurSocket = do_sockaddr_isequal (&__session.ls_peername,
+ peernamelen,
+ &peername,
+ peernamelen);
+ }
+ }
+ }
+#endif /* HAVE_LDAPSSL_CLIENT_INIT */
+ return isOurSocket;
+}
+
+static int
+do_dupfd(int oldfd, int newfd)
+{
+ int d = -1;
+ int flags;
+
+ flags = fcntl(oldfd, F_GETFD);
+
+ while (1)
+ {
+ d = (newfd > -1) ? dup2 (oldfd, newfd) : dup (oldfd);
+ if (d > -1)
+ break;
+
+ if (errno == EBADF)
+ return -1; /* not open */
+
+ if (errno != EINTR
+#ifdef EBUSY
+ && errno != EBUSY
+#endif
+ )
+ return -1;
+ }
+
+ /* duplicate close-on-exec flag */
+ (void) fcntl (d, F_SETFD, flags);
+
+ return d;
+}
+
+static int
+do_closefd(int fd)
+{
+ int rc;
+
+ while ((rc = close(fd)) < 0 && errno == EINTR)
+ ;
+
+ return rc;
+}
+
+static void
+do_drop_connection(int sd, int closeSd)
+{
+ /* Close the LDAP connection without writing anything to the
+ underlying socket. The socket will be left open afterwards if
+ closeSd is 0 */
+#ifndef HAVE_LDAPSSL_CLIENT_INIT
+ {
+ int dummyfd = -1, savedfd = -1;
+ /* Under OpenLDAP 2.x, ldap_set_option (..., LDAP_OPT_DESC, ...) is
+ a no-op, so to shut down the LDAP connection without writing
+ anything to the socket, we swap a dummy socket onto that file
+ descriptor, and then swap the real fd back once the shutdown is
+ done. */
+ savedfd = do_dupfd (sd, -1);
+ dummyfd = socket (AF_INET, SOCK_STREAM, 0);
+ if (dummyfd > -1 && dummyfd != sd)
+ {
+ do_closefd (sd);
+ do_dupfd (dummyfd, sd);
+ do_closefd (dummyfd);
+ }
+
+#ifdef HAVE_LDAP_LD_FREE
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+ (void) ldap_ld_free (__session.ls_conn, 0, NULL, NULL);
+#else
+ (void) ldap_ld_free (__session.ls_conn, 0);
+#endif /* OPENLDAP 2.x */
+#else
+ ldap_unbind (__session.ls_conn);
+#endif /* HAVE_LDAP_LD_FREE */
+
+ /* Do we want our original sd back? */
+ do_closefd (sd);
+ if (savedfd > -1)
+ {
+ if (closeSd == 0)
+ do_dupfd (savedfd, sd);
+ do_closefd (savedfd);
+ }
+ }
+#else /* No sd available */
+ {
+ int bogusSd = -1;
+ if (closeSd == 0)
+ {
+ sd = -1; /* don't want to really close the socket */
+#ifdef HAVE_LDAP_LD_FREE
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_DESC)
+ (void) ldap_set_option (__session.ls_conn, LDAP_OPT_DESC, &sd);
+#else
+ __session.ls_conn->ld_sb.sb_sd = -1;
+#endif /* LDAP_OPT_DESC */
+#endif /* HAVE_LDAP_LD_FREE */
+ }
+
+#ifdef HAVE_LDAP_LD_FREE
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+ (void) ldap_ld_free (__session.ls_conn, 0, NULL, NULL);
+#else
+ (void) ldap_ld_free (__session.ls_conn, 0);
+#endif /* OPENLDAP 2.x */
+
+#else
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_DESC)
+ (void) ldap_set_option (__session.ls_conn, LDAP_OPT_DESC, &bogusSd);
+#else
+ __session.ls_conn->ld_sb.sb_sd = bogusSd;
+#endif /* LDAP_OPT_DESC */
+
+ /* hope we closed it OK! */
+ ldap_unbind (__session.ls_conn);
+
+#endif /* HAVE_LDAP_LD_FREE */
+
+ }
+#endif /* HAVE_LDAPSSL_CLIENT_INIT */
+ __session.ls_conn = NULL;
+ __session.ls_state = LS_UNINITIALIZED;
+
+ return;
+}
+
+/*
+ * If we've forked, then we need to open a new session.
+ * Careful: we have the socket shared with our parent,
+ * so we don't want to send an unbind to the server.
+ * However, we want to close the descriptor to avoid
+ * leaking it, and we also want to release the memory
+ * used by __session.ls_conn. The only entry point
+ * we have is ldap_unbind() which does both of these
+ * things, so we use an internal API, at the expense
+ * of compatibility.
+ */
+static void
+do_close_no_unbind (void)
+{
+ int sd = -1;
+ int closeSd = 1;
+
+ debug ("==> do_close_no_unbind");
+
+ if (__session.ls_state == LS_UNINITIALIZED)
+ {
+ assert (__session.ls_conn == NULL);
+ debug ("<== do_close_no_unbind (connection was not open)");
+ return;
+ }
+
+ closeSd = do_get_our_socket (&sd);
+
+#if defined(DEBUG) || defined(DEBUG_SOCKETS)
+ syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: %sclosing connection (no unbind) %p fd %d",
+ closeSd ? "" : "not ", (void *)__session.ls_conn, sd);
+#endif /* DEBUG */
+
+ do_drop_connection(sd, closeSd);
+
+ debug ("<== do_close_no_unbind");
+
+ return;
+}
+
+static enum nss_status
+do_init_session (LDAP ** ld, const char *uri, int defport)
+{
+ int rc;
+ int ldaps;
+ char uribuf[NSS_BUFSIZ];
+ char *p;
+ enum nss_status stat;
+
+ ldaps = (strncasecmp (uri, "ldaps://", sizeof ("ldaps://") - 1) == 0);
+ p = strchr (uri, ':');
+ /* we should be looking for the second instance to find the port number */
+ if (p != NULL)
+ {
+ p = strchr (p, ':');
+ }
+
+#ifdef HAVE_LDAP_INITIALIZE
+ if (p == NULL &&
+ ((ldaps && defport != LDAPS_PORT) || (!ldaps && defport != LDAP_PORT)))
+ {
+ /* No port specified in URI and non-default port specified */
+ snprintf (uribuf, sizeof (uribuf), "%s:%d", uri, defport);
+ uri = uribuf;
+ }
+
+ rc = ldap_initialize (ld, uri);
+#else
+ if (strncasecmp (uri, "ldap://", sizeof ("ldap://") - 1) != 0)
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ uri += sizeof ("ldap://") - 1;
+ p = strchr (uri, ':');
+
+ if (p != NULL)
+ {
+ size_t urilen = (p - uri);
+
+ if (urilen >= sizeof (uribuf))
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ memcpy (uribuf, uri, urilen);
+ uribuf[urilen] = '\0';
+
+ defport = atoi (p + 1);
+ uri = uribuf;
+ }
+
+#ifdef HAVE_LDAP_INIT
+ *ld = ldap_init (uri, defport);
+#else
+ *ld = ldap_open (uri, defport);
+#endif
+
+ rc = (*ld == NULL) ? LDAP_SERVER_DOWN : LDAP_SUCCESS;
+
+#endif /* HAVE_LDAP_INITIALIZE */
+
+ stat = do_map_error (rc);
+ if (stat == NSS_STATUS_SUCCESS && *ld == NULL)
+ {
+ stat = NSS_STATUS_UNAVAIL;
+ }
+ return stat;
+}
+
+static enum nss_status
+do_init (void)
+{
+ struct ldap_config *cfg;
+#ifndef HAVE_PTHREAD_ATFORK
+ pid_t pid;
+#endif
+ uid_t euid;
+ enum nss_status stat;
+ int sd=-1;
+
+ debug ("==> do_init");
+
+ if (_nss_ldap_validateconfig (__config) != NSS_STATUS_SUCCESS)
+ {
+ do_close ();
+ __config = NULL;
+ __session.ls_current_uri = 0;
+ }
+
+#ifndef HAVE_PTHREAD_ATFORK
+#if defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+ /*
+ * This bogosity is necessary because Linux uses different
+ * PIDs for different threads (like IRIX, which we don't
+ * support). We can tell whether we are linked against
+ * libpthreads by whether __pthread_once is NULL or
+ * not. If it is NULL, then we're not linked with the
+ * threading library, and we need to compare the current
+ * process ID against the saved one to figure out
+ * whether we've forked.
+ *
+ * Once we know whether we have forked or not,
+ * courtesy of pthread_atfork() or us checking
+ * ourselves, we can close the socket to the LDAP
+ * server to avoid leaking a socket, and reopen
+ * another connection. Under no circumstances do we
+ * wish to use the same connection, or to send an
+ * unbind PDU over the parents connection, as that
+ * will wreak all sorts of havoc or inefficiencies,
+ * respectively.
+ */
+ if (__pthread_once == NULL)
+ pid = getpid ();
+ else
+ pid = -1; /* linked against libpthreads, don't care */
+#else
+ pid = getpid ();
+#endif /* HAVE_LIBC_LOCK_H || HAVE_BITS_LIBC_LOCK_H */
+#endif /* HAVE_PTHREAD_ATFORK */
+
+ euid = geteuid ();
+
+#ifdef DEBUG
+#ifdef HAVE_PTHREAD_ATFORK
+ syslog (LOG_AUTHPRIV | LOG_DEBUG,
+ "nss_ldap: __session.ls_state=%d, __session.ls_conn=%p, __euid=%i, euid=%i",
+ __session.ls_state, __session.ls_conn, __euid, euid);
+#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+ syslog (LOG_AUTHPRIV | LOG_DEBUG,
+ "nss_ldap: libpthreads=%s, __session.ls_state=%d, __session.ls_conn=%p, __pid=%i, pid=%i, __euid=%i, euid=%i",
+ (__pthread_once == NULL ? "FALSE" : "TRUE"),
+ __session.ls_state,
+ (void *)__session.ls_conn,
+ (__pthread_once == NULL ? __pid : -1),
+ (__pthread_once == NULL ? pid : -1), __euid, euid);
+#else
+ syslog (LOG_AUTHPRIV | LOG_DEBUG,
+ "nss_ldap: __session.ls_state=%d, __session.ls_conn=%p, __pid=%i, pid=%i, __euid=%i, euid=%i",
+ __session.ls_state, __session.ls_conn, __pid, pid, __euid, euid);
+#endif
+#endif /* DEBUG */
+
+ if (__session.ls_state == LS_CONNECTED_TO_DSA &&
+ do_get_our_socket (&sd) == 0)
+ {
+ /* The calling app has stolen our socket. */
+ debug (":== do_init (stolen socket detected)");
+ do_drop_connection (sd, 0);
+ }
+ else
+#ifndef HAVE_PTHREAD_ATFORK
+#if defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+ if (__pthread_once == NULL && __pid != pid)
+#else
+ if (__pid != pid)
+#endif /* HAVE_LIBC_LOCK_H || HAVE_BITS_LIBC_LOCK_H */
+ {
+ do_close_no_unbind ();
+ }
+ else
+#endif /* HAVE_PTHREAD_ATFORK */
+ if (__euid != euid && (__euid == 0 || euid == 0))
+ {
+ /*
+ * If we've changed user ids, close the session so we can
+ * rebind as the correct user.
+ */
+ do_close ();
+ }
+ else if (__session.ls_state == LS_CONNECTED_TO_DSA)
+ {
+ time_t current_time;
+
+ /*
+ * Otherwise we can hand back this process' global
+ * LDAP session.
+ *
+ * Patch from Steven Barrus <sbarrus@eng.utah.edu> to
+ * close the session after an idle timeout.
+ */
+
+ assert (__session.ls_conn != NULL);
+ assert (__session.ls_config != NULL);
+
+ if (__session.ls_config->ldc_idle_timelimit)
+ {
+ time (&current_time);
+ if ((__session.ls_timestamp +
+ __session.ls_config->ldc_idle_timelimit) < current_time)
+ {
+ debug ("idle_timelimit reached");
+ do_close ();
+ }
+ }
+
+ /*
+ * If the connection is still there (ie. do_close() wasn't
+ * called) then we can return the cached connection.
+ */
+ if (__session.ls_state == LS_CONNECTED_TO_DSA)
+ {
+ debug ("<== do_init (cached session)");
+ return NSS_STATUS_SUCCESS;
+ }
+ }
+
+ __session.ls_conn = NULL;
+ __session.ls_timestamp = 0;
+ __session.ls_state = LS_UNINITIALIZED;
+
+#ifdef HAVE_PTHREAD_ATFORK
+ if (pthread_once (&__once, do_atfork_setup) != 0)
+ {
+ debug ("<== do_init (pthread_once failed)");
+ return NSS_STATUS_UNAVAIL;
+ }
+#elif defined(HAVE_LIBC_LOCK_H) || defined(HAVE_BITS_LIBC_LOCK_H)
+ /*
+ * Only install the pthread_atfork() handlers i
+ * we are linked against libpthreads. Otherwise,
+ * do close the session when the PID changes.
+ */
+ if (__pthread_once == NULL)
+ __pid = pid;
+ else
+ __libc_once (__once, do_atfork_setup);
+#else
+ __pid = pid;
+#endif
+
+ __euid = euid;
+
+ /* Initialize schema and LDAP handle (but do not connect) */
+ if (__config == NULL)
+ {
+ char *configbufp = __configbuf;
+ size_t configbuflen = sizeof (__configbuf);
+
+ stat = _nss_ldap_readconfig (&__config, &configbufp, &configbuflen);
+ if (stat == NSS_STATUS_NOTFOUND)
+ {
+ /* Config was read but no host information specified; try DNS */
+ stat = _nss_ldap_mergeconfigfromdns (__config, &configbufp, &configbuflen);
+ }
+
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== do_init (failed to read config)");
+ return NSS_STATUS_UNAVAIL;
+ }
+ }
+
+ cfg = __config;
+
+ _nss_ldap_init_attributes (cfg->ldc_attrtab);
+ _nss_ldap_init_filters ();
+
+#ifdef HAVE_LDAP_SET_OPTION
+ if (cfg->ldc_debug)
+ {
+#ifdef LBER_OPT_LOG_PRINT_FILE
+ if (cfg->ldc_logdir && !__debugfile)
+ {
+ char namebuf[PATH_MAX];
+
+ snprintf (namebuf, sizeof (namebuf), "%s/ldap.%d", cfg->ldc_logdir,
+ (int) getpid ());
+ __debugfile = fopen (namebuf, "a");
+
+ if (__debugfile != NULL)
+ {
+ ber_set_option (NULL, LBER_OPT_LOG_PRINT_FILE, __debugfile);
+ }
+ }
+#endif /* LBER_OPT_LOG_PRINT_FILE */
+#ifdef LBER_OPT_DEBUG_LEVEL
+ if (cfg->ldc_debug)
+ {
+ ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL, &cfg->ldc_debug);
+ ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &cfg->ldc_debug);
+ }
+#endif /* LBER_OPT_DEBUG_LEVEL */
+ }
+#endif /* HAVE_LDAP_SET_OPTION */
+
+#ifdef HAVE_LDAPSSL_CLIENT_INIT
+ /*
+ * Initialize the SSL library.
+ */
+ if (cfg->ldc_ssl_on == SSL_LDAPS)
+ {
+ int rc = 0;
+ if (__ssl_initialized == 0
+ && (rc = ldapssl_client_init (cfg->ldc_sslpath, NULL)) != LDAP_SUCCESS)
+ {
+ debug ("<== do_init (ldapssl_client_init failed with rc = %d)", rc);
+ return NSS_STATUS_UNAVAIL;
+ }
+ __ssl_initialized = 1;
+ }
+#endif /* SSL */
+
+ __session.ls_conn = NULL;
+
+ assert (__session.ls_current_uri <= NSS_LDAP_CONFIG_URI_MAX);
+ assert (cfg->ldc_uris[__session.ls_current_uri] != NULL);
+
+ stat = do_init_session (&__session.ls_conn,
+ cfg->ldc_uris[__session.ls_current_uri],
+ cfg->ldc_port);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== do_init (failed to initialize LDAP session)");
+ return stat;
+ }
+
+ __session.ls_config = cfg;
+ __session.ls_state = LS_INITIALIZED;
+
+ debug ("<== do_init (initialized session)");
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * A simple alias around do_init().
+ */
+enum nss_status
+_nss_ldap_init (void)
+{
+ return do_init ();
+}
+
+/*
+ * A simple alias around do_close().
+ */
+void
+_nss_ldap_close (void)
+{
+ do_close ();
+}
+
+#if defined(HAVE_LDAP_START_TLS_S) || defined(HAVE_LDAP_START_TLS)
+static int
+do_start_tls (struct ldap_session * session)
+{
+ int rc;
+#ifdef HAVE_LDAP_START_TLS
+ int msgid;
+ struct timeval tv, *timeout;
+ LDAPMessage *res = NULL;
+
+ debug ("==> do_start_tls");
+
+ rc = ldap_start_tls (session->ls_conn, NULL, NULL, &msgid);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug ("<== do_start_tls (ldap_start_tls failed: %s)", ldap_err2string (rc));
+ return rc;
+ }
+
+ if (session->ls_config->ldc_bind_timelimit == LDAP_NO_LIMIT)
+ {
+ timeout = NULL;
+ }
+ else
+ {
+ tv.tv_sec = session->ls_config->ldc_bind_timelimit;
+ tv.tv_usec = 0;
+ timeout = &tv;
+ }
+
+ rc = ldap_result (session->ls_conn, msgid, 1, timeout, &res);
+ if (rc == -1)
+ {
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
+ if (ldap_get_option (session->ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
+ {
+ rc = LDAP_UNAVAILABLE;
+ }
+#else
+ rc = ld->ld_errno;
+#endif /* LDAP_OPT_ERROR_NUMBER */
+
+ debug ("<== do_start_tls (ldap_start_tls failed: %s)", ldap_err2string (rc));
+ return rc;
+ }
+
+ rc = ldap_result2error (session->ls_conn, res, 1);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug ("<== do_start_tls (ldap_result2error failed: %s)", ldap_err2string (rc));
+ return rc;
+ }
+
+ rc = ldap_install_tls (session->ls_conn);
+#else
+ rc = ldap_start_tls_s (session->ls_conn, NULL, NULL);
+#endif /* HAVE_LDAP_START_TLS */
+
+ if (rc != LDAP_SUCCESS)
+ {
+ debug ("<== do_start_tls (start TLS failed: %s)", ldap_err2string(rc));
+ return rc;
+ }
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+/*
+ * Opens connection to an LDAP server - should only be called from search
+ * API. Other API that just needs access to configuration and schema should
+ * call do_init().
+ *
+ * As with do_close(), this assumes ownership of sess.
+ * It also wants to own __config: is there a potential deadlock here? XXX
+ */
+static enum nss_status
+do_open (void)
+{
+ struct ldap_config *cfg;
+ int usesasl;
+ char *bindarg;
+ enum nss_status stat;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+ struct timeval tv;
+#endif
+#ifdef LDAP_X_OPT_CONNECT_TIMEOUT
+ int timeout;
+#endif
+ int rc;
+
+ debug ("==> do_open");
+
+ /* Moved the head part of do_open() into do_init() */
+ stat = do_init ();
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== do_open (session initialization failed)");
+ return stat;
+ }
+
+ assert (__session.ls_conn != NULL);
+ assert (__session.ls_config != NULL);
+ assert (__session.ls_state != LS_UNINITIALIZED);
+
+ if (__session.ls_state == LS_CONNECTED_TO_DSA)
+ {
+ debug ("<== do_open (cached session)");
+ return NSS_STATUS_SUCCESS;
+ }
+
+ cfg = __session.ls_config;
+
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc (__session.ls_conn, do_rebind, NULL);
+#elif LDAP_SET_REBIND_PROC_ARGS == 2
+ ldap_set_rebind_proc (__session.ls_conn, do_rebind);
+#endif
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_PROTOCOL_VERSION)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION,
+ &cfg->ldc_version);
+#else
+ __session.ls_conn->ld_version = cfg->ldc_version;
+#endif /* LDAP_OPT_PROTOCOL_VERSION */
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_DEREF)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_DEREF, &cfg->ldc_deref);
+#else
+ __session.ls_conn->ld_deref = cfg->ldc_deref;
+#endif /* LDAP_OPT_DEREF */
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_TIMELIMIT)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_TIMELIMIT,
+ &cfg->ldc_timelimit);
+#else
+ __session.ls_conn->ld_timelimit = cfg->ldc_timelimit;
+#endif /* LDAP_OPT_TIMELIMIT */
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_X_OPT_CONNECT_TIMEOUT)
+ /*
+ * This is a new option in the Netscape SDK which sets
+ * the TCP connect timeout. For want of a better value,
+ * we use the bind_timelimit to control this.
+ */
+ timeout = cfg->ldc_bind_timelimit * 1000;
+ ldap_set_option (__session.ls_conn, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout);
+#endif /* LDAP_X_OPT_CONNECT_TIMEOUT */
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_NETWORK_TIMEOUT)
+ tv.tv_sec = cfg->ldc_bind_timelimit;
+ tv.tv_usec = 0;
+ ldap_set_option (__session.ls_conn, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+#endif /* LDAP_OPT_NETWORK_TIMEOUT */
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_REFERRALS)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_REFERRALS,
+ cfg->ldc_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF);
+#endif
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_RESTART)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_RESTART,
+ cfg->ldc_restart ? LDAP_OPT_ON : LDAP_OPT_OFF);
+#endif
+
+#if defined(HAVE_LDAP_START_TLS_S) || defined(HAVE_LDAP_START_TLS)
+ if (cfg->ldc_ssl_on == SSL_START_TLS)
+ {
+ int version;
+
+ if (ldap_get_option
+ (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION,
+ &version) == LDAP_OPT_SUCCESS)
+ {
+ if (version < LDAP_VERSION3)
+ {
+ version = LDAP_VERSION3;
+ ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION,
+ &version);
+ }
+ }
+
+ /* set up SSL context */
+ if (do_ssl_options (cfg) != LDAP_SUCCESS)
+ {
+ do_close ();
+ debug ("<== do_open (SSL setup failed)");
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ stat = do_map_error (do_start_tls (&__session));
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ debug (":== do_open (TLS startup succeeded)");
+ }
+ else
+ {
+ do_close ();
+ debug ("<== do_open (TLS startup failed)");
+ return stat;
+ }
+ }
+ else
+#endif /* HAVE_LDAP_START_TLS_S || HAVE_LDAP_START_TLS */
+
+ /*
+ * If SSL is desired, then enable it.
+ */
+ if (cfg->ldc_ssl_on == SSL_LDAPS)
+ {
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)
+ int tls = LDAP_OPT_X_TLS_HARD;
+ if (ldap_set_option (__session.ls_conn, LDAP_OPT_X_TLS, &tls) !=
+ LDAP_SUCCESS)
+ {
+ do_close ();
+ debug ("<== do_open (TLS setup failed)");
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ /* set up SSL context */
+ if (do_ssl_options (cfg) != LDAP_SUCCESS)
+ {
+ do_close ();
+ debug ("<== do_open (SSL setup failed)");
+ return NSS_STATUS_UNAVAIL;
+ }
+
+#elif defined(HAVE_LDAPSSL_CLIENT_INIT)
+ if (ldapssl_install_routines (__session.ls_conn) != LDAP_SUCCESS)
+ {
+ do_close ();
+ debug ("<== do_open (SSL setup failed)");
+ return NSS_STATUS_UNAVAIL;
+ }
+/* not in Solaris 9? */
+#ifndef LDAP_OPT_SSL
+#define LDAP_OPT_SSL 0x0A
+#endif
+ if (ldap_set_option (__session.ls_conn, LDAP_OPT_SSL, LDAP_OPT_ON) !=
+ LDAP_SUCCESS)
+ {
+ do_close ();
+ debug ("<== do_open (SSL setup failed)");
+ return NSS_STATUS_UNAVAIL;
+ }
+#endif
+ }
+
+ /*
+ * If we're running as root, let us bind as a special
+ * user, so we can fake shadow passwords.
+ * Thanks to Doug Nazar <nazard@dragoninc.on.ca> for this
+ * patch.
+ */
+ if (__euid == 0 && cfg->ldc_rootbinddn != NULL)
+ {
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))
+ usesasl = cfg->ldc_rootusesasl;
+ bindarg =
+ cfg->ldc_rootusesasl ? cfg->ldc_rootsaslid : cfg->ldc_rootbindpw;
+#else
+ usesasl = 0;
+ bindarg = cfg->ldc_rootbindpw;
+#endif
+
+ rc = do_bind (__session.ls_conn,
+ cfg->ldc_bind_timelimit,
+ cfg->ldc_rootbinddn, bindarg, usesasl);
+ }
+ else
+ {
+#if defined(HAVE_LDAP_SASL_INTERACTIVE_BIND_S) && (defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H))
+ usesasl = cfg->ldc_usesasl;
+ bindarg = cfg->ldc_usesasl ? cfg->ldc_saslid : cfg->ldc_bindpw;
+#else
+ usesasl = 0;
+ bindarg = cfg->ldc_bindpw;
+#endif
+
+ rc = do_bind (__session.ls_conn,
+ cfg->ldc_bind_timelimit,
+ cfg->ldc_binddn,
+ cfg->ldc_bindpw, usesasl);
+ }
+
+ if (rc != LDAP_SUCCESS)
+ {
+ /* log actual LDAP error code */
+ syslog (LOG_AUTHPRIV | LOG_INFO,
+ "nss_ldap: failed to bind to LDAP server %s: %s",
+ cfg->ldc_uris[__session.ls_current_uri],
+ ldap_err2string (rc));
+ stat = do_map_error (rc);
+ do_close ();
+ debug ("<== do_open (failed to bind to DSA");
+ }
+ else
+ {
+ do_set_sockopts ();
+ time (&__session.ls_timestamp);
+ __session.ls_state = LS_CONNECTED_TO_DSA;
+ stat = NSS_STATUS_SUCCESS;
+ debug ("<== do_open (session connected to DSA)");
+ }
+
+ return stat;
+}
+
+#if defined HAVE_LDAP_START_TLS_S || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS))
+static int
+do_ssl_options (struct ldap_config * cfg)
+{
+ int rc;
+
+ debug ("==> do_ssl_options");
+
+#ifdef LDAP_OPT_X_TLS_RANDOM_FILE
+ if (cfg->ldc_tls_randfile != NULL)
+ {
+ /* rand file */
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
+ cfg->ldc_tls_randfile);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_RANDOM_FILE failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+#endif /* LDAP_OPT_X_TLS_RANDOM_FILE */
+
+ if (cfg->ldc_tls_cacertfile != NULL)
+ {
+ /* ca cert file */
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE,
+ cfg->ldc_tls_cacertfile);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CACERTFILE failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ if (cfg->ldc_tls_cacertdir != NULL)
+ {
+ /* ca cert directory */
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR,
+ cfg->ldc_tls_cacertdir);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CACERTDIR failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ /* require cert? */
+ if (cfg->ldc_tls_checkpeer > -1)
+ {
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
+ &cfg->ldc_tls_checkpeer);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_REQUIRE_CERT failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ if (cfg->ldc_tls_ciphers != NULL)
+ {
+ /* set cipher suite, certificate and private key: */
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
+ cfg->ldc_tls_ciphers);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CIPHER_SUITE failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ if (cfg->ldc_tls_cert != NULL)
+ {
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CERTFILE, cfg->ldc_tls_cert);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_CERTFILE failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ if (cfg->ldc_tls_key != NULL)
+ {
+ rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_KEYFILE, cfg->ldc_tls_key);
+ if (rc != LDAP_SUCCESS)
+ {
+ debug
+ ("<== do_ssl_options: Setting of LDAP_OPT_X_TLS_KEYFILE failed");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ debug ("<== do_ssl_options");
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+/*
+ * This function initializes an enumeration context, acquiring
+ * the global mutex.
+ *
+ * It could be done from the default constructor, under Solaris, but we
+ * delay it until the setXXent() function is called.
+ */
+struct ent_context *
+_nss_ldap_ent_context_init (struct ent_context ** pctx)
+{
+ struct ent_context *ctx;
+
+ _nss_ldap_enter ();
+
+ ctx = _nss_ldap_ent_context_init_locked (pctx);
+
+ _nss_ldap_leave ();
+
+ return ctx;
+}
+
+/*
+ * Wrapper around ldap_result() to skip over search references
+ * and deal transparently with the last entry.
+ */
+static enum nss_status
+do_result (struct ent_context * ctx, int all)
+{
+ int rc = LDAP_UNAVAILABLE;
+ enum nss_status stat = NSS_STATUS_TRYAGAIN;
+ struct timeval tv, *tvp;
+
+ debug ("==> do_result");
+
+ if (__session.ls_config->ldc_timelimit == LDAP_NO_LIMIT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = __session.ls_config->ldc_timelimit;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ do
+ {
+ if (ctx->ec_res != NULL)
+ {
+ ldap_msgfree (ctx->ec_res);
+ ctx->ec_res = NULL;
+ }
+
+ rc =
+ ldap_result (__session.ls_conn, ctx->ec_msgid, all, tvp,
+ &ctx->ec_res);
+ switch (rc)
+ {
+ case -1:
+ case 0:
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
+ if (ldap_get_option
+ (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
+ {
+ rc = LDAP_UNAVAILABLE;
+ }
+#else
+ rc = __session.ls_conn->ld_errno;
+#endif /* LDAP_OPT_ERROR_NUMBER */
+ syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not get LDAP result - %s",
+ ldap_err2string (rc));
+ stat = NSS_STATUS_UNAVAIL;
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ stat = NSS_STATUS_SUCCESS;
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ if (all == LDAP_MSG_ALL)
+ {
+ /* we asked for the result chain, we got it. */
+ stat = NSS_STATUS_SUCCESS;
+ }
+ else
+ {
+#ifdef LDAP_MORE_RESULTS_TO_RETURN
+ int parserc;
+ /* NB: this frees ctx->ec_res */
+ LDAPControl **resultControls = NULL;
+
+ ctx->ec_cookie = NULL;
+
+ parserc =
+ ldap_parse_result (__session.ls_conn, ctx->ec_res, &rc, NULL,
+ NULL, NULL, &resultControls, 1);
+ if (parserc != LDAP_SUCCESS
+ && parserc != LDAP_MORE_RESULTS_TO_RETURN)
+ {
+ stat = NSS_STATUS_UNAVAIL;
+ ldap_abandon (__session.ls_conn, ctx->ec_msgid);
+ syslog (LOG_AUTHPRIV | LOG_ERR,
+ "nss_ldap: could not get LDAP result - %s",
+ ldap_err2string (rc));
+ }
+ else if (resultControls != NULL)
+ {
+ /* See if there are any more pages to come */
+ parserc = ldap_parse_page_control (__session.ls_conn,
+ resultControls, NULL,
+ &(ctx->ec_cookie));
+ ldap_controls_free (resultControls);
+ stat = NSS_STATUS_NOTFOUND;
+ }
+ else
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ }
+#else
+ stat = NSS_STATUS_NOTFOUND;
+#endif /* LDAP_MORE_RESULTS_TO_RETURN */
+ ctx->ec_res = NULL;
+ ctx->ec_msgid = -1;
+ }
+ break;
+ default:
+ stat = NSS_STATUS_UNAVAIL;
+ break;
+ }
+ }
+#ifdef LDAP_RES_SEARCH_REFERENCE
+ while (rc == LDAP_RES_SEARCH_REFERENCE);
+#else
+ while (0);
+#endif /* LDAP_RES_SEARCH_REFERENCE */
+
+ if (stat == NSS_STATUS_SUCCESS)
+ time (&__session.ls_timestamp);
+
+ debug ("<== do_result");
+
+ return stat;
+}
+
+/*
+ * This function initializes an enumeration context.
+ *
+ * It could be done from the default constructor, under Solaris, but we
+ * delay it until the setXXent() function is called.
+ */
+struct ent_context *
+_nss_ldap_ent_context_init_locked (struct ent_context ** pctx)
+{
+ struct ent_context *ctx;
+
+ debug ("==> _nss_ldap_ent_context_init_locked");
+
+ ctx = *pctx;
+
+ if (ctx == NULL)
+ {
+ ctx = (struct ent_context *) malloc (sizeof (*ctx));
+ if (ctx == NULL)
+ {
+ debug ("<== _nss_ldap_ent_context_init_locked");
+ return NULL;
+ }
+ *pctx = ctx;
+ }
+ else
+ {
+ if (ctx->ec_res != NULL)
+ {
+ ldap_msgfree (ctx->ec_res);
+ }
+ if (ctx->ec_cookie != NULL)
+ {
+ ber_bvfree (ctx->ec_cookie);
+ }
+ if (ctx->ec_msgid > -1 && do_result (ctx, LDAP_MSG_ONE) == NSS_STATUS_SUCCESS)
+ {
+ ldap_abandon (__session.ls_conn, ctx->ec_msgid);
+ }
+ }
+
+ ctx->ec_cookie = NULL;
+ ctx->ec_res = NULL;
+ ctx->ec_msgid = -1;
+ ctx->ec_sd = NULL;
+
+ LS_INIT (ctx->ec_state);
+
+ debug ("<== _nss_ldap_ent_context_init_locked");
+
+ return ctx;
+}
+
+/*
+ * Clears a given context; we require the caller
+ * to acquire the lock.
+ */
+void
+_nss_ldap_ent_context_release (struct ent_context * ctx)
+{
+ debug ("==> _nss_ldap_ent_context_release");
+
+ if (ctx == NULL)
+ {
+ debug ("<== _nss_ldap_ent_context_release");
+ return;
+ }
+
+ if (ctx->ec_res != NULL)
+ {
+ ldap_msgfree (ctx->ec_res);
+ ctx->ec_res = NULL;
+ }
+
+ /*
+ * Abandon the search if there were more results to fetch.
+ */
+ if (ctx->ec_msgid > -1 && do_result (ctx, LDAP_MSG_ONE) == NSS_STATUS_SUCCESS)
+ {
+ ldap_abandon (__session.ls_conn, ctx->ec_msgid);
+ ctx->ec_msgid = -1;
+ }
+
+ if (ctx->ec_cookie != NULL)
+ {
+ ber_bvfree (ctx->ec_cookie);
+ ctx->ec_cookie = NULL;
+ }
+
+ ctx->ec_sd = NULL;
+
+ LS_INIT (ctx->ec_state);
+
+ if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_CONNECT_POLICY_ONESHOT))
+ {
+ do_close ();
+ }
+
+ debug ("<== _nss_ldap_ent_context_release");
+
+ return;
+}
+
+/*
+ * AND or OR a set of filters.
+ */
+static enum nss_status
+do_aggregate_filter (const char **values,
+ enum ldap_args_types type,
+ const char *filterprot, char *bufptr, size_t buflen)
+{
+ enum nss_status stat;
+ const char **valueP;
+
+ assert (buflen > sizeof ("(|)"));
+
+ bufptr[0] = '(';
+ bufptr[1] = (type == LA_TYPE_STRING_LIST_AND) ? '&' : '|';
+
+ bufptr += 2;
+ buflen -= 2;
+
+ for (valueP = values; *valueP != NULL; valueP++)
+ {
+ size_t len;
+ char filter[LDAP_FILT_MAXSIZ], escapedBuf[LDAP_FILT_MAXSIZ];
+
+ stat =
+ _nss_ldap_escape_string (*valueP, escapedBuf, sizeof (escapedBuf));
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ snprintf (filter, sizeof (filter), filterprot, escapedBuf);
+ len = strlen (filter);
+
+ if (buflen < len + 1 /* ')' */ )
+ return NSS_STATUS_TRYAGAIN;
+
+ memcpy (bufptr, filter, len);
+ bufptr[len] = '\0';
+ bufptr += len;
+ buflen -= len;
+ }
+
+ if (buflen < 2)
+ return NSS_STATUS_TRYAGAIN;
+
+ *bufptr++ = ')';
+ *bufptr++ = '\0';
+
+ buflen -= 2;
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Do the necessary formatting to create a string filter.
+ */
+static enum nss_status
+do_filter (const struct ldap_args * args, const char *filterprot,
+ struct ldap_service_search_descriptor * sd, char *userBuf,
+ size_t userBufSiz, char **dynamicUserBuf, const char **retFilter)
+{
+ char buf1[LDAP_FILT_MAXSIZ], buf2[LDAP_FILT_MAXSIZ];
+ char *filterBufP, filterBuf[LDAP_FILT_MAXSIZ];
+ size_t filterSiz;
+ enum nss_status stat = NSS_STATUS_SUCCESS;
+
+ debug ("==> do_filter");
+
+ *dynamicUserBuf = NULL;
+
+ if (args != NULL && args->la_type != LA_TYPE_NONE)
+ {
+ /* choose what to use for temporary storage */
+
+ if (sd != NULL && sd->lsd_filter != NULL)
+ {
+ filterBufP = filterBuf;
+ filterSiz = sizeof (filterBuf);
+ }
+ else
+ {
+ filterBufP = userBuf;
+ filterSiz = userBufSiz;
+ }
+
+ switch (args->la_type)
+ {
+ case LA_TYPE_STRING:
+ stat = _nss_ldap_escape_string (args->la_arg1.la_string, buf1,
+ sizeof (buf1));
+ if (stat != NSS_STATUS_SUCCESS)
+ break;
+
+ snprintf (filterBufP, filterSiz, filterprot, buf1);
+ break;
+ case LA_TYPE_NUMBER:
+ snprintf (filterBufP, filterSiz, filterprot,
+ args->la_arg1.la_number);
+ break;
+ case LA_TYPE_STRING_AND_STRING:
+ stat = _nss_ldap_escape_string (args->la_arg1.la_string, buf1,
+ sizeof (buf1));
+ if (stat != NSS_STATUS_SUCCESS)
+ break;
+
+ stat = _nss_ldap_escape_string (args->la_arg2.la_string, buf2,
+ sizeof (buf2));
+ if (stat != NSS_STATUS_SUCCESS)
+ break;
+
+ snprintf (filterBufP, filterSiz, filterprot, buf1, buf2);
+ break;
+ case LA_TYPE_NUMBER_AND_STRING:
+ stat = _nss_ldap_escape_string (args->la_arg2.la_string, buf1,
+ sizeof (buf1));
+ if (stat != NSS_STATUS_SUCCESS)
+ break;
+
+ snprintf (filterBufP, filterSiz, filterprot,
+ args->la_arg1.la_number, buf1);
+ break;
+ case LA_TYPE_STRING_LIST_OR:
+ case LA_TYPE_STRING_LIST_AND:
+ do
+ {
+ stat = do_aggregate_filter (args->la_arg1.la_string_list,
+ args->la_type,
+ filterprot, filterBufP, filterSiz);
+ if (stat == NSS_STATUS_TRYAGAIN)
+ {
+ filterBufP = *dynamicUserBuf = realloc (*dynamicUserBuf,
+ 2 * filterSiz);
+ if (filterBufP == NULL)
+ return NSS_STATUS_UNAVAIL;
+ filterSiz *= 2;
+ }
+ }
+ while (stat == NSS_STATUS_TRYAGAIN);
+ break;
+ default:
+ return NSS_STATUS_UNAVAIL;
+ break;
+ }
+
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ /*
+ * This code really needs to be cleaned up.
+ */
+ if (sd != NULL && sd->lsd_filter != NULL)
+ {
+ size_t filterBufPLen = strlen (filterBufP);
+
+ /* remove trailing bracket */
+ if (filterBufP[filterBufPLen - 1] == ')')
+ filterBufP[filterBufPLen - 1] = '\0';
+
+ if (*dynamicUserBuf != NULL)
+ {
+ char *oldDynamicUserBuf = *dynamicUserBuf;
+ size_t dynamicUserBufSiz;
+
+ dynamicUserBufSiz = filterBufPLen + strlen (sd->lsd_filter) +
+ sizeof ("())");
+ *dynamicUserBuf = malloc (dynamicUserBufSiz);
+ if (*dynamicUserBuf == NULL)
+ {
+ free (oldDynamicUserBuf);
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ snprintf (*dynamicUserBuf, dynamicUserBufSiz, "%s(%s))",
+ filterBufP, sd->lsd_filter);
+ free (oldDynamicUserBuf);
+ }
+ else
+ {
+ snprintf (userBuf, userBufSiz, "%s(%s))",
+ filterBufP, sd->lsd_filter);
+ }
+ }
+
+ if (*dynamicUserBuf != NULL)
+ *retFilter = *dynamicUserBuf;
+ else
+ *retFilter = userBuf;
+ }
+ else
+ {
+ /* no arguments, probably an enumeration filter */
+ if (sd != NULL && sd->lsd_filter != NULL)
+ {
+ snprintf (userBuf, userBufSiz, "(&%s(%s))",
+ filterprot, sd->lsd_filter);
+ *retFilter = userBuf;
+ }
+ else
+ {
+ *retFilter = filterprot;
+ }
+ }
+
+ debug (":== do_filter: %s", *retFilter);
+
+ debug ("<== do_filter");
+
+ return NSS_STATUS_SUCCESS;
+}
+
+/*
+ * Function to call either do_search() or do_search_s() with
+ * reconnection logic.
+ */
+static enum nss_status
+do_with_reconnect (const char *base, int scope,
+ const char *filter, const char **attrs, int sizelimit,
+ void *private, search_func_t search_func)
+{
+ int rc = LDAP_UNAVAILABLE, tries = 0, backoff = 0;
+ int hard = 1, start_uri = 0, log = 0;
+ enum nss_status stat = NSS_STATUS_UNAVAIL;
+ int maxtries;
+
+ debug ("==> do_with_reconnect");
+
+ /* caller must successfully call do_init() first */
+ assert (__session.ls_config != NULL);
+
+ maxtries = __session.ls_config->ldc_reconnect_maxconntries +
+ __session.ls_config->ldc_reconnect_tries;
+
+ while (stat == NSS_STATUS_UNAVAIL && hard && tries < maxtries)
+ {
+ if (tries >= __session.ls_config->ldc_reconnect_maxconntries)
+ {
+ if (backoff == 0)
+ backoff = __session.ls_config->ldc_reconnect_sleeptime;
+ else if (backoff < __session.ls_config->ldc_reconnect_maxsleeptime)
+ backoff *= 2;
+
+ syslog (LOG_AUTHPRIV | LOG_INFO,
+ "nss_ldap: reconnecting to LDAP server (sleeping %d seconds)...",
+ backoff);
+ (void) sleep (backoff);
+ }
+ else if (tries > 1)
+ {
+ /* Don't sleep, reconnect immediately. */
+ syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: reconnecting to LDAP server...");
+ }
+
+ /* For each "try", attempt to connect to all specified URIs */
+ start_uri = __session.ls_current_uri;
+ do
+ {
+ stat = do_open ();
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ stat = do_map_error (search_func (base, scope, filter,
+ attrs, sizelimit, private));
+ }
+ if (stat != NSS_STATUS_UNAVAIL)
+ break;
+
+ log++;
+
+ /* test in case config file could not be read */
+ if (__session.ls_config != NULL)
+ {
+ assert (__session.ls_config->
+ ldc_uris[__session.ls_current_uri] != NULL);
+
+ __session.ls_current_uri++;
+
+ if (__session.ls_config->ldc_uris[__session.ls_current_uri] ==
+ NULL)
+ __session.ls_current_uri = 0;
+ }
+ }
+ while (__session.ls_current_uri != start_uri);
+
+ if (stat == NSS_STATUS_UNAVAIL)
+ {
+ do_close ();
+
+ /*
+ * If a soft reconnect policy is specified, then do not
+ * try to reconnect to the LDAP server if it is down.
+ */
+ if (__session.ls_config->ldc_reconnect_pol == LP_RECONNECT_SOFT)
+ hard = 0;
+
+ /*
+ * If the file /lib/init/rw/libnss-ldap.bind_policy_soft exists,
+ * then ignore the actual bind_policy definition and use the
+ * soft semantics. This file should only exist during early
+ * boot and late shutdown, points at which the networking or
+ * the LDAP server itself are likely to be unavailable anyway.
+ */
+ if (access("/lib/init/rw/libnss-ldap.bind_policy_soft",R_OK) == 0)
+ hard = 0;
+
+ ++tries;
+ }
+ }
+
+ switch (stat)
+ {
+ case NSS_STATUS_UNAVAIL:
+ syslog (LOG_AUTHPRIV | LOG_ERR, "nss_ldap: could not search LDAP server - %s",
+ ldap_err2string (rc));
+ break;
+ case NSS_STATUS_TRYAGAIN:
+ syslog (LOG_AUTHPRIV | LOG_ERR,
+ "nss_ldap: could not %s %sconnect to LDAP server - %s",
+ hard ? "hard" : "soft", tries ? "re" : "",
+ ldap_err2string (rc));
+ stat = NSS_STATUS_UNAVAIL;
+ break;
+ case NSS_STATUS_SUCCESS:
+ if (log)
+ {
+ char *uri = __session.ls_config->ldc_uris[__session.ls_current_uri];
+
+ if (uri == NULL)
+ uri = "(null)";
+
+ if (tries)
+ syslog (LOG_AUTHPRIV | LOG_INFO,
+ "nss_ldap: reconnected to LDAP server %s after %d attempt%s",
+ uri, tries, (tries == 1) ? "" : "s");
+ else
+ syslog (LOG_AUTHPRIV | LOG_INFO, "nss_ldap: reconnected to LDAP server %s", uri);
+ }
+ time (&__session.ls_timestamp);
+ break;
+ default:
+ break;
+ }
+
+ debug ("<== do_with_reconnect");
+ return stat;
+}
+
+/*
+ * Synchronous search function. Don't call this directly;
+ * always wrap calls to this with do_with_reconnect(), or,
+ * better still, use _nss_ldap_search_s().
+ */
+static int
+do_search_s (const char *base, int scope,
+ const char *filter, const char **attrs, int sizelimit,
+ LDAPMessage ** res)
+{
+ int rc;
+ struct timeval tv, *tvp;
+
+ debug ("==> do_search_s");
+
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_SIZELIMIT)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_SIZELIMIT,
+ (void *) &sizelimit);
+#else
+ __session.ls_conn->ld_sizelimit = sizelimit;
+#endif /* LDAP_OPT_SIZELIMIT */
+
+ if (__session.ls_config->ldc_timelimit == LDAP_NO_LIMIT)
+ {
+ tvp = NULL;
+ }
+ else
+ {
+ tv.tv_sec = __session.ls_config->ldc_timelimit;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ rc = ldap_search_st (__session.ls_conn, base, scope, filter,
+ (char **) attrs, 0, tvp, res);
+
+ debug ("<== do_search_s");
+
+ return rc;
+}
+
+/*
+ * Asynchronous search function. Don't call this directly;
+ * always wrap calls to this with do_with_reconnect(), or,
+ * better still, use _nss_ldap_search().
+ */
+static int
+do_search (const char *base, int scope,
+ const char *filter, const char **attrs, int sizelimit, int *msgid)
+{
+ int rc;
+ LDAPControl *serverCtrls[2];
+ LDAPControl **pServerCtrls;
+
+ debug ("==> do_search");
+
+#ifdef HAVE_LDAP_SEARCH_EXT
+ if (_nss_ldap_test_config_flag (NSS_LDAP_FLAGS_PAGED_RESULTS))
+ {
+ rc = ldap_create_page_control (__session.ls_conn,
+ __session.ls_config->ldc_pagesize,
+ NULL, 0, &serverCtrls[0]);
+ if (rc != LDAP_SUCCESS)
+ return rc;
+
+ serverCtrls[1] = NULL;
+ pServerCtrls = serverCtrls;
+ }
+ else
+ {
+ pServerCtrls = NULL;
+ }
+
+ rc = ldap_search_ext (__session.ls_conn, base, scope, filter,
+ (char **) attrs, 0, pServerCtrls, NULL,
+ LDAP_NO_LIMIT, sizelimit, msgid);
+
+ if (pServerCtrls != NULL)
+ {
+ ldap_control_free (serverCtrls[0]);
+ serverCtrls[0] = NULL;
+ }
+
+#else
+#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_SIZELIMIT)
+ ldap_set_option (__session.ls_conn, LDAP_OPT_SIZELIMIT,
+ (void *) &sizelimit);
+#else
+ __session.ls_conn->ld_sizelimit = sizelimit;
+#endif /* LDAP_OPT_SIZELIMIT */
+
+ *msgid = ldap_search (__session.ls_conn, base, scope, filter,
+ (char **) attrs, 0);
+ if (*msgid < 0)
+ {
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
+ if (ldap_get_option
+ (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
+ {
+ rc = LDAP_UNAVAILABLE;
+ }
+#else
+ rc = __session.ls_conn->ld_errno;
+#endif /* LDAP_OPT_ERROR_NUMBER */
+ }
+ else
+ {
+ rc = LDAP_SUCCESS;
+ }
+#endif /* HAVE_LDAP_SEARCH_EXT */
+
+ debug ("<== do_search");
+
+ return rc;
+}
+
+static void
+do_map_errno (enum nss_status status, int *errnop)
+{
+ switch (status)
+ {
+ case NSS_STATUS_TRYAGAIN:
+ *errnop = ERANGE;
+ break;
+ case NSS_STATUS_NOTFOUND:
+ *errnop = ENOENT;
+ break;
+ case NSS_STATUS_SUCCESS:
+ default:
+ *errnop = 0;
+ }
+}
+
+/*
+ * Tries parser function "parser" on entries, calling do_result()
+ * to retrieve them from the LDAP server until one parses
+ * correctly or there is an exceptional condition.
+ */
+static enum nss_status
+do_parse (struct ent_context * ctx, void *result, char
+ *buffer, size_t buflen, int *errnop, parser_t parser)
+{
+ enum nss_status parseStat = NSS_STATUS_NOTFOUND;
+
+ debug ("==> do_parse");
+
+ /*
+ * if ec_state.ls_info.ls_index is non-zero, then we don't collect another
+ * entry off the LDAP chain, and instead refeed the existing result to
+ * the parser. Once the parser has finished with it, it will return
+ * NSS_STATUS_NOTFOUND and reset the index to -1, at which point we'll retrieve
+ * another entry.
+ */
+ do
+ {
+ enum nss_status resultStat = NSS_STATUS_SUCCESS;
+
+ if (ctx->ec_state.ls_retry == 0 &&
+ (ctx->ec_state.ls_type == LS_TYPE_KEY
+ || ctx->ec_state.ls_info.ls_index == -1))
+ {
+ resultStat = do_result (ctx, LDAP_MSG_ONE);
+ }
+
+ if (resultStat != NSS_STATUS_SUCCESS)
+ {
+ /* Could not get a result; bail */
+ parseStat = resultStat;
+ break;
+ }
+
+ /*
+ * We have an entry; now, try to parse it.
+ *
+ * If we do not parse the entry because of a schema
+ * violation, the parser should return NSS_STATUS_NOTFOUND.
+ * We'll keep on trying subsequent entries until we
+ * find one which is parseable, or exhaust avialable
+ * entries, whichever is first.
+ */
+ parseStat = parser (ctx->ec_res, &ctx->ec_state, result,
+ buffer, buflen);
+
+ /* hold onto the state if we're out of memory XXX */
+ ctx->ec_state.ls_retry = (parseStat == NSS_STATUS_TRYAGAIN && buffer != NULL ? 1 : 0);
+
+ /* free entry is we're moving on */
+ if (ctx->ec_state.ls_retry == 0 &&
+ (ctx->ec_state.ls_type == LS_TYPE_KEY
+ || ctx->ec_state.ls_info.ls_index == -1))
+ {
+ /* we don't need the result anymore, ditch it. */
+ ldap_msgfree (ctx->ec_res);
+ ctx->ec_res = NULL;
+ }
+ }
+ while (parseStat == NSS_STATUS_NOTFOUND);
+
+ do_map_errno (parseStat, errnop);
+
+ debug ("<== do_parse");
+
+ return parseStat;
+}
+
+/*
+ * Parse, fetching reuslts from chain instead of server.
+ */
+static enum nss_status
+do_parse_s (struct ent_context * ctx, void *result, char
+ *buffer, size_t buflen, int *errnop, parser_t parser)
+{
+ enum nss_status parseStat = NSS_STATUS_NOTFOUND;
+ LDAPMessage *e = NULL;
+
+ debug ("==> do_parse_s");
+
+ /*
+ * if ec_state.ls_info.ls_index is non-zero, then we don't collect another
+ * entry off the LDAP chain, and instead refeed the existing result to
+ * the parser. Once the parser has finished with it, it will return
+ * NSS_STATUS_NOTFOUND and reset the index to -1, at which point we'll retrieve
+ * another entry.
+ */
+ do
+ {
+ if (ctx->ec_state.ls_retry == 0 &&
+ (ctx->ec_state.ls_type == LS_TYPE_KEY
+ || ctx->ec_state.ls_info.ls_index == -1))
+ {
+ if (e == NULL)
+ e = ldap_first_entry (__session.ls_conn, ctx->ec_res);
+ else
+ e = ldap_next_entry (__session.ls_conn, e);
+ }
+
+ if (e == NULL)
+ {
+ /* Could not get a result; bail */
+ parseStat = NSS_STATUS_NOTFOUND;
+ break;
+ }
+
+ /*
+ * We have an entry; now, try to parse it.
+ *
+ * If we do not parse the entry because of a schema
+ * violation, the parser should return NSS_STATUS_NOTFOUND.
+ * We'll keep on trying subsequent entries until we
+ * find one which is parseable, or exhaust avialable
+ * entries, whichever is first.
+ */
+ parseStat = parser (e, &ctx->ec_state, result, buffer, buflen);
+
+ /* hold onto the state if we're out of memory XXX */
+ ctx->ec_state.ls_retry = (parseStat == NSS_STATUS_TRYAGAIN && buffer != NULL ? 1 : 0);
+ }
+ while (parseStat == NSS_STATUS_NOTFOUND);
+
+ do_map_errno (parseStat, errnop);
+
+ debug ("<== do_parse_s");
+
+ return parseStat;
+}
+
+/*
+ * Read an entry from the directory, a la X.500. This is used
+ * for functions that need to retrieve attributes from a DN,
+ * such as the RFC2307bis group expansion function.
+ */
+enum nss_status
+_nss_ldap_read (const char *dn, const char **attributes, LDAPMessage ** res)
+{
+ return do_with_reconnect (dn, LDAP_SCOPE_BASE, "(objectclass=*)",
+ attributes, 1, /* sizelimit */ res,
+ (search_func_t) do_search_s);
+}
+
+/*
+ * Simple wrapper around ldap_get_values(). Requires that
+ * session is already established.
+ */
+char **
+_nss_ldap_get_values (LDAPMessage * e, const char *attr)
+{
+ if (__session.ls_state != LS_CONNECTED_TO_DSA)
+ {
+ return NULL;
+ }
+ assert (__session.ls_conn != NULL);
+
+ return ldap_get_values (__session.ls_conn, e, (char *) attr);
+}
+
+/*
+ * Simple wrapper around ldap_get_dn(). Requires that
+ * session is already established.
+ */
+char *
+_nss_ldap_get_dn (LDAPMessage * e)
+{
+ if (__session.ls_state != LS_CONNECTED_TO_DSA)
+ {
+ return NULL;
+ }
+ assert (__session.ls_conn != NULL);
+
+ return ldap_get_dn (__session.ls_conn, e);
+}
+
+/*
+ * Simple wrapper around ldap_first_entry(). Requires that
+ * session is already established.
+ */
+LDAPMessage *
+_nss_ldap_first_entry (LDAPMessage * res)
+{
+ if (__session.ls_state != LS_CONNECTED_TO_DSA)
+ {
+ return NULL;
+ }
+ assert (__session.ls_conn != NULL);
+
+ return ldap_first_entry (__session.ls_conn, res);
+}
+
+/*
+ * Simple wrapper around ldap_next_entry(). Requires that
+ * session is already established.
+ */
+LDAPMessage *
+_nss_ldap_next_entry (LDAPMessage * res)
+{
+ if (__session.ls_state != LS_CONNECTED_TO_DSA)
+ {
+ return NULL;
+ }
+ assert (__session.ls_conn != NULL);
+
+ return ldap_next_entry (__session.ls_conn, res);
+}
+
+char *
+_nss_ldap_first_attribute (LDAPMessage * entry, BerElement ** berptr)
+{
+ if (__session.ls_state != LS_CONNECTED_TO_DSA)
+ {
+ return NULL;
+ }
+ assert (__session.ls_conn != NULL);
+
+ return ldap_first_attribute (__session.ls_conn, entry, berptr);
+}
+
+char *
+_nss_ldap_next_attribute (LDAPMessage * entry, BerElement * ber)
+{
+ if (__session.ls_state != LS_CONNECTED_TO_DSA)
+ {
+ return NULL;
+ }
+ assert (__session.ls_conn != NULL);
+
+ return ldap_next_attribute (__session.ls_conn, entry, ber);
+}
+
+/*
+ * The generic synchronous lookup cover function.
+ * Assumes caller holds lock.
+ */
+enum nss_status
+_nss_ldap_search_s (const struct ldap_args * args,
+ const char *filterprot, enum ldap_map_selector sel, const
+ char **user_attrs, int sizelimit, LDAPMessage ** res)
+{
+ char sdBase[LDAP_FILT_MAXSIZ];
+ const char *base = NULL;
+ char filterBuf[LDAP_FILT_MAXSIZ], *dynamicFilterBuf = NULL;
+ const char **attrs, *filter;
+ int scope;
+ enum nss_status stat;
+ struct ldap_service_search_descriptor *sd = NULL;
+
+ debug ("==> _nss_ldap_search_s");
+
+ stat = do_init ();
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== _nss_ldap_search_s");
+ return stat;
+ }
+
+ /* Set some reasonable defaults. */
+ base = __session.ls_config->ldc_base;
+ scope = __session.ls_config->ldc_scope;
+ attrs = NULL;
+
+ if (args != NULL && args->la_base != NULL)
+ {
+ sel = LM_NONE;
+ base = args->la_base;
+ }
+
+ if (sel < LM_NONE)
+ {
+ sd = __session.ls_config->ldc_sds[sel];
+ next:
+ if (sd != NULL)
+ {
+ size_t len = strlen (sd->lsd_base);
+ if (sd->lsd_base[len - 1] == ',')
+ {
+ /* is relative */
+ snprintf (sdBase, sizeof (sdBase),
+ "%s%s", sd->lsd_base,
+ __session.ls_config->ldc_base);
+ base = sdBase;
+ }
+ else
+ {
+ base = sd->lsd_base;
+ }
+
+ if (sd->lsd_scope != -1)
+ {
+ scope = sd->lsd_scope;
+ }
+ }
+ attrs = __session.ls_config->ldc_attrtab[sel];
+ }
+
+ stat =
+ do_filter (args, filterprot, sd, filterBuf, sizeof (filterBuf),
+ &dynamicFilterBuf, &filter);
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ stat = do_with_reconnect (base, scope, filter,
+ (user_attrs != NULL) ? user_attrs : attrs,
+ sizelimit, res, (search_func_t) do_search_s);
+
+ if (dynamicFilterBuf != NULL)
+ {
+ free (dynamicFilterBuf);
+ dynamicFilterBuf = NULL;
+ }
+
+ /* If no entry was returned, try the next search descriptor. */
+ if (sd != NULL && sd->lsd_next != NULL)
+ {
+ if (stat == NSS_STATUS_NOTFOUND ||
+ (stat == NSS_STATUS_SUCCESS &&
+ ldap_first_entry (__session.ls_conn, *res) == NULL))
+ {
+ sd = sd->lsd_next;
+ goto next;
+ }
+ }
+
+ debug ("<== _nss_ldap_search_s");
+
+ return stat;
+}
+
+/*
+ * The generic lookup cover function (asynchronous).
+ * Assumes caller holds lock.
+ */
+static enum nss_status
+_nss_ldap_search (const struct ldap_args * args,
+ const char *filterprot, enum ldap_map_selector sel,
+ const char **user_attrs, int sizelimit, int *msgid,
+ struct ldap_service_search_descriptor ** csd)
+{
+ char sdBase[LDAP_FILT_MAXSIZ];
+ const char *base = NULL;
+ char filterBuf[LDAP_FILT_MAXSIZ], *dynamicFilterBuf = NULL;
+ const char **attrs, *filter;
+ int scope;
+ enum nss_status stat;
+ struct ldap_service_search_descriptor *sd = NULL;
+
+ debug ("==> _nss_ldap_search");
+
+ *msgid = -1;
+
+ stat = do_init ();
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== _nss_ldap_search");
+ return stat;
+ }
+
+ /* Set some reasonable defaults. */
+ base = __session.ls_config->ldc_base;
+ scope = __session.ls_config->ldc_scope;
+ attrs = NULL;
+
+ if (args != NULL && args->la_base != NULL)
+ {
+ sel = LM_NONE;
+ base = args->la_base;
+ }
+
+ if (sel < LM_NONE || *csd != NULL)
+ {
+ /*
+ * If we were chasing multiple descriptors and there are none left,
+ * just quit with NSS_STATUS_NOTFOUND.
+ */
+ if (*csd != NULL)
+ {
+ sd = (*csd)->lsd_next;
+ if (sd == NULL)
+ return NSS_STATUS_NOTFOUND;
+ }
+ else
+ {
+ sd = __session.ls_config->ldc_sds[sel];
+ }
+
+ *csd = sd;
+
+ if (sd != NULL)
+ {
+ size_t len = strlen (sd->lsd_base);
+ if (sd->lsd_base[len - 1] == ',')
+ {
+ /* is relative */
+ snprintf (sdBase, sizeof (sdBase), "%s%s", sd->lsd_base,
+ __session.ls_config->ldc_base);
+ base = sdBase;
+ }
+ else
+ {
+ base = sd->lsd_base;
+ }
+
+ if (sd->lsd_scope != -1)
+ {
+ scope = sd->lsd_scope;
+ }
+ }
+ attrs = __session.ls_config->ldc_attrtab[sel];
+ }
+
+ stat =
+ do_filter (args, filterprot, sd, filterBuf, sizeof (filterBuf),
+ &dynamicFilterBuf, &filter);
+ if (stat != NSS_STATUS_SUCCESS)
+ return stat;
+
+ stat = do_with_reconnect (base, scope, filter,
+ (user_attrs != NULL) ? user_attrs : attrs,
+ sizelimit, msgid, (search_func_t) do_search);
+
+ if (dynamicFilterBuf != NULL)
+ free (dynamicFilterBuf);
+
+ debug ("<== _nss_ldap_search");
+
+ return stat;
+}
+
+#ifdef HAVE_LDAP_SEARCH_EXT
+static enum nss_status
+do_next_page (const struct ldap_args * args,
+ const char *filterprot, enum ldap_map_selector sel, int
+ sizelimit, int *msgid, struct berval *pCookie)
+{
+ char sdBase[LDAP_FILT_MAXSIZ];
+ const char *base = NULL;
+ char filterBuf[LDAP_FILT_MAXSIZ], *dynamicFilterBuf = NULL;
+ const char **attrs, *filter;
+ int scope;
+ enum nss_status stat;
+ struct ldap_service_search_descriptor *sd = NULL;
+ LDAPControl *serverctrls[2] = {
+ NULL, NULL
+ };
+
+ /* Set some reasonable defaults. */
+ base = __session.ls_config->ldc_base;
+ scope = __session.ls_config->ldc_scope;
+ attrs = NULL;
+
+ if (args != NULL && args->la_base != NULL)
+ {
+ sel = LM_NONE;
+ base = args->la_base;
+ }
+
+ if (sel < LM_NONE)
+ {
+ sd = __session.ls_config->ldc_sds[sel];
+ if (sd != NULL)
+ {
+ size_t len = strlen (sd->lsd_base);
+ if (sd->lsd_base[len - 1] == ',')
+ {
+ snprintf (sdBase, sizeof (sdBase), "%s%s", sd->lsd_base,
+ __session.ls_config->ldc_base);
+ base = sdBase;
+ }
+ else
+ {
+ base = sd->lsd_base;
+ }
+
+ if (sd->lsd_scope != -1)
+ {
+ scope = sd->lsd_scope;
+ }
+ }
+ attrs = __session.ls_config->ldc_attrtab[sel];
+ }
+
+ stat =
+ do_filter (args, filterprot, sd, filterBuf, sizeof (filterBuf),
+ &dynamicFilterBuf, &filter);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ return stat;
+ }
+
+ stat =
+ ldap_create_page_control (__session.ls_conn,
+ __session.ls_config->ldc_pagesize,
+ pCookie, 0, &serverctrls[0]);
+ if (stat != LDAP_SUCCESS)
+ {
+ if (dynamicFilterBuf != NULL)
+ free (dynamicFilterBuf);
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ stat =
+ ldap_search_ext (__session.ls_conn, base,
+ __session.ls_config->ldc_scope,
+ filter,
+ (char **) attrs, 0, serverctrls, NULL, LDAP_NO_LIMIT,
+ sizelimit, msgid);
+
+ ldap_control_free (serverctrls[0]);
+ if (dynamicFilterBuf != NULL)
+ free (dynamicFilterBuf);
+
+ return (*msgid < 0) ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
+}
+#endif /* HAVE_LDAP_SEARCH_EXT */
+
+/*
+ * General entry point for enumeration routines.
+ * This should really use the asynchronous LDAP search API to avoid
+ * pulling down all the entries at once, particularly if the
+ * enumeration is not completed.
+ * Locks mutex.
+ */
+enum nss_status
+_nss_ldap_getent (struct ent_context ** ctx,
+ void *result, char *buffer, size_t buflen,
+ int *errnop, const char *filterprot,
+ enum ldap_map_selector sel, parser_t parser)
+{
+ enum nss_status status;
+
+ /*
+ * we need to lock here as the context may not be thread-specific
+ * data (under glibc, for example). Maybe we should make the lock part
+ * of the context.
+ */
+
+ _nss_ldap_enter ();
+ status = _nss_ldap_getent_ex (NULL, ctx, result,
+ buffer, buflen,
+ errnop, filterprot, sel, NULL, parser);
+ _nss_ldap_leave ();
+
+ return status;
+}
+
+/*
+ * Internal entry point for enumeration routines.
+ * Caller holds global mutex
+ */
+enum nss_status
+_nss_ldap_getent_ex (struct ldap_args * args,
+ struct ent_context ** ctx, void *result,
+ char *buffer, size_t buflen, int *errnop,
+ const char *filterprot,
+ enum ldap_map_selector sel,
+ const char **user_attrs, parser_t parser)
+{
+ enum nss_status stat = NSS_STATUS_SUCCESS;
+
+ debug ("==> _nss_ldap_getent_ex");
+
+ if (*ctx == NULL || (*ctx)->ec_msgid < 0)
+ {
+ /*
+ * implicitly call setent() if this is the first time
+ * or there is no active search
+ */
+ if (_nss_ldap_ent_context_init_locked (ctx) == NULL)
+ {
+ debug ("<== _nss_ldap_getent_ex");
+ return NSS_STATUS_UNAVAIL;
+ }
+ }
+
+next:
+ /*
+ * If ctx->ec_msgid < 0, then we haven't searched yet. Let's do it!
+ */
+ if ((*ctx)->ec_msgid < 0)
+ {
+ int msgid;
+
+ stat = _nss_ldap_search (args, filterprot, sel, user_attrs,
+ LDAP_NO_LIMIT, &msgid, &(*ctx)->ec_sd);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== _nss_ldap_getent_ex");
+ return stat;
+ }
+
+ (*ctx)->ec_msgid = msgid;
+ }
+
+ stat = do_parse (*ctx, result, buffer, buflen, errnop, parser);
+
+#ifdef HAVE_LDAP_SEARCH_EXT
+ if (stat == NSS_STATUS_NOTFOUND)
+ {
+ /* Is there another page of results? */
+ if ((*ctx)->ec_cookie != NULL && (*ctx)->ec_cookie->bv_len != 0)
+ {
+ int msgid;
+
+ stat =
+ do_next_page (NULL, filterprot, sel, LDAP_NO_LIMIT, &msgid,
+ (*ctx)->ec_cookie);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== _nss_ldap_getent_ex");
+ return stat;
+ }
+ (*ctx)->ec_msgid = msgid;
+ stat = do_parse (*ctx, result, buffer, buflen, errnop, parser);
+ }
+ }
+#endif /* HAVE_LDAP_SEARCH_EXT */
+
+ if (stat == NSS_STATUS_NOTFOUND && (*ctx)->ec_sd != NULL)
+ {
+ (*ctx)->ec_msgid = -1;
+ goto next;
+ }
+
+ debug ("<== _nss_ldap_getent_ex");
+
+ return stat;
+}
+
+/*
+ * General match function.
+ * Locks mutex.
+ */
+enum nss_status
+_nss_ldap_getbyname (struct ldap_args * args,
+ void *result, char *buffer, size_t buflen, int
+ *errnop, const char *filterprot,
+ enum ldap_map_selector sel, parser_t parser)
+{
+ enum nss_status stat = NSS_STATUS_NOTFOUND;
+ struct ent_context ctx;
+
+ _nss_ldap_enter ();
+
+ debug ("==> _nss_ldap_getbyname");
+
+ ctx.ec_msgid = -1;
+ ctx.ec_cookie = NULL;
+
+ stat = _nss_ldap_search_s (args, filterprot, sel, NULL, 1, &ctx.ec_res);
+ if (stat != NSS_STATUS_SUCCESS)
+ {
+ _nss_ldap_leave ();
+ debug ("<== _nss_ldap_getbyname");
+ return stat;
+ }
+
+ /*
+ * we pass this along for the benefit of the services parser,
+ * which uses it to figure out which protocol we really wanted.
+ * we only pass the second argument along, as that's what we need
+ * in services.
+ */
+ LS_INIT (ctx.ec_state);
+ ctx.ec_state.ls_type = LS_TYPE_KEY;
+ ctx.ec_state.ls_info.ls_key = args->la_arg2.la_string;
+
+ stat = do_parse_s (&ctx, result, buffer, buflen, errnop, parser);
+
+ _nss_ldap_ent_context_release (&ctx);
+
+ debug ("<== _nss_ldap_getbyname");
+
+ /* moved unlock here to avoid race condition bug #49 */
+ _nss_ldap_leave ();
+
+ return stat;
+}
+
+/*
+ * These functions are called from within the parser, where it is assumed
+ * to be safe to use the connection and the respective message.
+ */
+
+/*
+ * Assign all values, bar omitvalue (if not NULL), to *valptr.
+ */
+enum nss_status
+_nss_ldap_assign_attrvals (LDAPMessage * e,
+ const char *attr, const char *omitvalue,
+ char ***valptr, char **pbuffer, size_t *
+ pbuflen, size_t * pvalcount)
+{
+ char **vals;
+ char **valiter;
+ int valcount;
+ char **p = NULL;
+
+ register int buflen = *pbuflen;
+ register char *buffer = *pbuffer;
+
+ if (pvalcount != NULL)
+ {
+ *pvalcount = 0;
+ }
+
+ if (__session.ls_conn == NULL)
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ vals = ldap_get_values (__session.ls_conn, e, (char *) attr);
+
+ valcount = (vals == NULL) ? 0 : ldap_count_values (vals);
+ if (bytesleft (buffer, buflen, char *) < (valcount + 1) * sizeof (char *))
+ {
+ ldap_value_free (vals);
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ align (buffer, buflen, char *);
+ p = *valptr = (char **) buffer;
+
+ buffer += (valcount + 1) * sizeof (char *);
+ buflen -= (valcount + 1) * sizeof (char *);
+
+ if (valcount == 0)
+ {
+ *p = NULL;
+ *pbuffer = buffer;
+ *pbuflen = buflen;
+ return NSS_STATUS_SUCCESS;
+ }
+
+ valiter = vals;
+
+ while (*valiter != NULL)
+ {
+ int vallen;
+ char *elt = NULL;
+
+ if (omitvalue != NULL && strcmp (*valiter, omitvalue) == 0)
+ {
+ valcount--;
+ }
+ else
+ {
+ vallen = strlen (*valiter);
+ if (buflen < (size_t) (vallen + 1))
+ {
+ ldap_value_free (vals);
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ /* copy this value into the next block of buffer space */
+ elt = buffer;
+ buffer += vallen + 1;
+ buflen -= vallen + 1;
+
+ strncpy (elt, *valiter, vallen);
+ elt[vallen] = '\0';
+ *p = elt;
+ p++;
+ }
+ valiter++;
+ }
+
+ *p = NULL;
+ *pbuffer = buffer;
+ *pbuflen = buflen;
+
+ if (pvalcount != NULL)
+ {
+ *pvalcount = valcount;
+ }
+
+ ldap_value_free (vals);
+ return NSS_STATUS_SUCCESS;
+}
+
+/* Assign a single value to *valptr. */
+enum nss_status
+_nss_ldap_assign_attrval (LDAPMessage * e,
+ const char *attr, char **valptr, char **buffer,
+ size_t * buflen)
+{
+ char **vals;
+ int vallen;
+ const char *ovr, *def;
+
+ ovr = OV (attr);
+ if (ovr != NULL)
+ {
+ vallen = strlen (ovr);
+ if (*buflen < (size_t) (vallen + 1))
+ {
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ *valptr = *buffer;
+
+ strncpy (*valptr, ovr, vallen);
+ (*valptr)[vallen] = '\0';
+
+ *buffer += vallen + 1;
+ *buflen -= vallen + 1;
+
+ return NSS_STATUS_SUCCESS;
+ }
+
+ if (__session.ls_conn == NULL)
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ vals = ldap_get_values (__session.ls_conn, e, (char *) attr);
+ if (vals == NULL)
+ {
+ def = DF (attr);
+ if (def != NULL)
+ {
+ vallen = strlen (def);
+ if (*buflen < (size_t) (vallen + 1))
+ {
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ *valptr = *buffer;
+
+ strncpy (*valptr, def, vallen);
+ (*valptr)[vallen] = '\0';
+
+ *buffer += vallen + 1;
+ *buflen -= vallen + 1;
+
+ return NSS_STATUS_SUCCESS;
+ }
+ else
+ {
+ return NSS_STATUS_NOTFOUND;
+ }
+ }
+
+ vallen = strlen (*vals);
+ if (*buflen < (size_t) (vallen + 1))
+ {
+ ldap_value_free (vals);
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ *valptr = *buffer;
+
+ strncpy (*valptr, *vals, vallen);
+ (*valptr)[vallen] = '\0';
+
+ *buffer += vallen + 1;
+ *buflen -= vallen + 1;
+
+ ldap_value_free (vals);
+
+ return NSS_STATUS_SUCCESS;
+}
+
+const char *
+_nss_ldap_locate_userpassword (char **vals)
+{
+ const char *token = NULL;
+ size_t token_length = 0;
+ char **valiter;
+ const char *pwd = NULL;
+
+ if (__config != NULL)
+ {
+ switch (__config->ldc_password_type)
+ {
+ case LU_RFC2307_USERPASSWORD:
+ token = "{CRYPT}";
+ token_length = sizeof ("{CRYPT}") - 1;
+ break;
+ case LU_RFC3112_AUTHPASSWORD:
+ token = "CRYPT$";
+ token_length = sizeof ("CRYPT$") - 1;
+ break;
+ case LU_OTHER_PASSWORD:
+ break;
+ }
+ }
+
+ if (vals != NULL)
+ {
+ for (valiter = vals; *valiter != NULL; valiter++)
+ {
+ if (token_length == 0 ||
+ strncasecmp (*valiter, token, token_length) == 0)
+ {
+ pwd = *valiter;
+ break;
+ }
+ }
+ }
+
+ if (pwd == NULL)
+ pwd = "*";
+ else
+ pwd += token_length;
+
+ return pwd;
+}
+
+/*
+ * Assign a single value to *valptr, after examining userPassword for
+ * a syntactically suitable value.
+ */
+enum nss_status
+_nss_ldap_assign_userpassword (LDAPMessage * e,
+ const char *attr, char **valptr,
+ char **buffer, size_t * buflen)
+{
+ char **vals;
+ const char *pwd;
+ int vallen;
+
+ debug ("==> _nss_ldap_assign_userpassword");
+
+ if (__session.ls_conn == NULL)
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ vals = ldap_get_values (__session.ls_conn, e, (char *) attr);
+ pwd = _nss_ldap_locate_userpassword (vals);
+
+ vallen = strlen (pwd);
+
+ if (*buflen < (size_t) (vallen + 1))
+ {
+ if (vals != NULL)
+ {
+ ldap_value_free (vals);
+ }
+ debug ("<== _nss_ldap_assign_userpassword");
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ *valptr = *buffer;
+
+ strncpy (*valptr, pwd, vallen);
+ (*valptr)[vallen] = '\0';
+
+ *buffer += vallen + 1;
+ *buflen -= vallen + 1;
+
+ if (vals != NULL)
+ {
+ ldap_value_free (vals);
+ }
+
+ debug ("<== _nss_ldap_assign_userpassword");
+
+ return NSS_STATUS_SUCCESS;
+}
+
+enum nss_status
+_nss_ldap_oc_check (LDAPMessage * e, const char *oc)
+{
+ char **vals, **valiter;
+ enum nss_status ret = NSS_STATUS_NOTFOUND;
+
+ if (__session.ls_conn == NULL)
+ {
+ return NSS_STATUS_UNAVAIL;
+ }
+
+ vals = ldap_get_values (__session.ls_conn, e, AT (objectClass));
+ if (vals != NULL)
+ {
+ for (valiter = vals; *valiter != NULL; valiter++)
+ {
+ if (strcasecmp (*valiter, oc) == 0)
+ {
+ ret = NSS_STATUS_SUCCESS;
+ break;
+ }
+ }
+ }
+
+ if (vals != NULL)
+ {
+ ldap_value_free (vals);
+ }
+
+ return ret;
+}
+
+#ifdef HAVE_SHADOW_H
+int
+_nss_ldap_shadow_date (const char *val)
+{
+ int date;
+
+ if (__config->ldc_shadow_type == LS_AD_SHADOW)
+ {
+ date = atoll (val) / 864000000000LL - 134774LL;
+ date = (date > 99999) ? 99999 : date;
+ }
+ else
+ {
+ date = atol (val);
+ }
+
+ return date;
+}
+
+void
+_nss_ldap_shadow_handle_flag (struct spwd *sp)
+{
+ if (__config->ldc_shadow_type == LS_AD_SHADOW)
+ {
+ if (sp->sp_flag & UF_DONT_EXPIRE_PASSWD)
+ sp->sp_max = 99999;
+ sp->sp_flag = 0;
+ }
+}
+#endif /* HAVE_SHADOW_H */
+
+const char *
+_nss_ldap_map_at (enum ldap_map_selector sel, const char *attribute)
+{
+ const char *mapped = NULL;
+ enum nss_status stat;
+
+ stat = _nss_ldap_map_get (__config, sel, MAP_ATTRIBUTE, attribute, &mapped);
+
+ return (stat == NSS_STATUS_SUCCESS) ? mapped : attribute;
+}
+
+const char *
+_nss_ldap_unmap_at (enum ldap_map_selector sel, const char *attribute)
+{
+ const char *mapped = NULL;
+ enum nss_status stat;
+
+ stat = _nss_ldap_map_get (__config, sel, MAP_ATTRIBUTE_REVERSE, attribute, &mapped);
+
+ return (stat == NSS_STATUS_SUCCESS) ? mapped : attribute;
+}
+
+const char *
+_nss_ldap_map_oc (enum ldap_map_selector sel, const char *objectclass)
+{
+ const char *mapped = NULL;
+ enum nss_status stat;
+
+ stat = _nss_ldap_map_get (__config, sel, MAP_OBJECTCLASS, objectclass, &mapped);
+
+ return (stat == NSS_STATUS_SUCCESS) ? mapped : objectclass;
+}
+
+const char *
+_nss_ldap_unmap_oc (enum ldap_map_selector sel, const char *objectclass)
+{
+ const char *mapped = NULL;
+ enum nss_status stat;
+
+ stat = _nss_ldap_map_get (__config, sel, MAP_OBJECTCLASS_REVERSE, objectclass, &mapped);
+
+ return (stat == NSS_STATUS_SUCCESS) ? mapped : objectclass;
+}
+
+const char *
+_nss_ldap_map_ov (const char *attribute)
+{
+ const char *value = NULL;
+
+ _nss_ldap_map_get (__config, LM_NONE, MAP_OVERRIDE, attribute, &value);
+
+ return value;
+}
+
+const char *
+_nss_ldap_map_df (const char *attribute)
+{
+ const char *value = NULL;
+
+ _nss_ldap_map_get (__config, LM_NONE, MAP_DEFAULT, attribute, &value);
+
+ return value;
+}
+
+enum nss_status
+_nss_ldap_map_put (struct ldap_config * config,
+ enum ldap_map_selector sel,
+ enum ldap_map_type type,
+ const char *from,
+ const char *to)
+{
+ struct ldap_datum key, val;
+ void **map;
+ enum nss_status stat;
+
+ switch (type)
+ {
+ case MAP_ATTRIBUTE:
+ /* special handling for attribute mapping */ if (strcmp
+ (from,
+ "userPassword") == 0)
+ {
+ if (strcasecmp (to, "userPassword") == 0)
+ config->ldc_password_type = LU_RFC2307_USERPASSWORD;
+ else if (strcasecmp (to, "authPassword") == 0)
+ config->ldc_password_type = LU_RFC3112_AUTHPASSWORD;
+ else
+ config->ldc_password_type = LU_OTHER_PASSWORD;
+ }
+ else if (strcmp (from, "shadowLastChange") == 0)
+ {
+ if (strcasecmp (to, "shadowLastChange") == 0)
+ config->ldc_shadow_type = LS_RFC2307_SHADOW;
+ else if (strcasecmp (to, "pwdLastSet") == 0)
+ config->ldc_shadow_type = LS_AD_SHADOW;
+ else
+ config->ldc_shadow_type = LS_OTHER_SHADOW;
+ }
+ break;
+ case MAP_OBJECTCLASS:
+ case MAP_OVERRIDE:
+ case MAP_DEFAULT:
+ break;
+ default:
+ return NSS_STATUS_NOTFOUND;
+ break;
+ }
+
+ assert (sel <= LM_NONE);
+ map = &config->ldc_maps[sel][type];
+ assert (*map != NULL);
+
+ NSS_LDAP_DATUM_ZERO (&key);
+ key.data = (void *) from;
+ key.size = strlen (from) + 1;
+
+ NSS_LDAP_DATUM_ZERO (&val);
+ val.data = (void *) to;
+ val.size = strlen (to) + 1;
+
+ stat = _nss_ldap_db_put (*map, NSS_LDAP_DB_NORMALIZE_CASE, &key, &val);
+ if (stat == NSS_STATUS_SUCCESS &&
+ (type == MAP_ATTRIBUTE || type == MAP_OBJECTCLASS))
+ {
+ type = (type == MAP_ATTRIBUTE) ? MAP_ATTRIBUTE_REVERSE : MAP_OBJECTCLASS_REVERSE;
+ map = &config->ldc_maps[sel][type];
+
+ stat = _nss_ldap_db_put (*map, NSS_LDAP_DB_NORMALIZE_CASE, &val, &key);
+ }
+
+ return stat;
+}
+
+enum nss_status
+_nss_ldap_map_get (struct ldap_config * config,
+ enum ldap_map_selector sel,
+ enum ldap_map_type type,
+ const char *from, const char **to)
+{
+ struct ldap_datum key, val;
+ void *map;
+ enum nss_status stat;
+
+ if (config == NULL || sel > LM_NONE || type > MAP_MAX)
+ {
+ return NSS_STATUS_NOTFOUND;
+ }
+
+ map = config->ldc_maps[sel][type];
+ assert (map != NULL);
+
+ NSS_LDAP_DATUM_ZERO (&key);
+ key.data = from;
+ key.size = strlen (from) + 1;
+
+ NSS_LDAP_DATUM_ZERO (&val);
+
+ stat = _nss_ldap_db_get (map, NSS_LDAP_DB_NORMALIZE_CASE, &key, &val);
+ if (stat == NSS_STATUS_NOTFOUND && sel != LM_NONE)
+ {
+ map = config->ldc_maps[LM_NONE][type];
+ assert (map != NULL);
+ stat = _nss_ldap_db_get (map, NSS_LDAP_DB_NORMALIZE_CASE, &key, &val);
+ }
+
+ if (stat == NSS_STATUS_SUCCESS)
+ *to = (char *) val.data;
+ else
+ *to = NULL;
+
+ return stat;
+}
+
+/*
+ * Proxy bind support for AIX. Very simple, but should do
+ * the job.
+ */
+
+struct ldap_proxy_bind_args
+{
+ char *binddn;
+ const char *bindpw;
+};
+
+
+#if LDAP_SET_REBIND_PROC_ARGS < 3
+static struct ldap_proxy_bind_args __proxy_args = { NULL, NULL };
+#endif
+
+#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+static int
+do_proxy_rebind (LDAP * ld, LDAP_CONST char *url, ber_tag_t request,
+ ber_int_t msgid, void *arg)
+#else
+static int
+do_proxy_rebind (LDAP * ld, LDAP_CONST char *url, int request,
+ ber_int_t msgid)
+#endif
+{
+ int timelimit;
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+ struct ldap_proxy_bind_args *who = (struct ldap_proxy_bind_args *) arg;
+#else
+ struct ldap_proxy_bind_args *who = &__proxy_args;
+#endif
+
+ timelimit = __session.ls_config->ldc_bind_timelimit;
+
+ return do_bind (ld, timelimit, who->binddn, who->bindpw, 0);
+}
+#else
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+static int
+do_proxy_rebind (LDAP * ld, char **whop, char **credp, int *methodp,
+ int freeit, void *arg)
+#elif LDAP_SET_REBIND_PROC_ARGS == 2
+static int
+do_proxy_rebind (LDAP * ld, char **whop, char **credp, int *methodp,
+ int freeit)
+#endif
+{
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+ struct ldap_proxy_bind_args *who = (struct ldap_proxy_bind_args *) arg;
+#else
+ struct ldap_proxy_bind_args *who = &__proxy_args;
+#endif
+ if (freeit)
+ {
+ if (*whop != NULL)
+ free (*whop);
+ if (*credp != NULL)
+ free (*credp);
+ }
+
+ *whop = who->binddn ? strdup (who->binddn) : NULL;
+ *credp = who->bindpw ? strdup (who->bindpw) : NULL;
+
+ *methodp = LDAP_AUTH_SIMPLE;
+
+ return LDAP_SUCCESS;
+}
+#endif
+
+enum nss_status
+_nss_ldap_proxy_bind (const char *user, const char *password)
+{
+ struct ldap_args args;
+ LDAPMessage *res, *e;
+ enum nss_status stat;
+ int rc;
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+ struct ldap_proxy_bind_args proxy_args_buf;
+ struct ldap_proxy_bind_args *proxy_args = &proxy_args_buf;
+#else
+ struct ldap_proxy_bind_args *proxy_args = &__proxy_args;
+#endif
+
+ debug ("==> _nss_ldap_proxy_bind");
+
+ LA_INIT (args);
+ LA_TYPE (args) = LA_TYPE_STRING;
+ LA_STRING (args) = user;
+
+ /*
+ * Binding with an empty password will always work, so don't let
+ * the user in if they try that.
+ */
+ if (password == NULL || password[0] == '\0')
+ {
+ debug ("<== _nss_ldap_proxy_bind (empty password not permitted)");
+ /* XXX overload */
+ return NSS_STATUS_TRYAGAIN;
+ }
+
+ _nss_ldap_enter ();
+
+ stat = _nss_ldap_search_s (&args, _nss_ldap_filt_getpwnam,
+ LM_PASSWD, NULL, 1, &res);
+ if (stat == NSS_STATUS_SUCCESS)
+ {
+ e = _nss_ldap_first_entry (res);
+ if (e != NULL)
+ {
+ proxy_args->binddn = _nss_ldap_get_dn (e);
+ proxy_args->bindpw = password;
+
+ if (proxy_args->binddn != NULL)
+ {
+ /* Use our special rebind procedure. */
+#if LDAP_SET_REBIND_PROC_ARGS == 3
+ ldap_set_rebind_proc (__session.ls_conn, do_proxy_rebind, NULL);
+#elif LDAP_SET_REBIND_PROC_ARGS == 2
+ ldap_set_rebind_proc (__session.ls_conn, do_proxy_rebind);
+#endif
+
+ debug (":== _nss_ldap_proxy_bind: %s", proxy_args->binddn);
+
+ rc = do_bind (__session.ls_conn,
+ __session.ls_config->ldc_bind_timelimit,
+ proxy_args->binddn, proxy_args->bindpw, 0);
+ switch (rc)
+ {
+ case LDAP_INVALID_CREDENTIALS:
+ /* XXX overload */
+ stat = NSS_STATUS_TRYAGAIN;
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ stat = NSS_STATUS_NOTFOUND;
+ break;
+ case LDAP_SUCCESS:
+ stat = NSS_STATUS_SUCCESS;
+ break;
+ default:
+ stat = NSS_STATUS_UNAVAIL;
+ break;
+ }
+ /*
+ * Close the connection, don't want to continue
+ * being bound as this user or using this rebind proc.
+ */
+ do_close ();
+ ldap_memfree (proxy_args->binddn);
+ }
+ else
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ }
+ proxy_args->binddn = NULL;
+ proxy_args->bindpw = NULL;
+ }
+ else
+ {
+ stat = NSS_STATUS_NOTFOUND;
+ }
+ ldap_msgfree (res);
+ }
+
+ _nss_ldap_leave ();
+
+ debug ("<== _nss_ldap_proxy_bind");
+
+ return stat;
+}
+
+const char **
+_nss_ldap_get_attributes (enum ldap_map_selector sel)
+{
+ const char **attrs = NULL;
+
+ debug ("==> _nss_ldap_get_attributes");
+
+ if (sel < LM_NONE)
+ {
+ if (do_init () != NSS_STATUS_SUCCESS)
+ {
+ debug ("<== _nss_ldap_get_attributes (init failed)");
+ return NULL;
+ }
+
+ attrs = __session.ls_config->ldc_attrtab[sel];
+ }
+
+ debug ("<== _nss_ldap_get_attributes");
+
+ return attrs;
+}
+
+int
+_nss_ldap_test_config_flag (unsigned int flag)
+{
+ if (__config != NULL && (__config->ldc_flags & flag) != 0)
+ return 1;
+
+ return 0;
+}
+
+int
+_nss_ldap_test_initgroups_ignoreuser (const char *user)
+{
+ char **p;
+
+ if (__config == NULL)
+ return 0;
+
+ if (__config->ldc_initgroups_ignoreusers == NULL)
+ return 0;
+
+ for (p = __config->ldc_initgroups_ignoreusers; *p != NULL; p++)
+ {
+ if (strcmp (*p, user) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+_nss_ldap_get_ld_errno (char **m, char **s)
+{
+#ifdef HAVE_LDAP_GET_OPTION
+ int rc;
+#endif
+ int lderrno;
+
+ if (__session.ls_conn == NULL)
+ {
+ return LDAP_UNAVAILABLE;
+ }
+
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER)
+ /* is this needed? */
+ rc = ldap_get_option (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &lderrno);
+ if (rc != LDAP_SUCCESS)
+ return rc;
+#else
+ lderrno = ld->ld_errno;
+#endif
+
+ if (s != NULL)
+ {
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_STRING)
+ rc = ldap_get_option (__session.ls_conn, LDAP_OPT_ERROR_STRING, s);
+ if (rc != LDAP_SUCCESS)
+ return rc;
+#else
+ *s = ld->ld_error;
+#endif
+ }
+
+ if (m != NULL)
+ {
+#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_MATCHED_DN)
+ rc = ldap_get_option (__session.ls_conn, LDAP_OPT_MATCHED_DN, m);
+ if (rc != LDAP_SUCCESS)
+ return rc;
+#else
+ *m = ld->ld_matched;
+#endif
+ }
+
+ return lderrno;
+}