Commit f467430a authored by Bradley Nicholes's avatar Bradley Nicholes
Browse files

Implement the util_ldap_cache_getuserdn() API so that the ldap authorization...

Implement the util_ldap_cache_getuserdn() API so that the ldap authorization only modules have access to the util_ldap user cache without having to require ldap authentication as well.  [PR 31898]

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
parent 9e62880c
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
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]
+1 −8
Original line number Diff line number Diff line
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
+21 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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 \
+198 −10
Original line number Diff line number Diff line
@@ -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 
Loading