From: Bradley Nicholes Date: Mon, 1 Nov 2004 23:43:24 +0000 (+0000) Subject: Implement the util_ldap_cache_getuserdn() API so that the ldap authorization only... X-Git-Tag: 2.1.1~76 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=05556534cd5107b74fee7f1a1cbece9c516eac5d;p=thirdparty%2Fapache%2Fhttpd.git Implement the util_ldap_cache_getuserdn() API so that the ldap authorization only modules have access to the util_ldap user cache without have to require ldap authentication as well. Also required by mod_authnz_ldap so that it's authorization functionality can be used independant from the authentication functionality. [PR 31898] Submitted by: Jari Ahonen [jah progress.com] Reviewed by: Brad Nicholes git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@105667 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/ldap/NWGNUmakefile b/modules/ldap/NWGNUmakefile index aa15476e4ec..98be959d065 100644 --- a/modules/ldap/NWGNUmakefile +++ b/modules/ldap/NWGNUmakefile @@ -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 \ diff --git a/modules/ldap/util_ldap.c b/modules/ldap/util_ldap.c index f724cc426b5..9c44b761971 100644 --- a/modules/ldap/util_ldap.c +++ b/modules/ldap/util_ldap.c @@ -745,24 +745,22 @@ 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 storepassword, 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; @@ -835,7 +833,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; @@ -912,11 +910,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(); @@ -927,6 +934,180 @@ 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, + basedn, scope, 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(st->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); + } + else { + /* 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