You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by mi...@apache.org on 2023/12/02 09:52:54 UTC

svn commit: r1914282 - /httpd/httpd/patches/2.4.x/httpd-2.4-httpd-2.4-ldap-search5.patch

Author: minfrin
Date: Sat Dec  2 09:52:54 2023
New Revision: 1914282

URL: http://svn.apache.org/viewvc?rev=1914282&view=rev
Log:
Update proposal with r1914281.

Added:
    httpd/httpd/patches/2.4.x/httpd-2.4-httpd-2.4-ldap-search5.patch

Added: httpd/httpd/patches/2.4.x/httpd-2.4-httpd-2.4-ldap-search5.patch
URL: http://svn.apache.org/viewvc/httpd/httpd/patches/2.4.x/httpd-2.4-httpd-2.4-ldap-search5.patch?rev=1914282&view=auto
==============================================================================
--- httpd/httpd/patches/2.4.x/httpd-2.4-httpd-2.4-ldap-search5.patch (added)
+++ httpd/httpd/patches/2.4.x/httpd-2.4-httpd-2.4-ldap-search5.patch Sat Dec  2 09:52:54 2023
@@ -0,0 +1,631 @@
+Index: changes-entries/ldap-search.txt
+===================================================================
+--- changes-entries/ldap-search.txt	(nonexistent)
++++ changes-entries/ldap-search.txt	(working copy)
+@@ -0,0 +1,12 @@
++  *) Add the ldap-search option to mod_authnz_ldap, allowing authorization
++     to be based on arbitrary expressions that do not include the username.
++     Make sure that when ldap searches are too long, we explicitly log the
++     error. [Graham Leggett]
++
++
++  *) Add the ldap-search option to mod_authnz_ldap, allowing authorization
++     to be based on arbitrary expressions that do not include the username.
++     Make sure that when ldap searches are too long, we explicitly log the
++     error. [Graham Leggett]
++
++
+Index: docs/manual/expr.xml
+===================================================================
+--- docs/manual/expr.xml	(revision 1914280)
++++ docs/manual/expr.xml	(working copy)
+@@ -61,6 +61,7 @@
+ <seealso><a href="mod/mod_authnz_ldap.html#reqdn">Require ldap-dn</a></seealso>
+ <seealso><a href="mod/mod_authnz_ldap.html#reqattribute">Require ldap-attribute</a></seealso>
+ <seealso><a href="mod/mod_authnz_ldap.html#reqfilter">Require ldap-filter</a></seealso>
++<seealso><a href="mod/mod_authnz_ldap.html#reqsearch">Require ldap-search</a></seealso>
+ <seealso><a href="mod/mod_authz_dbd.html#reqgroup">Require dbd-group</a></seealso>
+ <seealso><a href="mod/mod_authz_dbm.html#reqgroup">Require dbm-group</a></seealso>
+ <seealso><a href="mod/mod_authz_groupfile.html#reqgroup">Require group</a></seealso>
+Index: docs/manual/mod/mod_authnz_ldap.xml
+===================================================================
+--- docs/manual/mod/mod_authnz_ldap.xml	(revision 1914280)
++++ docs/manual/mod/mod_authnz_ldap.xml	(working copy)
+@@ -89,6 +89,7 @@
+           <li><a href="#reqdn">Require ldap-dn</a></li>
+           <li><a href="#reqattribute">Require ldap-attribute</a></li>
+           <li><a href="#reqfilter">Require ldap-filter</a></li>
++          <li><a href="#reqsearch">Require ldap-search</a></li>
+         </ul>
+       </li>
+ 
+@@ -234,6 +235,11 @@
+       directive, and the search filter successfully finds a single user
+       object that matches the dn of the authenticated user.</li>
+ 
++      <li>Grant access if there is a <a href="#reqsearch">
++      <code>Require ldap-search</code></a>
++      directive, and the search filter successfully returns a single
++      matching object with any distinguished name.</li>
++
+       <li>otherwise, deny or decline access</li>
+     </ul>
+ 
+@@ -531,8 +537,30 @@
+ 
+ </section>
+ 
++<section id="reqsearch"><title>Require ldap-search</title>
++
++    <p>The <code>Require ldap-search</code> directive allows the
++    administrator to grant access based on a generic LDAP search filter using an
++    <a href="../expr.html">expression</a>. If there is exactly one match to the search filter,
++    regardless of the distinguished name, access is granted.</p>
++
++    <p>The following directive would grant access to URLs that match the given objects in the
++    LDAP server:</p>
++
++<highlight language="config">
++&lt;LocationMatch ^/dav/(?<SITENAME>[^/]+)/&gt;
++Require ldap-search (cn=%{ldap:%{unescape:%{env:MATCH_SITENAME}} Website)
++&lt;/LocationMatch&gt;
++</highlight>
++
++    <p>Note: care must be taken to ensure that any expressions are properly escaped to guard
++    against LDAP injection. The <strong>ldap</strong> function can be used as per the example
++    above.</p>
++
+ </section>
+ 
++</section>
++
+ <section id="examples"><title>Examples</title>
+ 
+     <ul>
+Index: modules/aaa/mod_authnz_ldap.c
+===================================================================
+--- modules/aaa/mod_authnz_ldap.c	(revision 1914280)
++++ modules/aaa/mod_authnz_ldap.c	(working copy)
+@@ -84,10 +84,10 @@
+ } authn_ldap_config_t;
+ 
+ typedef struct {
+-    char *dn;                       /* The saved dn from a successful search */
+-    char *user;                     /* The username provided by the client */
++    const char *dn;                 /* The saved dn from a successful search */
++    const char *user;               /* The username provided by the client */
+     const char **vals;              /* The additional values pulled during the DN search*/
+-    char *password;                 /* if this module successfully authenticates, the basic auth password, else null */
++    const char *password;           /* if this module successfully authenticates, the basic auth password, else null */
+ } authn_ldap_request_t;
+ 
+ enum auth_ldap_phase {
+@@ -192,11 +192,8 @@
+ 
+ /*
+  * Build the search filter, or at least as much of the search filter that
+- * will fit in the buffer. We don't worry about the buffer not being able
+- * to hold the entire filter. If the buffer wasn't big enough to hold the
+- * filter, ldap_search_s will complain, but the only situation where this
+- * is likely to happen is if the client sent a really, really long
+- * username, most likely as part of an attack.
++ * will fit in the buffer, and return APR_EGENERAL if it won't fit, otherwise
++ * APR_SUCCESS.
+  *
+  * The search filter consists of the filter provided with the URL,
+  * combined with a filter made up of the attribute provided with the URL,
+@@ -209,32 +206,25 @@
+  * search filter will be (&(posixid=*)(uid=userj)).
+  */
+ #define FILTER_LENGTH MAX_STRING_LEN
+-static void authn_ldap_build_filter(char *filtbuf,
++static apr_status_t authn_ldap_build_filter(char filtbuf[FILTER_LENGTH],
+                              request_rec *r,
+-                             const char* sent_user,
+-                             const char* sent_filter,
++                             const char *user,
++                             const char *filter,
+                              authn_ldap_config_t *sec)
+ {
+-    char *p, *q, *filtbuf_end;
+-    char *user, *filter;
++    char *q;
++    const char *p, *filtbuf_end;
+     apr_xlate_t *convset = NULL;
+     apr_size_t inbytes;
+     apr_size_t outbytes;
+     char *outbuf;
+-    int nofilter = 0;
++    int nofilter = 0, len;
++    apr_status_t rv = APR_SUCCESS;
+ 
+-    if (sent_user != NULL) {
+-        user = apr_pstrdup (r->pool, sent_user);
++    if (!filter) {
++        filter = sec->filter;
+     }
+-    else
+-        return;
+ 
+-    if (sent_filter != NULL) {
+-        filter = apr_pstrdup (r->pool, sent_filter);
+-    }
+-    else
+-        filter = sec->filter;
+-
+     if (charset_conversions) {
+         convset = get_conv_set(r);
+     }
+@@ -246,7 +236,7 @@
+ 
+         /* Convert the user name to UTF-8.  This is only valid for LDAP v3 */
+         if (apr_xlate_conv_buffer(convset, user, &inbytes, outbuf, &outbytes) == APR_SUCCESS) {
+-            user = apr_pstrdup(r->pool, outbuf);
++            user = outbuf;
+         }
+     }
+ 
+@@ -255,11 +245,11 @@
+      * config-supplied portions.
+      */
+ 
+-    if ((nofilter = (filter && !strcasecmp(filter, "none")))) { 
+-        apr_snprintf(filtbuf, FILTER_LENGTH, "(%s=", sec->attribute);
++    if ((nofilter = (!filter || !*filter || !strcasecmp(filter, "none")))) { 
++        len = apr_snprintf(filtbuf, FILTER_LENGTH, "(%s=", sec->attribute);
+     }
+     else { 
+-        apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", filter, sec->attribute);
++        len = apr_snprintf(filtbuf, FILTER_LENGTH, "(&(%s)(%s=", filter, sec->attribute);
+     }
+ 
+     /*
+@@ -267,12 +257,13 @@
+      * LDAP filter metachars are escaped.
+      */
+     filtbuf_end = filtbuf + FILTER_LENGTH - 1;
++    for (p = user, q = filtbuf + len; *p; ) {
++        if (strchr("*()\\", *p) != NULL) {
+ #if APR_HAS_MICROSOFT_LDAPSDK
+-    for (p = user, q=filtbuf + strlen(filtbuf);
+-         *p && q < filtbuf_end; ) {
+-        if (strchr("*()\\", *p) != NULL) {
+-            if ( q + 3 >= filtbuf_end)
+-              break;  /* Don't write part of escape sequence if we can't write all of it */
++            if (q + 3 >= filtbuf_end) { /* accounts for final \0 */
++                rv = APR_EGENERAL;
++                goto out;
++            }
+             *q++ = '\\';
+             switch ( *p++ )
+             {
+@@ -292,23 +283,24 @@
+                 *q++ = '5';
+                 *q++ = 'c';
+                 break;
+-                        }
+-        }
+-        else
+-            *q++ = *p++;
+-    }
++            }
+ #else
+-    for (p = user, q=filtbuf + strlen(filtbuf);
+-         *p && q < filtbuf_end; *q++ = *p++) {
+-        if (strchr("*()\\", *p) != NULL) {
++            if (q + 2 >= filtbuf_end) { /* accounts for final \0 */
++                rv = APR_EGENERAL;
++                goto out;
++            }
+             *q++ = '\\';
+-            if (q >= filtbuf_end) {
+-              break;
++            *q++ = *p++;
++#endif
++        }
++        else {
++            if (q + 1 >= filtbuf_end) { /* accounts for final \0 */
++                rv = APR_EGENERAL;
++                goto out;
+             }
++            *q++ = *p++;
+         }
+     }
+-#endif
+-    *q = '\0';
+ 
+     /*
+      * Append the closing parens of the filter, unless doing so would
+@@ -316,14 +308,24 @@
+      */
+ 
+     if (nofilter) { 
+-        if (q + 1 <= filtbuf_end)
+-            strcat(filtbuf, ")");
++        if (q + 1 >= filtbuf_end) { /* accounts for final \0 */
++            rv = APR_EGENERAL;
++            goto out;
++        }
++        *q++ = ')';
+     } 
+     else { 
+-        if (q + 2 <= filtbuf_end)
+-            strcat(filtbuf, "))");
++        if (q + 2 >= filtbuf_end) { /* accounts for final \0 */
++            rv = APR_EGENERAL;
++            goto out;
++        }
++        *q++ = ')';
++        *q++ = ')';
+     }
+ 
++out:
++    *q = '\0';
++    return rv;
+ }
+ 
+ static void *create_authnz_ldap_dir_config(apr_pool_t *p, char *d)
+@@ -371,15 +373,12 @@
+     return APR_SUCCESS;
+ }
+ 
+-static int set_request_vars(request_rec *r, enum auth_ldap_phase phase) {
++static int set_request_vars(request_rec *r, enum auth_ldap_phase phase, const char **vals) {
+     char *prefix = NULL;
+     int prefix_len;
+     int remote_user_attribute_set = 0;
+-    authn_ldap_request_t *req =
+-        (authn_ldap_request_t *)ap_get_module_config(r->request_config, &authnz_ldap_module);
+     authn_ldap_config_t *sec =
+         (authn_ldap_config_t *)ap_get_module_config(r->per_dir_config, &authnz_ldap_module);
+-    const char **vals = req->vals;
+ 
+     prefix = (phase == LDAP_AUTHN) ? AUTHN_PREFIX : sec->authz_prefix;
+     prefix_len = strlen(prefix);
+@@ -441,8 +440,8 @@
+     authn_ldap_config_t *sec =
+         (authn_ldap_config_t *)ap_get_module_config(r->per_dir_config, &authnz_ldap_module);
+ 
+-    char *binddn = sec->binddn;
+-    char *bindpw = sec->bindpw;
++    const char *binddn = sec->binddn;
++    const char *bindpw = sec->bindpw;
+ 
+     /* If the per-request config isn't set, we didn't authenticate this user, and leave the default credentials */
+     if (req && req->password &&
+@@ -549,7 +548,13 @@
+                   "auth_ldap authenticate: using URL %s", sec->url);
+ 
+     /* build the username filter */
+-    authn_ldap_build_filter(filtbuf, r, user, NULL, sec);
++    if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, user, NULL, sec)) {
++        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02622)
++                      "auth_ldap authenticate: ldap filter too long (>%d): %s",
++                      FILTER_LENGTH, filtbuf);
++        util_ldap_connection_close(ldc);
++        return AUTH_GENERAL_ERROR;
++    }
+ 
+     ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                       "auth_ldap authenticate: final authn filter is %s", filtbuf);
+@@ -606,15 +611,15 @@
+     }
+ 
+     /* mark the user and DN */
+-    req->dn = apr_pstrdup(r->pool, dn);
+-    req->user = apr_pstrdup(r->pool, user);
+-    req->password = apr_pstrdup(r->pool, password);
++    req->dn = dn;
++    req->user = user;
++    req->password = password;
+     if (sec->user_is_dn) {
+-        r->user = req->dn;
++        r->user = (char *)req->dn;
+     }
+ 
+     /* add environment variables */
+-    remote_user_attribute_set = set_request_vars(r, LDAP_AUTHN);
++    remote_user_attribute_set = set_request_vars(r, LDAP_AUTHN, req->vals);
+ 
+     /* sanity check */
+     if (sec->remote_user_attribute && !remote_user_attribute_set) {
+@@ -696,7 +701,12 @@
+             sizeof(authn_ldap_request_t));
+ 
+         /* Build the username filter */
+-        authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec);
++        if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
++            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02623)
++                          "auth_ldap authorize: ldap filter too long (>%d): %s",
++                          FILTER_LENGTH, filtbuf);
++            return AUTHZ_DENIED;
++        }
+ 
+         /* Search for the user DN */
+         result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
+@@ -710,7 +720,7 @@
+         }
+ 
+         ap_set_module_config(r->request_config, &authnz_ldap_module, req);
+-        req->dn = apr_pstrdup(r->pool, dn);
++        req->dn = dn;
+         req->user = r->user;
+ 
+     }
+@@ -740,7 +750,7 @@
+             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01703)
+                           "auth_ldap authorize: require user: authorization "
+                           "successful");
+-            set_request_vars(r, LDAP_AUTHZ);
++            set_request_vars(r, LDAP_AUTHZ, req->vals);
+             return AUTHZ_GRANTED;
+         }
+         default: {
+@@ -762,7 +772,7 @@
+                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01705)
+                               "auth_ldap authorize: "
+                               "require user: authorization successful");
+-                set_request_vars(r, LDAP_AUTHZ);
++                set_request_vars(r, LDAP_AUTHZ, req->vals);
+                 return AUTHZ_GRANTED;
+             }
+             default: {
+@@ -880,7 +890,12 @@
+         req = (authn_ldap_request_t *)apr_pcalloc(r->pool,
+             sizeof(authn_ldap_request_t));
+         /* Build the username filter */
+-        authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec);
++        if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
++            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02624)
++                          "auth_ldap authorize: ldap filter too long (>%d): %s",
++                          FILTER_LENGTH, filtbuf);
++            return AUTHZ_DENIED;
++        }
+ 
+         /* Search for the user DN */
+         result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
+@@ -894,7 +909,7 @@
+         }
+ 
+         ap_set_module_config(r->request_config, &authnz_ldap_module, req);
+-        req->dn = apr_pstrdup(r->pool, dn);
++        req->dn = dn;
+         req->user = r->user;
+     }
+ 
+@@ -949,7 +964,7 @@
+                           "[%s][%d - %s]",
+                           ent[i].name, ldc->reason, result,
+                           ldap_err2string(result));
+-            set_request_vars(r, LDAP_AUTHZ);
++            set_request_vars(r, LDAP_AUTHZ, req->vals);
+             return AUTHZ_GRANTED;
+         }
+         else { 
+@@ -988,7 +1003,7 @@
+                           "(attribute %s) [%s][%d - %s]",
+                           ent[i].name, ldc->reason, result,
+                           ldap_err2string(result));
+-            set_request_vars(r, LDAP_AUTHZ);
++            set_request_vars(r, LDAP_AUTHZ, req->vals);
+             return AUTHZ_GRANTED;
+         }
+         else {
+@@ -1069,7 +1084,12 @@
+         req = (authn_ldap_request_t *)apr_pcalloc(r->pool,
+             sizeof(authn_ldap_request_t));
+         /* Build the username filter */
+-        authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec);
++        if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
++            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02625)
++                          "auth_ldap authorize: ldap filter too long (>%d): %s",
++                          FILTER_LENGTH, filtbuf);
++            return AUTHZ_DENIED;
++        }
+ 
+         /* Search for the user DN */
+         result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
+@@ -1083,7 +1103,7 @@
+         }
+ 
+         ap_set_module_config(r->request_config, &authnz_ldap_module, req);
+-        req->dn = apr_pstrdup(r->pool, dn);
++        req->dn = dn;
+         req->user = r->user;
+     }
+ 
+@@ -1110,7 +1130,7 @@
+             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01726)
+                           "auth_ldap authorize: "
+                           "require dn: authorization successful");
+-            set_request_vars(r, LDAP_AUTHZ);
++            set_request_vars(r, LDAP_AUTHZ, req->vals);
+             return AUTHZ_GRANTED;
+         }
+         default: {
+@@ -1191,7 +1211,12 @@
+         req = (authn_ldap_request_t *)apr_pcalloc(r->pool,
+             sizeof(authn_ldap_request_t));
+         /* Build the username filter */
+-        authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec);
++        if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
++            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02626)
++                          "auth_ldap authorize: ldap filter too long (>%d): %s",
++                          FILTER_LENGTH, filtbuf);
++            return AUTHZ_DENIED;
++        }
+ 
+         /* Search for the user DN */
+         result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
+@@ -1205,7 +1230,7 @@
+         }
+ 
+         ap_set_module_config(r->request_config, &authnz_ldap_module, req);
+-        req->dn = apr_pstrdup(r->pool, dn);
++        req->dn = dn;
+         req->user = r->user;
+     }
+ 
+@@ -1239,7 +1264,7 @@
+                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01735)
+                               "auth_ldap authorize: "
+                               "require attribute: authorization successful");
+-                set_request_vars(r, LDAP_AUTHZ);
++                set_request_vars(r, LDAP_AUTHZ, req->vals);
+                 return AUTHZ_GRANTED;
+             }
+             default: {
+@@ -1319,7 +1344,12 @@
+         req = (authn_ldap_request_t *)apr_pcalloc(r->pool,
+             sizeof(authn_ldap_request_t));
+         /* Build the username filter */
+-        authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec);
++        if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, r->user, NULL, sec)) {
++            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02627)
++                          "auth_ldap authorize: ldap filter too long (>%d): %s",
++                          FILTER_LENGTH, filtbuf);
++            return AUTHZ_DENIED;
++        }
+ 
+         /* Search for the user DN */
+         result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
+@@ -1333,7 +1363,7 @@
+         }
+ 
+         ap_set_module_config(r->request_config, &authnz_ldap_module, req);
+-        req->dn = apr_pstrdup(r->pool, dn);
++        req->dn = dn;
+         req->user = r->user;
+     }
+ 
+@@ -1359,7 +1389,12 @@
+                       "auth_ldap authorize: checking filter %s", t);
+ 
+         /* Build the username filter */
+-        authn_ldap_build_filter(filtbuf, r, req->user, t, sec);
++        if (APR_SUCCESS != authn_ldap_build_filter(filtbuf, r, req->user, t, sec)) {
++            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02628)
++                          "auth_ldap authorize: ldap filter too long (>%d): %s",
++                          FILTER_LENGTH, filtbuf);
++            return AUTHZ_DENIED;
++        }
+ 
+         /* Search for the user DN */
+         result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
+@@ -1384,7 +1419,7 @@
+                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01745)
+                               "auth_ldap authorize: require ldap-filter: "
+                               "authorization successful");
+-                set_request_vars(r, LDAP_AUTHZ);
++                set_request_vars(r, LDAP_AUTHZ, req->vals);
+                 return AUTHZ_GRANTED;
+             }
+             case LDAP_FILTER_ERROR: {
+@@ -1411,6 +1446,83 @@
+     return AUTHZ_DENIED;
+ }
+ 
++static authz_status ldapsearch_check_authorization(request_rec *r,
++                                                   const char *require_args,
++                                                   const void *parsed_require_args)
++{
++    int result = 0;
++    authn_ldap_request_t *req =
++        (authn_ldap_request_t *)ap_get_module_config(r->request_config, &authnz_ldap_module);
++    authn_ldap_config_t *sec =
++        (authn_ldap_config_t *)ap_get_module_config(r->per_dir_config, &authnz_ldap_module);
++
++    util_ldap_connection_t *ldc = NULL;
++
++    const char *err = NULL;
++    const ap_expr_info_t *expr = parsed_require_args;
++    const char *require;
++    const char *t;
++    const char *dn = NULL;
++
++    if (!sec->have_ldap_url) {
++        return AUTHZ_DENIED;
++    }
++
++    if (sec->host) {
++        ldc = get_connection_for_authz(r, LDAP_SEARCH);
++        apr_pool_cleanup_register(r->pool, ldc,
++                                  authnz_ldap_cleanup_connection_close,
++                                  apr_pool_cleanup_null);
++    }
++    else {
++        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(02636)
++                      "auth_ldap authorize: no sec->host - weird...?");
++        return AUTHZ_DENIED;
++    }
++
++    require = ap_expr_str_exec(r, expr, &err);
++    if (err) {
++        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02629)
++                      "auth_ldap authorize: require ldap-search: Can't "
++                      "evaluate require expression: %s", err);
++        return AUTHZ_DENIED;
++    }
++
++    t = require;
++
++    if (t[0]) {
++
++        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02630)
++                      "auth_ldap authorize: checking filter %s", t);
++
++        /* Search for the user DN */
++        result = util_ldap_cache_getuserdn(r, ldc, sec->url, sec->basedn,
++             sec->scope, sec->attributes, t, &dn, &(req->vals));
++
++        /* Make sure that the filtered search returned a single dn */
++        if (result == LDAP_SUCCESS && dn) {
++            req->dn = dn;
++            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02631)
++                          "auth_ldap authorize: require ldap-search: "
++                          "authorization successful");
++            set_request_vars(r, LDAP_AUTHZ, req->vals);
++            return AUTHZ_GRANTED;
++        }
++        else {
++            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02632)
++                          "auth_ldap authorize: require ldap-search: "
++                          "%s authorization failed [%s][%s]",
++                          t, ldc->reason, ldap_err2string(result));
++        }
++    }
++
++    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02633)
++                  "auth_ldap authorize search: authorization denied for "
++                  "to %s", r->uri);
++
++    return AUTHZ_DENIED;
++}
++
+ static const char *ldap_parse_config(cmd_parms *cmd, const char *require_line,
+                                      const void **parsed_require_line)
+ {
+@@ -1919,6 +2031,12 @@
+     &ldap_parse_config,
+ };
+ 
++static const authz_provider authz_ldapsearch_provider =
++{
++    &ldapsearch_check_authorization,
++    &ldap_parse_config,
++};
++
+ static void ImportULDAPOptFn(void)
+ {
+     util_ldap_connection_close  = APR_RETRIEVE_OPTIONAL_FN(uldap_connection_close);
+@@ -1959,6 +2077,10 @@
+                               AUTHZ_PROVIDER_VERSION,
+                               &authz_ldapfilter_provider,
+                               AP_AUTH_INTERNAL_PER_CONF);
++    ap_register_auth_provider(p, AUTHZ_PROVIDER_GROUP, "ldap-search",
++                              AUTHZ_PROVIDER_VERSION,
++                              &authz_ldapsearch_provider,
++                              AP_AUTH_INTERNAL_PER_CONF);
+ 
+     ap_hook_post_config(authnz_ldap_post_config,NULL,NULL,APR_HOOK_MIDDLE);
+