ASF Bugzilla – Attachment 20549 Details for
Bug 42891
Support for nested groups in LDAP
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch to add nested group support to httpd-trunk
Apache_nested_group_support.diff (text/plain), 39.37 KB, created by
Paul J. Reder
on 2007-07-25 09:27:33 UTC
(
hide
)
Description:
Patch to add nested group support to httpd-trunk
Filename:
MIME Type:
Creator:
Paul J. Reder
Created:
2007-07-25 09:27:33 UTC
Size:
39.37 KB
patch
obsolete
>Index: httpd-trunk/modules/ldap/util_ldap_cache.c >=================================================================== >--- httpd-trunk/modules/ldap/util_ldap_cache.c (revision 558897) >+++ httpd-trunk/modules/ldap/util_ldap_cache.c (working copy) >@@ -259,12 +259,14 @@ > if (node) { > if (!(node->dn = util_ald_strdup(cache, n->dn)) || > !(node->attrib = util_ald_strdup(cache, n->attrib)) || >- !(node->value = util_ald_strdup(cache, n->value))) { >+ !(node->value = util_ald_strdup(cache, n->value)) || >+ ((n->subgroupList) && !(node->subgroupList = util_ald_sgl_dup(cache, n->subgroupList)))) { > util_ldap_compare_node_free(cache, node); > return NULL; > } > node->lastcompare = n->lastcompare; > node->result = n->result; >+ node->sgl_processed = n->sgl_processed; > return node; > } > else { >@@ -275,6 +277,8 @@ > void util_ldap_compare_node_free(util_ald_cache_t *cache, void *n) > { > util_compare_node_t *node = n; >+ >+ util_ald_sgl_free(cache, &(node->subgroupList)); > util_ald_free(cache, node->dn); > util_ald_free(cache, node->attrib); > util_ald_free(cache, node->value); >@@ -286,6 +290,8 @@ > util_compare_node_t *node = n; > char date_str[APR_CTIME_LEN+1]; > char *cmp_result; >+ char *sub_groups_val; >+ char *sub_groups_checked; > > apr_ctime(date_str, node->lastcompare); > >@@ -299,6 +305,20 @@ > cmp_result = apr_itoa(r->pool, node->result); > } > >+ if(node->subgroupList) { >+ sub_groups_val = "Yes"; >+ } >+ else { >+ sub_groups_val = "No"; >+ } >+ >+ if(node->sgl_processed) { >+ sub_groups_checked = "Yes"; >+ } >+ else { >+ sub_groups_checked = "No"; >+ } >+ > ap_rprintf(r, > "<tr valign='top'>" > "<td nowrap>%s</td>" >@@ -306,12 +326,16 @@ > "<td nowrap>%s</td>" > "<td nowrap>%s</td>" > "<td nowrap>%s</td>" >+ "<td nowrap>%s</td>" >+ "<td nowrap>%s</td>" > "</tr>", > node->dn, > node->attrib, > node->value, > date_str, >- cmp_result); >+ cmp_result, >+ sub_groups_val, >+ sub_groups_checked); > } > > /* ------------------------------------------------------------------ */ >Index: httpd-trunk/modules/ldap/util_ldap_cache.h >=================================================================== >--- httpd-trunk/modules/ldap/util_ldap_cache.h (revision 558897) >+++ httpd-trunk/modules/ldap/util_ldap_cache.h (working copy) >@@ -97,6 +97,14 @@ > } util_url_node_t; > > /* >+ * When a group is found, subgroups are stored in the group's cache entry. >+ */ >+typedef struct util_compare_subgroup_t { >+ const char **subgroupDNs; >+ int len; >+} util_compare_subgroup_t; >+ >+/* > * We cache every successful search and bind operation, using the username > * as the key. Each node in the cache contains the returned DN, plus the > * password used to bind. >@@ -121,6 +129,8 @@ > const char *value; > apr_time_t lastcompare; > int result; >+ int sgl_processed; /* 0 if no sgl processing yet. 1 if sgl has been processed (even if SGL is NULL). Saves repeat work on leaves. */ >+ struct util_compare_subgroup_t *subgroupList; > } util_compare_node_t; > > /* >@@ -169,6 +179,8 @@ > void util_ald_free(util_ald_cache_t *cache, const void *ptr); > void *util_ald_alloc(util_ald_cache_t *cache, unsigned long size); > const char *util_ald_strdup(util_ald_cache_t *cache, const char *s); >+util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl); >+void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl); > > /* Cache managing function */ > unsigned long util_ald_hash_string(int nstr, ...); >Index: httpd-trunk/modules/ldap/util_ldap_cache_mgr.c >=================================================================== >--- httpd-trunk/modules/ldap/util_ldap_cache_mgr.c (revision 558897) >+++ httpd-trunk/modules/ldap/util_ldap_cache_mgr.c (working copy) >@@ -126,7 +126,8 @@ > else { > return NULL; > } >- } else { >+ } >+ else { > /* Cache shm is not used */ > return strdup(s); > } >@@ -135,8 +136,46 @@ > #endif > } > >+/* >+ * Duplicate a subgroupList from one compare entry to another. >+ * Returns: ptr to a new copy of the subgroupList or NULL if allocation failed. >+ */ >+util_compare_subgroup_t *util_ald_sgl_dup(util_ald_cache_t *cache, util_compare_subgroup_t *sgl_in) >+{ >+ int i = 0; >+ util_compare_subgroup_t *sgl_out = NULL; > >+ if (!sgl_in) return NULL; >+ >+ sgl_out = (util_compare_subgroup_t *) util_ald_alloc(cache, sizeof(util_compare_subgroup_t)); >+ sgl_out->subgroupDNs = util_ald_alloc(cache, sizeof(char *) * sgl_in->len); >+ sgl_out->len = sgl_in->len; >+ >+ for (i = 0; i < sgl_in->len; i++) { >+ fprintf(stderr, "sgl_dup: Adding %s to sgl\n", sgl_in->subgroupDNs[i]); fflush(stderr); >+ sgl_out->subgroupDNs[i] = util_ald_strdup(cache, sgl_in->subgroupDNs[i]); >+ } >+ >+ return sgl_out; >+} >+ > /* >+ * Delete an entire subgroupList. >+ */ >+void util_ald_sgl_free(util_ald_cache_t *cache, util_compare_subgroup_t **sgl) >+{ >+ int i = 0; >+ if (sgl == NULL || *sgl == NULL) { >+ return; >+ } >+ >+ for (i = 0; i < (*sgl)->len; i++) { >+ util_ald_free(cache, (*sgl)->subgroupDNs[i]); >+ } >+ util_ald_free(cache, *sgl); >+} >+ >+/* > * Computes the hash on a set of strings. The first argument is the number > * of strings to hash, the rest of the args are strings. > * Algorithm taken from glibc. >@@ -365,9 +404,10 @@ > cache->fetches++; > > hashval = (*cache->hash)(payload) % cache->size; >+ > for (p = cache->nodes[hashval]; > p && !(*cache->compare)(p->payload, payload); >- p = p->next) ; >+ p = p->next) ; > > if (p != NULL) { > cache->hits++; >@@ -676,6 +716,8 @@ > "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Value</b></font></td>" > "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Last Compare</b></font></td>" > "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Result</b></font></td>" >+ "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>Sub-groups?</b></font></td>" >+ "<td><font size='-1' face='Arial,Helvetica' color='#ffffff'><b>S-G Checked?</b></font></td>" > "</tr>\n", r > ); > if (n) { >Index: httpd-trunk/modules/ldap/util_ldap.c >=================================================================== >--- httpd-trunk/modules/ldap/util_ldap.c (revision 558897) >+++ httpd-trunk/modules/ldap/util_ldap.c (working copy) >@@ -748,6 +748,8 @@ > the_compare_node.attrib = (char *)attrib; > the_compare_node.value = (char *)value; > the_compare_node.result = 0; >+ the_compare_node.sgl_processed = 0; >+ the_compare_node.subgroupList = NULL; > > compare_nodep = util_ald_cache_fetch(curl->compare_cache, > &the_compare_node); >@@ -760,24 +762,24 @@ > } > else { > /* ...and it is good */ >- /* unlock this read lock */ >- LDAP_CACHE_UNLOCK(); > if (LDAP_COMPARE_TRUE == compare_nodep->result) { > ldc->reason = "Comparison true (cached)"; >- return compare_nodep->result; > } > else if (LDAP_COMPARE_FALSE == compare_nodep->result) { > ldc->reason = "Comparison false (cached)"; >- return compare_nodep->result; > } > else if (LDAP_NO_SUCH_ATTRIBUTE == compare_nodep->result) { > ldc->reason = "Comparison no such attribute (cached)"; >- return compare_nodep->result; > } > else { > ldc->reason = "Comparison undefined (cached)"; >- return compare_nodep->result; > } >+ >+ /* record the result code to return with the reason... */ >+ result = compare_nodep->result; >+ /* and unlock this read lock */ >+ LDAP_CACHE_UNLOCK(); >+ return result; > } > } > /* unlock this read lock */ >@@ -789,6 +791,7 @@ > /* too many failures */ > return result; > } >+ > if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { > /* connect failed */ > return result; >@@ -814,6 +817,8 @@ > LDAP_CACHE_LOCK(); > the_compare_node.lastcompare = curtime; > the_compare_node.result = result; >+ the_compare_node.sgl_processed = 0; >+ the_compare_node.subgroupList = NULL; > > /* If the node doesn't exist then insert it, otherwise just update > * it with the last results >@@ -825,7 +830,12 @@ > || (strcmp(the_compare_node.attrib,compare_nodep->attrib) != 0) > || (strcmp(the_compare_node.value, compare_nodep->value) != 0)) > { >- util_ald_cache_insert(curl->compare_cache, &the_compare_node); >+ void *junk; >+ >+ junk = util_ald_cache_insert(curl->compare_cache, &the_compare_node); >+ if(junk == NULL) { >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] cache_compare: Cache insertion failure.", getpid()); >+ } > } > else { > compare_nodep->lastcompare = curtime; >@@ -849,6 +859,293 @@ > return result; > } > >+/* >+ * Does a recursive lookup operation to try to find a user within (cached) nested >+ * groups. It accepts a cache that it will use to lookup previous compare attempts. >+ * We cache two kinds of compares (require group compares) and (require user >+ * compares). Each compare has a different cache node: require group includes the DN; >+ * require user does not because the require user cache is owned by the >+ * >+ * DON'T CALL THIS UNLESS YOU CALLED uldap_cache_compare FIRST!!!!! >+ */ >+static int uldap_cache_check_subgroups(request_rec *r, util_ldap_connection_t *ldc, >+ const char *url, const char *dn, >+ const char *attrib, const char *value, >+ char **subgroupAttrs, apr_array_header_t *subgroupclasses, >+ int cur_subgroup_depth, int max_subgroup_depth) >+{ >+ int result = LDAP_COMPARE_FALSE; >+ util_url_node_t *curl; >+ util_url_node_t curnode; >+ util_compare_node_t *compare_nodep; >+ util_compare_node_t the_compare_node; >+ util_compare_subgroup_t *tmp_local_sgl = NULL; >+ int failures = 0; >+ LDAPMessage *sga_res, *entry; >+ apr_array_header_t *subgroups = apr_array_make(r->pool, 20, sizeof(char *)); >+ >+ util_ldap_state_t *st = (util_ldap_state_t *) >+ ap_get_module_config(r->server->module_config, >+ &ldap_module); >+ >+ /* >+ * 1. Call uldap_cache_compare for each subgroupclass value to check the generic, >+ * user-agnostic, cached group entry. This will create a new generic cache entry if there >+ * wasn't one. If nothing returns LDAP_COMPARE_TRUE skip to step 5 since we have no groups. >+ * 2. Lock The cache and get the generic cache entry. >+ * 3. Check if there is already a subgrouplist in this generic group's cache entry. >+ * A. If there is, go to step 4. >+ * B. If there isn't: >+ * i) Use ldap_search to get the full list >+ * of subgroup "members" (which may include non-group "members"). >+ * ii) Use uldap_cache_compare to strip the list down to just groups. >+ * iii) Lock and add this stripped down list to the cache of the generic group. >+ * 4. Loop through the sgl and call uldap_cache_compare (using the user info) for each >+ * subgroup to see if the subgroup contains the user and to get the subgroups added to the >+ * cache (with user-afinity, if they aren't already there). >+ * A. If the user is in the subgroup, then we'll be returning LDAP_COMPARE_TRUE. >+ * B. if the user isn't in the subgroup (LDAP_COMPARE_FALSE via uldap_cache_compare) then >+ * recursively call this function to get the sub-subgroups added... >+ * 5. Cleanup local allocations. >+ * 6. Return the final result. >+ */ >+ >+ /* Stop looking at deeper levels of nested groups if we have reached the max. >+ * Since we already checked the top-level group in uldap_cache_compare, we don't >+ * need to check it again here - so if max_subgroup_depth is set to 0, we won't >+ * check it (i.e. that is why we check < rather than <=). >+ * We'll be calling uldap_cache_compare from here to check if the user is in the >+ * next level before we recurse into that next level looking for more subgroups. >+ */ >+ if (cur_subgroup_depth < max_subgroup_depth) { >+ int base_sgcIndex = 0; >+ int lcl_sgl_processedFlag = 0; >+ struct mod_auth_ldap_groupattr_entry_t *sgc_ents = (struct mod_auth_ldap_groupattr_entry_t *) subgroupclasses->elts; >+ >+ /* 1. Check the "groupiness" of the specified basedn. Stopping at the first TRUE return. */ >+ while ((base_sgcIndex < subgroupclasses->nelts) && (result != LDAP_COMPARE_TRUE)) { >+ result = uldap_cache_compare(r, ldc, url, dn, "objectClass", sgc_ents[base_sgcIndex].name); >+ if (result != LDAP_COMPARE_TRUE) { >+ base_sgcIndex++; >+ } >+ } >+ >+ if (result != LDAP_COMPARE_TRUE) { >+ /* The dn we were handed doesn't seem to be a group, how can we check for SUB-groups if this >+ * isn't a group?!?!?! >+ */ >+ ldc->reason = "DN failed group verification."; >+ return result; >+ } >+ >+ /* 2. Find previously created cache entry and check if there is already a subgrouplist. */ >+ LDAP_CACHE_LOCK(); >+ curnode.url = url; >+ curl = util_ald_cache_fetch(st->util_ldap_cache, &curnode); >+ LDAP_CACHE_UNLOCK(); >+ >+ if (curl && curl->compare_cache) { >+ /* make a comparison to the cache */ >+ LDAP_CACHE_LOCK(); >+ >+ the_compare_node.dn = (char *)dn; >+ the_compare_node.attrib = (char *)"objectClass"; >+ the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name; >+ the_compare_node.result = 0; >+ the_compare_node.sgl_processed = 0; >+ the_compare_node.subgroupList = NULL; >+ >+ compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node); >+ >+ if (compare_nodep == NULL) { >+ /* Didn't find it. This shouldn't happen since we just called uldap_cache_compare. */ >+ LDAP_CACHE_UNLOCK(); >+ ldc->reason = "check_subgroups failed to find cached element."; >+ return LDAP_COMPARE_FALSE; >+ } >+ else { >+ /* Found the generic group entry... but the user isn't in this group or we wouldn't be here. */ >+ lcl_sgl_processedFlag = compare_nodep->sgl_processed; >+ if(compare_nodep->sgl_processed && compare_nodep->subgroupList) { >+ /* Make a local copy of the subgroup list */ >+ int i; >+ tmp_local_sgl = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t)); >+ tmp_local_sgl->len = compare_nodep->subgroupList->len; >+ tmp_local_sgl->subgroupDNs = apr_pcalloc(r->pool, sizeof(char *) * compare_nodep->subgroupList->len); >+ for (i = 0; i < compare_nodep->subgroupList->len; i++) { >+ tmp_local_sgl->subgroupDNs[i] = apr_pstrdup(r->pool, compare_nodep->subgroupList->subgroupDNs[i]); >+ } >+ } >+ } >+ /* unlock this read lock */ >+ LDAP_CACHE_UNLOCK(); >+ } >+ else { >+ /* If we get here, something is wrong. Caches should have been created and >+ this group entry should be found in the cache. */ >+ ldc->reason = "check_subgroups failed to find any caches."; >+ return LDAP_COMPARE_FALSE; >+ } >+ >+ result = LDAP_COMPARE_FALSE; >+ >+ /* No cache entry had a processed SGL. Retrieve from LDAP server */ >+ if ((lcl_sgl_processedFlag == 0) && (!tmp_local_sgl)) { >+start_over: >+ /* 3.B. The cache didn't have any subgrouplist yet. Go check for subgroups. */ >+ if (failures++ > 10) { >+ /* too many failures */ >+ return result; >+ } >+ >+ if (LDAP_SUCCESS != (result = uldap_connection_open(r, ldc))) { >+ /* connect failed */ >+ return result; >+ } >+ >+ /* try to do the search */ >+ result = ldap_search_ext_s(ldc->ldap, (char *)dn, LDAP_SCOPE_BASE, >+ (char *)"cn=*", subgroupAttrs, 0, >+ NULL, NULL, NULL, APR_LDAP_SIZELIMIT, &sga_res); >+ if (result == LDAP_SERVER_DOWN) { >+ ldc->reason = "ldap_search_ext_s() for subgroups failed with server down"; >+ uldap_connection_unbind(ldc); >+ 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 subgroups failed"; >+ return result; >+ } >+ >+ entry = ldap_first_entry(ldc->ldap, sga_res); >+ >+ /* >+ * Get values for the provided sub-group attributes. >+ */ >+ if (subgroupAttrs) { >+ int indx = 0, tmp_sgcIndex; >+ >+ while (subgroupAttrs[indx]) { >+ char **values; >+ int val_index = 0; >+ >+ /* Get *all* matching "member" values from this group. */ >+ values = ldap_get_values(ldc->ldap, entry, subgroupAttrs[indx]); >+ >+ if (values) { >+ val_index = 0; >+ /* >+ * Now we are going to pare the subgroup members of this group to *just* >+ * the subgroups, add them to the compare_nodep, and then proceed to check >+ * the new level of subgroups. >+ */ >+ while (values[val_index]) { >+ /* Check if this entry really is a group. */ >+ tmp_sgcIndex = 0; >+ result = LDAP_COMPARE_FALSE; >+ while ((tmp_sgcIndex < subgroupclasses->nelts) && (result != LDAP_COMPARE_TRUE)) { >+ result = uldap_cache_compare(r, ldc, url, values[val_index], "objectClass", >+ sgc_ents[tmp_sgcIndex].name); >+ >+ if (result != LDAP_COMPARE_TRUE) { >+ tmp_sgcIndex++; >+ } >+ } >+ /* It's a group, so add it to the array. */ >+ if (result == LDAP_COMPARE_TRUE) { >+ char **newgrp = (char **) apr_array_push(subgroups); >+ *newgrp = apr_pstrdup(r->pool, values[val_index]); >+ } >+ val_index++; >+ } >+ ldap_value_free(values); >+ } >+ indx++; >+ } >+ } >+ >+ ldap_msgfree(sga_res); >+ >+ if (subgroups->nelts > 0) { >+ /* We need to fill in tmp_local_subgroups using the data from LDAP */ >+ int sgindex; >+ char **group; >+ tmp_local_sgl = apr_pcalloc(r->pool, sizeof(util_compare_subgroup_t)); >+ tmp_local_sgl->subgroupDNs = apr_pcalloc(r->pool, sizeof(char *) * (subgroups->nelts)); >+ for (sgindex = 0; (group = apr_array_pop(subgroups)); sgindex++) { >+ tmp_local_sgl->subgroupDNs[sgindex] = apr_pstrdup(r->pool, *group); >+ } >+ tmp_local_sgl->len = sgindex; >+ } >+ >+ /* Find the generic group cache entry and add the sgl. */ >+ LDAP_CACHE_LOCK(); >+ >+ the_compare_node.dn = (char *)dn; >+ the_compare_node.attrib = (char *)"objectClass"; >+ the_compare_node.value = (char *)sgc_ents[base_sgcIndex].name; >+ the_compare_node.result = 0; >+ the_compare_node.sgl_processed = 0; >+ the_compare_node.subgroupList = NULL; >+ >+ compare_nodep = util_ald_cache_fetch(curl->compare_cache, &the_compare_node); >+ >+ if (compare_nodep == NULL) { >+ /* Didn't find it. This shouldn't happen since we just called uldap_cache_compare. */ >+ LDAP_CACHE_UNLOCK(); >+ ldc->reason = "check_subgroups failed to find the cache entry to add sub-group list to."; >+ return LDAP_COMPARE_FALSE; >+ } >+ else { >+ /* overwrite SGL if it was previously updated between the last >+ ** two times we looked at the cache >+ */ >+ compare_nodep->sgl_processed = 1; >+ if (tmp_local_sgl) { >+ compare_nodep->subgroupList = util_ald_sgl_dup(curl->compare_cache, tmp_local_sgl); >+ } >+ else { >+ /* We didn't find a single subgroup, next time save us from looking */ >+ compare_nodep->subgroupList = NULL; >+ } >+ } >+ /* unlock this read lock */ >+ LDAP_CACHE_UNLOCK(); >+ } >+ >+ /* tmp_local_sgl has either been created, or copied out of the cache */ >+ /* If tmp_local_sgl is NULL, there are no subgroups to process and we'll return false */ >+ result = LDAP_COMPARE_FALSE; >+ if (tmp_local_sgl) { >+ int sgindex = 0; >+ const char *group = NULL; >+ while ((result != LDAP_COMPARE_TRUE) && (sgindex < tmp_local_sgl->len)) { >+ group = tmp_local_sgl->subgroupDNs[sgindex]; >+ /* 4. Now loop through the subgroupList and call uldap_cache_compare to check for the user. */ >+ result = uldap_cache_compare(r, ldc, url, group, attrib, value); >+ if (result == LDAP_COMPARE_TRUE) { >+ /* 4.A. We found the user in the subgroup. Return LDAP_COMPARE_TRUE. */ >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "[%" APR_PID_T_FMT "] util_ldap:" >+ " Found the user in a subgroup (%s) at level %d of %d. (7).", >+ getpid(), group, cur_subgroup_depth+1, max_subgroup_depth); >+ } >+ else { >+ /* 4.B. We didn't find the user in this subgroup, so recurse into it and keep looking. */ >+ result = uldap_cache_check_subgroups(r, ldc, url, group, attrib, >+ value, subgroupAttrs, subgroupclasses, >+ cur_subgroup_depth+1, max_subgroup_depth); >+ } >+ sgindex++; >+ } >+ } >+ } >+ >+ return result; >+} >+ >+ > static int uldap_cache_checkuserid(request_rec *r, util_ldap_connection_t *ldc, > const char *url, const char *basedn, > int scope, char **attrs, const char *filter, >@@ -2106,6 +2403,7 @@ > APR_REGISTER_OPTIONAL_FN(uldap_cache_checkuserid); > APR_REGISTER_OPTIONAL_FN(uldap_cache_getuserdn); > APR_REGISTER_OPTIONAL_FN(uldap_ssl_supported); >+ APR_REGISTER_OPTIONAL_FN(uldap_cache_check_subgroups); > > ap_hook_post_config(util_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE); > ap_hook_handler(util_ldap_handler, NULL, NULL, APR_HOOK_MIDDLE); >Index: httpd-trunk/modules/aaa/mod_authnz_ldap.c >=================================================================== >--- httpd-trunk/modules/aaa/mod_authnz_ldap.c (revision 558897) >+++ httpd-trunk/modules/aaa/mod_authnz_ldap.c (working copy) >@@ -67,9 +67,14 @@ > > int have_ldap_url; /* Set if we have found an LDAP url */ > >- apr_array_header_t *groupattr; /* List of Group attributes */ >+ apr_array_header_t *groupattr; /* List of Group attributes identifying user members. Default:"member uniqueMember" */ > int group_attrib_is_dn; /* If true, the group attribute is the DN, otherwise, > it's the exact string passed by the HTTP client */ >+ apr_array_header_t *subgroupattrs; /* List of attributes used to find subgroup references >+ within a group directory entry. Default:"member uniqueMember" */ >+ char **sgAttributes; /* Array of strings constructed (post-config) from subgroupattrs. Last entry is NULL. */ >+ apr_array_header_t *subgroupclasses; /* List of object classes of sub-groups. Default:"groupOfNames groupOfUniqueNames" */ >+ int maxNestingDepth; /* Maximum recursive nesting depth permitted during subgroup processing. Default: 10 */ > > int secure; /* True if SSL connections are requested */ > } authn_ldap_config_t; >@@ -82,16 +87,13 @@ > /* maximum group elements supported */ > #define GROUPATTR_MAX_ELTS 10 > >-struct mod_auth_ldap_groupattr_entry_t { >- char *name; >-}; >- > module AP_MODULE_DECLARE_DATA authnz_ldap_module; > > static APR_OPTIONAL_FN_TYPE(uldap_connection_close) *util_ldap_connection_close; > static APR_OPTIONAL_FN_TYPE(uldap_connection_find) *util_ldap_connection_find; > static APR_OPTIONAL_FN_TYPE(uldap_cache_comparedn) *util_ldap_cache_comparedn; > static APR_OPTIONAL_FN_TYPE(uldap_cache_compare) *util_ldap_cache_compare; >+static APR_OPTIONAL_FN_TYPE(uldap_cache_check_subgroups) *util_ldap_cache_check_subgroups; > static APR_OPTIONAL_FN_TYPE(uldap_cache_checkuserid) *util_ldap_cache_checkuserid; > static APR_OPTIONAL_FN_TYPE(uldap_cache_getuserdn) *util_ldap_cache_getuserdn; > static APR_OPTIONAL_FN_TYPE(uldap_ssl_supported) *util_ldap_ssl_supported; >@@ -285,6 +287,10 @@ > */ > sec->groupattr = apr_array_make(p, GROUPATTR_MAX_ELTS, > sizeof(struct mod_auth_ldap_groupattr_entry_t)); >+ sec->subgroupattrs = apr_array_make(p, GROUPATTR_MAX_ELTS, >+ sizeof(struct mod_auth_ldap_groupattr_entry_t)); >+ sec->subgroupclasses = apr_array_make(p, GROUPATTR_MAX_ELTS, >+ sizeof(struct mod_auth_ldap_groupattr_entry_t)); > > sec->have_ldap_url = 0; > sec->url = ""; >@@ -294,6 +300,8 @@ > sec->deref = always; > sec->group_attrib_is_dn = 1; > sec->secure = -1; /*Initialize to unset*/ >+ sec->maxNestingDepth = 10; >+ sec->sgAttributes = NULL; > > sec->user_is_dn = 0; > sec->remote_user_attribute = NULL; >@@ -648,13 +656,49 @@ > grp = apr_array_push(sec->groupattr); > grp->name = "member"; > grp = apr_array_push(sec->groupattr); >- grp->name = "uniquemember"; >+ grp->name = "uniqueMember"; > #if APR_HAS_THREADS > apr_thread_mutex_unlock(sec->lock); > #endif > } > > /* >+ * If there are no elements in the sub group attribute array, the default >+ * should be member and uniquemember; populate the array now. >+ */ >+ if (sec->subgroupattrs->nelts == 0) { >+ struct mod_auth_ldap_groupattr_entry_t *grp; >+#if APR_HAS_THREADS >+ apr_thread_mutex_lock(sec->lock); >+#endif >+ grp = apr_array_push(sec->subgroupattrs); >+ grp->name = "member"; >+ grp = apr_array_push(sec->subgroupattrs); >+ grp->name = "uniqueMember"; >+#if APR_HAS_THREADS >+ apr_thread_mutex_unlock(sec->lock); >+#endif >+ } >+ >+ /* >+ * If there are no elements in the sub group classes array, the default >+ * should be groupOfNames and groupOfUniqueNames; populate the array now. >+ */ >+ if (sec->subgroupclasses->nelts == 0) { >+ struct mod_auth_ldap_groupattr_entry_t *grp; >+#if APR_HAS_THREADS >+ apr_thread_mutex_lock(sec->lock); >+#endif >+ grp = apr_array_push(sec->subgroupclasses); >+ grp->name = "groupOfNames"; >+ grp = apr_array_push(sec->subgroupclasses); >+ grp->name = "groupOfUniqueNames"; >+#if APR_HAS_THREADS >+ apr_thread_mutex_unlock(sec->lock); >+#endif >+ } >+ >+ /* > * If we have been authenticated by some other module than mod_auth_ldap, > * the req structure needed for authorization needs to be created > * and populated with the userid and DN of the account in LDAP >@@ -734,6 +778,45 @@ > getpid(), ent[i].name, ldc->reason, ldap_err2string(result)); > return AUTHZ_GRANTED; > } >+ case LDAP_COMPARE_FALSE: { >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, >+ "[%" APR_PID_T_FMT "] auth_ldap authorise: require group \"%s\": " >+ "failed [%s][%d - %s], checking sub-groups", >+ getpid(), t, ldc->reason, result, ldap_err2string(result)); >+ >+ if(sec->sgAttributes == NULL) { >+ struct mod_auth_ldap_groupattr_entry_t *sg_ent = (struct mod_auth_ldap_groupattr_entry_t *) sec->subgroupattrs->elts; >+ char **sg_attrs; >+ int sga_index; >+ >+ /* Allocate a null-terminated array of attribute strings. */ >+ sg_attrs = apr_pcalloc(sec->pool, (sec->subgroupattrs->nelts+1) * sizeof(char *)); >+ for(sga_index = 0; sga_index < sec->subgroupattrs->nelts; sga_index++) { >+ sg_attrs[sga_index] = apr_pstrdup(sec->pool, sg_ent[sga_index].name); >+ } >+ sg_attrs[sec->subgroupattrs->nelts] = NULL; >+ sec->sgAttributes = sg_attrs; >+ } >+ >+ result = util_ldap_cache_check_subgroups(r, ldc, sec->url, t, ent[i].name, >+ sec->group_attrib_is_dn ? req->dn : req->user, >+ sec->sgAttributes, sec->subgroupclasses, >+ 0, sec->maxNestingDepth); >+ if(result == LDAP_COMPARE_TRUE) { >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, >+ "[%" APR_PID_T_FMT "] auth_ldap authorise: require group (sub-group): " >+ "authorisation successful (attribute %s) [%s][%d - %s]", >+ getpid(), ent[i].name, ldc->reason, result, ldap_err2string(result)); >+ return OK; >+ } >+ else { >+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, >+ "[%" APR_PID_T_FMT "] auth_ldap authorise: require group (sub-group) \"%s\": " >+ "authorisation failed [%s][%d - %s]", >+ getpid(), t, ldc->reason, result, ldap_err2string(result)); >+ } >+ break; >+ } > default: { > ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, > "[%" APR_PID_T_FMT "] auth_ldap authorize: require group \"%s\": " >@@ -1249,6 +1332,47 @@ > return NULL; > } > >+static const char *mod_auth_ldap_add_subgroup_attribute(cmd_parms *cmd, void *config, const char *arg) >+{ >+ struct mod_auth_ldap_groupattr_entry_t *new; >+ >+ authn_ldap_config_t *sec = config; >+ >+ if (sec->subgroupattrs->nelts > GROUPATTR_MAX_ELTS) >+ return "Too many AuthLDAPSubGroupAttribute values"; >+ >+ new = apr_array_push(sec->subgroupattrs); >+ new->name = apr_pstrdup(cmd->pool, arg); >+ >+ return NULL; >+} >+ >+static const char *mod_auth_ldap_add_subgroup_class(cmd_parms *cmd, void *config, const char *arg) >+{ >+ struct mod_auth_ldap_groupattr_entry_t *new; >+ >+ authn_ldap_config_t *sec = config; >+ >+ if (sec->subgroupclasses->nelts > GROUPATTR_MAX_ELTS) >+ return "Too many AuthLDAPSubGroupClass values"; >+ >+ new = apr_array_push(sec->subgroupclasses); >+ new->name = apr_pstrdup(cmd->pool, arg); >+ >+ return NULL; >+} >+ >+static const char *mod_auth_ldap_set_subgroup_maxdepth(cmd_parms *cmd, >+ void *config, >+ const char *max_depth) >+{ >+ authn_ldap_config_t *sec = config; >+ >+ sec->maxNestingDepth = atol(max_depth); >+ >+ return NULL; >+} >+ > static const char *mod_auth_ldap_add_group_attribute(cmd_parms *cmd, void *config, const char *arg) > { > struct mod_auth_ldap_groupattr_entry_t *new; >@@ -1312,8 +1436,7 @@ > "the REMOTE_USER variable will contain whatever value the remote user sent."), > > AP_INIT_TAKE1("AuthLDAPRemoteUserAttribute", ap_set_string_slot, >- (void *)APR_OFFSETOF(authn_ldap_config_t, >- remote_user_attribute), OR_AUTHCFG, >+ (void *)APR_OFFSETOF(authn_ldap_config_t, remote_user_attribute), OR_AUTHCFG, > "Override the user supplied username and place the " > "contents of this attribute in the REMOTE_USER " > "environment variable."), >@@ -1325,9 +1448,20 @@ > "(at the expense of possible false matches). See the documentation for " > "a complete description of this option."), > >+ AP_INIT_ITERATE("AuthLDAPSubGroupAttribute", mod_auth_ldap_add_subgroup_attribute, NULL, OR_AUTHCFG, >+ "Attribute labels used to define sub-group (or nested group) membership in groups - " >+ "defaults to member and uniqueMember (one per directive)"), >+ >+ AP_INIT_ITERATE("AuthLDAPSubGroupClass", mod_auth_ldap_add_subgroup_class, NULL, OR_AUTHCFG, >+ "LDAP objectClass values used to identify sub-group instances - " >+ "defaults to groupOfNames and groupOfUniqueNames (one per directive)"), >+ >+ AP_INIT_TAKE1("AuthLDAPMaxSubGroupDepth", mod_auth_ldap_set_subgroup_maxdepth, NULL, OR_AUTHCFG, >+ "Maximum subgroup nesting depth to be evaluated - defaults to 10 (top-level group = 0)"), >+ > AP_INIT_ITERATE("AuthLDAPGroupAttribute", mod_auth_ldap_add_group_attribute, NULL, OR_AUTHCFG, >- "A list of attributes used to define group membership - defaults to " >- "member and uniquemember"), >+ "A list of attribute labels used to identify the user members of groups - defaults to " >+ "member and uniquemember (one per directive)"), > > AP_INIT_FLAG("AuthLDAPGroupAttributeIsDN", ap_set_flag_slot, > (void *)APR_OFFSETOF(authn_ldap_config_t, group_attrib_is_dn), OR_AUTHCFG, >@@ -1336,7 +1470,7 @@ > "provided by the client directly. Defaults to 'on'."), > > AP_INIT_TAKE1("AuthLDAPDereferenceAliases", mod_auth_ldap_set_deref, NULL, OR_AUTHCFG, >- "Determines how aliases are handled during a search. Can bo one of the" >+ "Determines how aliases are handled during a search. Can be one of the" > "values \"never\", \"searching\", \"finding\", or \"always\". " > "Defaults to always."), > >@@ -1468,6 +1602,7 @@ > util_ldap_cache_checkuserid = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_checkuserid); > util_ldap_cache_getuserdn = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_getuserdn); > util_ldap_ssl_supported = APR_RETRIEVE_OPTIONAL_FN(uldap_ssl_supported); >+ util_ldap_cache_check_subgroups = APR_RETRIEVE_OPTIONAL_FN(uldap_cache_check_subgroups); > } > > static void register_hooks(apr_pool_t *p) >Index: httpd-trunk/include/util_ldap.h >=================================================================== >--- httpd-trunk/include/util_ldap.h (revision 558897) >+++ httpd-trunk/include/util_ldap.h (working copy) >@@ -144,6 +144,10 @@ > > } util_ldap_state_t; > >+/* Used to store arrays of attribute labels/values. */ >+struct mod_auth_ldap_groupattr_entry_t { >+ char *name; >+}; > > /** > * Open a connection to an LDAP server >@@ -244,7 +248,8 @@ > * @param attrib The attribute within the object we are comparing for. > * @param value The value of the attribute we are trying to compare for. > * @tip Use this function to determine whether an attribute/value pair exists within an >- * object. Typically this would be used to determine LDAP group membership. >+ * object. Typically this would be used to determine LDAP top-level group >+ * membership. > * @fn int util_ldap_cache_compare(request_rec *r, util_ldap_connection_t *ldc, > * const char *url, const char *dn, const char *attrib, const char *value) > */ >@@ -252,6 +257,36 @@ > const char *url, const char *dn, const char *attrib, const char *value)); > > /** >+ * An LDAP function that checks if the specified user is a member of a subgroup. >+ * @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 dn The DN of the object in which we find subgroups to search within. >+ * @param attrib The attribute within group objects that identify users. >+ * @param value The user attribute value we are trying to compare for. >+ * @param subgroupAttrs The attributes within group objects that identify subgroups. >+ * Array of strings. >+ * @param subgroupclasses The objectClass values used to identify groups (and >+ * subgroups). apr_array_header_t *. >+ * @param cur_subgroup_depth Current recursive depth during subgroup processing. >+ * @param max_subgroup_depth Maximum depth of recursion allowed during subgroup >+ * processing. >+ * @tip Use this function to determine whether an attribute/value pair exists within a >+ * starting group object or one of its nested subgroups. Typically this would be >+ * used to determine LDAP nested group membership. >+ * @deffunc int util_ldap_cache_check_subgroups(request_rec *r, util_ldap_connection_t >+ * *ldc, const char *url, const char *dn, >+ * const char *attrib, const char value, >+ * char **subgroupAttrs, apr_array_header_t >+ * *subgroupclasses, int cur_subgroup_depth, int >+ * max_subgroup_depth ) >+ */ >+APR_DECLARE_OPTIONAL_FN(int,uldap_cache_check_subgroups,(request_rec *r, util_ldap_connection_t *ldc, >+ const char *url, const char *dn, const char *attrib, const char *value, >+ char **subgroupAttrs, apr_array_header_t *subgroupclasses, >+ int cur_subgroup_depth, int max_subgroup_depth)); >+ >+/** > * Checks a username/password combination by binding to the LDAP server > * @param r The request record > * @param ldc The LDAP connection being used.
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 42891
: 20549