]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Implement the util_ldap_cache_getuserdn() API so that the ldap authorization only...
authorBradley Nicholes <bnicholes@apache.org>
Wed, 10 Nov 2004 16:35:23 +0000 (16:35 +0000)
committerBradley Nicholes <bnicholes@apache.org>
Wed, 10 Nov 2004 16:35:23 +0000 (16:35 +0000)
Submitted by: Jari Ahonen [jah progress.com]
Reviewed by: bnicholes, wrowe, jim

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/APACHE_2_0_BRANCH@105744 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
STATUS
include/util_ldap.h
modules/experimental/NWGNUutilldap
modules/experimental/util_ldap.c
modules/experimental/util_ldap.def

diff --git a/CHANGES b/CHANGES
index 79a9516e9734108538fb62a60300d75954c988d2..08c83fd313215658cd4109d08c333a3708485bc2 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,10 @@
 Changes with Apache 2.0.53
 
+  *) Util_ldap: Implemented the util_ldap_cache_getuserdn() API so that 
+     ldap authorization only modules have access to the util_ldap 
+     user cache without having to require ldap authentication as well.  
+     [PR 31898] [Jari Ahonen jah progress.com, Brad Nicholes]
+
   *) SECURITY: CAN-2004-0942 (cve.mitre.org)
      Fix for memory consumption DoS in handling of MIME folded request
      headers.  [Joe Orton]
diff --git a/STATUS b/STATUS
index 603eda4e1aea15a4f03029d16ba21d3e5d580381..c48712573c39c4cdae2a7a97ccd61846af11dc26 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -1,5 +1,5 @@
 APACHE 2.0 STATUS:                                              -*-text-*-
-Last modified at [$Date: 2004/11/10 13:11:39 $]
+Last modified at [$Date: 2004/11/10 16:35:21 $]
 
 Release:
 
@@ -75,13 +75,6 @@ PATCHES TO BACKPORT FROM 2.1
   [ please place file names and revisions from HEAD here, so it is easy to
     identify exactly what the proposed changes are! ]
 
-    *) util_ldap: Add the util_ldap_cache_getuserdn() API to allow 
-       non-LDAP authentication modules the ability to use the util_ldap 
-       cache for authorization purposes only rather than authentication.  
-       PR #31898
-        http://www.apache.org/~bnicholes/apache_2.0_getuserdn.patch
-       +1:bnicholes, wrowe, jim
-       
     *) mod_authnz_ldap: Added the directive "Requires ldap-attribute" that
        allows the module to only authorize a user if the attribute value
        specified matches the value of the user object. PR 31913
index 52363efbb3bb35d9cc9c47b3586f15c98aaf7441..b44ab03377e608a7b14956725bafb70e0b9ac23c 100644 (file)
@@ -259,6 +259,27 @@ LDAP_DECLARE(int) util_ldap_cache_checkuserid(request_rec *r, util_ldap_connecti
                               const char *url, const char *basedn, int scope, char **attrs,
                               const char *filter, const char *bindpw, const char **binddn, const char ***retvals);
 
+/**
+ * Retrieves the LDAP DN of the user without the need to know user password
+ * @param r The request record
+ * @param ldc The LDAP connection being used.
+ * @param url The URL of the LDAP connection - used for deciding which cache to use.
+ * @param basedn The Base DN to search for the user in.
+ * @param scope LDAP scope of the search.
+ * @param attrs LDAP attributes to return in search.
+ * @param filter The user to search for in the form of an LDAP filter. This filter must return
+ *               exactly one user for the check to be successful.
+ * @param binddn The DN of the user will be returned in this variable.
+ * @param retvals The values corresponding to the attributes requested in the attrs array.
+ * @tip The filter supplied will be searched for. A single entry matching the search is returned.
+ * @deffunc int util_ldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
+ *                                          char *url, const char *basedn, int scope, char **attrs,
+ *                                          char *filter, char **binddn, char ***retvals)
+ */
+LDAP_DECLARE(int) util_ldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
+                              const char *url, const char *basedn, int scope, char **attrs,
+                              const char *filter, const char **binddn, const char ***retvals);
+
 /**
  * Checks if SSL support is available in mod_ldap
  * @deffunc int util_ldap_ssl_supported(request_rec *r)
index aa15476e4ec79bc2fa0fbc3e03031f9c72c5744f..98be959d06589c5ff1da255c810ea1c4ebbad994 100644 (file)
@@ -226,6 +226,7 @@ FILES_nlm_exports = \
        util_ldap_connection_unbind \
        util_ldap_connection_cleanup \
        util_ldap_cache_checkuserid \
+       util_ldap_cache_getuserdn \
        util_ldap_cache_compare \
        util_ldap_cache_comparedn \
        util_ldap_ssl_supported \
index f942d147ca430108783e2f35c1db920b3c60a259..6b077e25e41fe377fe396417d57b26418431cfa9 100644 (file)
@@ -776,24 +776,24 @@ LDAP_DECLARE(int) util_ldap_cache_checkuserid(request_rec *r, util_ldap_connecti
         LDAP_CACHE_LOCK();
         the_search_node.username = filter;
         search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
-        if (search_nodep != NULL && search_nodep->bindpw) {
+        if (search_nodep != NULL) {
     
             /* found entry in search cache... */
             curtime = apr_time_now();
     
             /*
-             * Remove this item from the cache if its expired, or if the 
-             * sent password doesn't match the storepassword.
+             * Remove this item from the cache if its expired.
+             * If the sent password doesn't match the stored password,
+             * the entry will be removed and readded later if the
+             * credentials pass authentication.
              */
             if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
                 /* ...but entry is too old */
                 util_ald_cache_remove(curl->search_cache, search_nodep);
             }
