/* ldap-nss.c - main file for NSS interface This file was part of the nss_ldap library which has been forked into the nss-ldapd library. Copyright (C) 1997-2006 Luke Howard Copyright (C) 2006 West Consulting Copyright (C) 2006 Arthur de Jong This 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. This 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 this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_UN_H #include #endif #include #ifdef HAVE_LBER_H #include #endif #ifdef HAVE_LDAP_H #include #endif #ifdef HAVE_LDAP_SSL_H #include #endif #ifdef HAVE_GSSLDAP_H #include #endif #ifdef HAVE_GSSSASL_H #include #endif #ifdef HAVE_MALLOC_H #include #endif #if defined(HAVE_THREAD_H) #include #elif defined(HAVE_PTHREAD_H) #include #endif /* Try to handle systems with both SASL libraries installed */ #if defined(HAVE_SASL_SASL_H) && defined(HAVE_SASL_AUXPROP_REQUEST) #include #elif defined(HAVE_SASL_H) #include #endif #ifdef HAVE_GSSAPI_H #include #elif defined(HAVE_GSSAPI_GSSAPI_KRB5_H) #include #include #endif #include "ldap-nss.h" #include "util.h" #include "dnsconfig.h" #include "pagectrl.h" #include "common.h" #include "log.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); /* * LS_INIT only used for enumeration contexts */ #define LS_INIT(state) do { state.ls_type = LS_TYPE_INDEX; state.ls_retry = 0; state.ls_info.ls_index = -1; } while (0) /* * 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 (ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) { rc = LDAP_UNAVAILABLE; } /* 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 (ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, &sd) == 0) { int off = 0; socklen_t socknamelen = sizeof (struct sockaddr_storage); 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, socklen_t _slen1, struct sockaddr_storage *_s2, 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 (ldap_get_option (__session.ls_conn, LDAP_OPT_DESC, sd) == 0) { struct sockaddr_storage sockname; struct sockaddr_storage peername; socklen_t socknamelen = sizeof (sockname); 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 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 (¤t_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 ldap_set_option (__session.ls_conn, LDAP_OPT_PROTOCOL_VERSION, &cfg->ldc_version); ldap_set_option (__session.ls_conn, LDAP_OPT_DEREF, &cfg->ldc_deref); ldap_set_option (__session.ls_conn, LDAP_OPT_TIMELIMIT, &cfg->ldc_timelimit); #ifdef 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 */ #ifdef 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 */ #ifdef LDAP_OPT_REFERRALS ldap_set_option (__session.ls_conn, LDAP_OPT_REFERRALS, cfg->ldc_referrals ? LDAP_OPT_ON : LDAP_OPT_OFF); #endif /* LDAP_OPT_REFERRALS */ #ifdef LDAP_OPT_RESTART ldap_set_option (__session.ls_conn, LDAP_OPT_RESTART, cfg->ldc_restart ? LDAP_OPT_ON : LDAP_OPT_OFF); #endif /* LDAP_OPT_RESTART */ #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(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 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 (ldap_get_option (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) { rc = LDAP_UNAVAILABLE; } 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"); ldap_set_option (__session.ls_conn, LDAP_OPT_SIZELIMIT, (void *) &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 ldap_set_option (__session.ls_conn, LDAP_OPT_SIZELIMIT, (void *) &sizelimit); *msgid = ldap_search (__session.ls_conn, base, scope, filter, (char **) attrs, 0); if (*msgid < 0) { if (ldap_get_option (__session.ls_conn, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS) { rc = LDAP_UNAVAILABLE; } } 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; } static int NEW_do_parse_s(struct ent_context *ctx,FILE *fp,NEWparser_t parser) { int parseStat=NSLCD_RESULT_NOTFOUND; LDAPMessage *e=NULL; /* * 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=NSLCD_RESULT_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,fp); /* hold onto the state if we're out of memory XXX */ ctx->ec_state.ls_retry=0; } while (parseStat==NSLCD_RESULT_NOTFOUND); return parseStat; } int _nss_ldap_searchbyname( struct ldap_args *args,const char *filterprot, enum ldap_map_selector sel,FILE *fp,NEWparser_t parser) { int stat; struct ent_context ctx; int32_t tmpint32; _nss_ldap_enter(); ctx.ec_msgid=-1; ctx.ec_cookie=NULL; stat=nss2nslcd(_nss_ldap_search_s(args,filterprot,sel,NULL,1,&ctx.ec_res)); /* write the result code */ WRITE_INT32(fp,stat); /* bail on nothing found */ if (stat!=NSLCD_RESULT_SUCCESS) { _nss_ldap_leave(); return 1; } /* * 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; /* call the parser for the result */ stat=NEW_do_parse_s(&ctx,fp,parser); _nss_ldap_ent_context_release(&ctx); /* 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; } int _nss_ldap_write_attrvals(FILE *fp,LDAPMessage *e,const char *attr) { char **vals; int valcount; int i; int32_t tmpint32; /* log */ log_log(LOG_DEBUG,"_nss_ldap_write_attrvals(%s)",attr); /* check if we have a connection */ if (__session.ls_conn==NULL) return NSLCD_RESULT_UNAVAIL; /* get the values and the number of values */ vals=ldap_get_values(__session.ls_conn,e,(char *)attr); valcount=(vals==NULL)?0:ldap_count_values(vals); /* write number of entries */ WRITE_INT32(fp,valcount); /* write the entries themselves */ for (i=0;ildc_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; }