You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by bn...@apache.org on 2004/11/10 17:35:24 UTC

cvs commit: httpd-2.0/modules/experimental NWGNUutilldap util_ldap.c util_ldap.def

bnicholes    2004/11/10 08:35:24

  Modified:    .        Tag: APACHE_2_0_BRANCH CHANGES STATUS
               include  Tag: APACHE_2_0_BRANCH util_ldap.h
               modules/experimental Tag: APACHE_2_0_BRANCH NWGNUutilldap
                        util_ldap.c util_ldap.def
  Log:
  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
  
  Revision  Changes    Path
  No                   revision
  No                   revision
  1.988.2.379 +5 -0      httpd-2.0/CHANGES
  
  Index: CHANGES
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/CHANGES,v
  retrieving revision 1.988.2.378
  retrieving revision 1.988.2.379
  diff -u -r1.988.2.378 -r1.988.2.379
  --- CHANGES	10 Nov 2004 12:18:50 -0000	1.988.2.378
  +++ CHANGES	10 Nov 2004 16:35:20 -0000	1.988.2.379
  @@ -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]
  
  
  
  1.751.2.1146 +1 -8      httpd-2.0/STATUS
  
  Index: STATUS
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/STATUS,v
  retrieving revision 1.751.2.1145
  retrieving revision 1.751.2.1146
  diff -u -r1.751.2.1145 -r1.751.2.1146
  --- STATUS	10 Nov 2004 13:11:39 -0000	1.751.2.1145
  +++ STATUS	10 Nov 2004 16:35:21 -0000	1.751.2.1146
  @@ -75,13 +75,6 @@
     [ 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
  
  
  
  No                   revision
  No                   revision
  1.9.2.10  +21 -0     httpd-2.0/include/util_ldap.h
  
  Index: util_ldap.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/include/util_ldap.h,v
  retrieving revision 1.9.2.9
  retrieving revision 1.9.2.10
  diff -u -r1.9.2.9 -r1.9.2.10
  --- util_ldap.h	4 Aug 2004 21:04:16 -0000	1.9.2.9
  +++ util_ldap.h	10 Nov 2004 16:35:23 -0000	1.9.2.10
  @@ -260,6 +260,27 @@
                                 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)
    */
  
  
  
  No                   revision
  No                   revision
  1.6.2.4   +1 -0      httpd-2.0/modules/experimental/Attic/NWGNUutilldap
  
  Index: NWGNUutilldap
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/Attic/NWGNUutilldap,v
  retrieving revision 1.6.2.3
  retrieving revision 1.6.2.4
  diff -u -r1.6.2.3 -r1.6.2.4
  --- NWGNUutilldap	21 May 2004 22:42:56 -0000	1.6.2.3
  +++ NWGNUutilldap	10 Nov 2004 16:35:23 -0000	1.6.2.4
  @@ -226,6 +226,7 @@
   	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 \
  
  
  
  1.6.2.26  +198 -10   httpd-2.0/modules/experimental/Attic/util_ldap.c
  
  Index: util_ldap.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/Attic/util_ldap.c,v
  retrieving revision 1.6.2.25
  retrieving revision 1.6.2.26
  diff -u -r1.6.2.25 -r1.6.2.26
  --- util_ldap.c	23 Oct 2004 13:47:05 -0000	1.6.2.25
  +++ util_ldap.c	10 Nov 2004 16:35:23 -0000	1.6.2.26
  @@ -776,24 +776,24 @@
           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 @@
        * 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 @@
              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 @@
       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 
  
  
  
  1.1.2.1   +1 -0      httpd-2.0/modules/experimental/Attic/util_ldap.def
  
  Index: util_ldap.def
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/experimental/Attic/util_ldap.def,v
  retrieving revision 1.1
  retrieving revision 1.1.2.1
  diff -u -r1.1 -r1.1.2.1
  --- util_ldap.def	8 Feb 2002 18:31:32 -0000	1.1
  +++ util_ldap.def	10 Nov 2004 16:35:23 -0000	1.1.2.1
  @@ -2,5 +2,6 @@
   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