-            else if (strcmp(search_nodep->bindpw, bindpw) != 0) {
-           /* ...but cached password doesn't match sent password */
-                util_ald_cache_remove(curl->search_cache, search_nodep);
-            }
-            else {
+            else if ((search_nodep->bindpw) &&
+                (search_nodep->bindpw[0] != '\0') &&
+                (strcmp(search_nodep->bindpw, bindpw) == 0)) {
                 /* ...and entry is valid */
                 *binddn = search_nodep->dn;
                 *retvals = search_nodep->vals;
@@ -866,7 +866,7 @@ start_over:
      * able to authenticate with this module. I don't see this as a big
      * problem.
      */
-    if (strlen(bindpw) <= 0) {
+    if (!bindpw || strlen(bindpw) <= 0) {
         ldap_msgfree(res);
         ldc->reason = "Empty password not allowed";
         return LDAP_INVALID_CREDENTIALS;
@@ -943,11 +943,20 @@ start_over:
            into the cache before we got here. If it does exist then update the lastbind */
         search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
         if ((search_nodep == NULL) || 
-            (strcmp(*binddn, search_nodep->dn) != 0) || (strcmp(bindpw, search_nodep->bindpw) != 0)) {
+            (strcmp(*binddn, search_nodep->dn) != 0)) {
+
+            /* Nothing in cache, insert new entry */
+            util_ald_cache_insert(curl->search_cache, &the_search_node);
+        }
+        else if ((!search_nodep->bindpw) ||
+            (strcmp(bindpw, search_nodep->bindpw) != 0)) {
 
+            /* Entry in cache is invalid, remove it and insert new one */
+            util_ald_cache_remove(curl->search_cache, search_nodep);
             util_ald_cache_insert(curl->search_cache, &the_search_node);
         }
         else {
+            /* Cache entry is valid, update lastbind */
             search_nodep->lastbind = the_search_node.lastbind;
         }
         LDAP_CACHE_UNLOCK();
@@ -958,6 +967,185 @@ start_over:
     return LDAP_SUCCESS;
 }
 
+/*
+ * This function will return the DN of the entry matching userid.
+ * It is used to get the DN in case some other module than mod_auth_ldap
+ * has authenticated the user.
+ * The function is basically a copy of util_ldap_cache_checkuserid
+ * with password checking removed.
+ */
+LDAP_DECLARE(int) util_ldap_cache_getuserdn(request_rec *r, util_ldap_connection_t *ldc,
+                              const char *url, const char *basedn, int scope, char **attrs,
+                              const char *filter, const char **binddn,
+                              const char ***retvals)
+{
+    const char **vals = NULL;
+    int result = 0;
+    LDAPMessage *res, *entry;
+    char *dn;
+    int count;
+    int failures = 0;
+    util_url_node_t *curl;             /* Cached URL node */
+    util_url_node_t curnode;
+    util_search_node_t *search_nodep;  /* Cached search node */
+    util_search_node_t the_search_node;
+    apr_time_t curtime;
+
+    util_ldap_state_t *st = 
+        (util_ldap_state_t *)ap_get_module_config(r->server->module_config,
+        &ldap_module);
+
+    /* Get the cache node for this url */
+    LDAP_CACHE_LOCK();
+    curnode.url = url;
+    curl = (util_url_node_t *)util_ald_cache_fetch(st->util_ldap_cache, &curnode);
+    if (curl == NULL) {
+        curl = util_ald_create_caches(st, url);
+    }
+    LDAP_CACHE_UNLOCK();
+
+    if (curl) {
+        LDAP_CACHE_LOCK();
+        the_search_node.username = filter;
+        search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
+        if (search_nodep != NULL) {
+    
+            /* found entry in search cache... */
+            curtime = apr_time_now();
+    
+            /*
+             * Remove this item from the cache if its expired.
+             */
+            if ((curtime - search_nodep->lastbind) > st->search_cache_ttl) {
+                /* ...but entry is too old */
+                util_ald_cache_remove(curl->search_cache, search_nodep);
+            }
+            else {
+                /* ...and entry is valid */
+                *binddn = search_nodep->dn;
+                *retvals = search_nodep->vals;
+                LDAP_CACHE_UNLOCK();
+                ldc->reason = "Search successful (cached)";
+                return LDAP_SUCCESS;
+            }
+        }
+        /* unlock this read lock */
+        LDAP_CACHE_UNLOCK();
+    }
+
+    /* 
+     * At this point, there is no valid cached search, so lets do the search.
+     */
+
+    /*
+     * If any LDAP operation fails due to LDAP_SERVER_DOWN, control returns here.
+     */
+start_over:
+    if (failures++ > 10) {
+        return result;
+    }
+    if (LDAP_SUCCESS != (result = util_ldap_connection_open(r, ldc))) {
+        return result;
+    }
+
+    /* try do the search */
+    if ((result = ldap_search_ext_s(ldc->ldap,
+                                   const_cast(basedn), scope, 
+                                   const_cast(filter), attrs, 0, 
+                                   NULL, NULL, NULL, -1, &res)) == LDAP_SERVER_DOWN) {
+        ldc->reason = "ldap_search_ext_s() for user failed with server down";
+        goto start_over;
+    }
+
+    /* if there is an error (including LDAP_NO_SUCH_OBJECT) return now */
+    if (result != LDAP_SUCCESS) {
+        ldc->reason = "ldap_search_ext_s() for user failed";
+        return result;
+    }
+
+    /* 
+     * We should have found exactly one entry; to find a different
+     * number is an error.
+     */
+    count = ldap_count_entries(ldc->ldap, res);
+    if (count != 1) 
+    {
+        if (count == 0 )
+            ldc->reason = "User not found";
+        else
+            ldc->reason = "User is not unique (search found two or more matches)";
+        ldap_msgfree(res);
+        return LDAP_NO_SUCH_OBJECT;
+    }
+
+    entry = ldap_first_entry(ldc->ldap, res);
+
+    /* Grab the dn, copy it into the pool, and free it again */
+    dn = ldap_get_dn(ldc->ldap, entry);
+    *binddn = apr_pstrdup(r->pool, dn);
+    ldap_memfree(dn);
+
+    /*
+     * Get values for the provided attributes.
+     */
+    if (attrs) {
+        int k = 0;
+        int i = 0;
+        while (attrs[k++]);
+        vals = apr_pcalloc(r->pool, sizeof(char *) * (k+1));
+        while (attrs[i]) {
+            char **values;
+            int j = 0;
+            char *str = NULL;
+            /* get values */
+            values = ldap_get_values(ldc->ldap, entry, attrs[i]);
+            while (values && values[j]) {
+                str = str ? apr_pstrcat(r->pool, str, "; ", values[j], NULL) : apr_pstrdup(r->pool, values[j]);
+                j++;
+            }
+            ldap_value_free(values);
+            vals[i] = str;
+            i++;
+        }
+        *retvals = vals;
+    }
+
+    /*                 
+     * Add the new username to the search cache.
+     */
+    if (curl) {
+        LDAP_CACHE_LOCK();
+        the_search_node.username = filter;
+        the_search_node.dn = *binddn;
+        the_search_node.bindpw = NULL;
+        the_search_node.lastbind = apr_time_now();
+        the_search_node.vals = vals;
+
+        /* Search again to make sure that another thread didn't ready insert this node
+           into the cache before we got here. If it does exist then update the lastbind */
+        search_nodep = util_ald_cache_fetch(curl->search_cache, &the_search_node);
+        if ((search_nodep == NULL) || 
+            (strcmp(*binddn, search_nodep->dn) != 0)) {
+
+            /* Nothing in cache, insert new entry */
+            util_ald_cache_insert(curl->search_cache, &the_search_node);
+        }
+        /*
+         * Don't update lastbind on entries with bindpw because
+         * we haven't verified that password. It's OK to update
+         * the entry if there is no password in it.
+         */
+        else if (!search_nodep->bindpw) {
+            /* Cache entry is valid, update lastbind */
+            search_nodep->lastbind = the_search_node.lastbind;
+        }
+        LDAP_CACHE_UNLOCK();
+    }
+    ldap_msgfree(res);
+
+    ldc->reason = "Search successful";
+    return LDAP_SUCCESS;
+}
 
 /*
  * Reports if ssl support is enabled 
index d1fac89b354f38adb32e2e6ec90534b504857df0..f3ca326466a841e23b7b831b7bb276293c703268 100644 (file)
@@ -2,5 +2,6 @@ EXPORT  ldap_module
 EXPORT  util_ldap_connection_find
 EXPORT  util_ldap_connection_close
 EXPORT  util_ldap_cache_checkuserid
+EXPORT  util_ldap_cache_getuserdn
 EXPORT  util_ldap_cache_compare
 EXPORT  util_ldap_cache_comparedn