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 2014/04/25 13:14:36 UTC

svn commit: r1589993 - in /httpd/httpd/trunk: CHANGES docs/manual/expr.xml docs/manual/mod/mod_authnz_ldap.xml modules/aaa/mod_authnz_ldap.c

Author: minfrin
Date: Fri Apr 25 11:14:36 2014
New Revision: 1589993

URL: http://svn.apache.org/r1589993
Log:
Add the ldap-search option to mod_authnz_ldap, allowing authorization
to be based on arbitrary expressions that do not include the username.

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/docs/manual/expr.xml
    httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml
    httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1589993&r1=1589992&r2=1589993&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Fri Apr 25 11:14:36 2014
@@ -1,6 +1,10 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) Add the ldap-search option to mod_authnz_ldap, allowing authorization
+     to be based on arbitrary expressions that do not include the username.
+     [Graham Leggett]
+
   *) Add the ldap function to the expression API, allowing LDAP filters and
      distinguished names based on expressions to be escaped correctly to
      guard against LDAP injection. [Graham Leggett]

Modified: httpd/httpd/trunk/docs/manual/expr.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/expr.xml?rev=1589993&r1=1589992&r2=1589993&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/expr.xml (original)
+++ httpd/httpd/trunk/docs/manual/expr.xml Fri Apr 25 11:14:36 2014
@@ -55,6 +55,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>

Modified: httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml?rev=1589993&r1=1589992&r2=1589993&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml (original)
+++ httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml Fri Apr 25 11:14:36 2014
@@ -88,6 +88,7 @@ for HTTP Basic authentication.</descript
           <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>
 
@@ -223,6 +224,11 @@ for HTTP Basic authentication.</descript
       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>
 
@@ -508,6 +514,28 @@ AuthLDAPMaxSubGroupDepth 1
 
 </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>

Modified: httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c?rev=1589993&r1=1589992&r2=1589993&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c (original)
+++ httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c Fri Apr 25 11:14:36 2014
@@ -367,15 +367,12 @@ static apr_status_t authnz_ldap_cleanup_
     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);
@@ -599,7 +596,7 @@ static authn_status authn_ldap_check_pas
     }
 
     /* 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) {
@@ -725,7 +722,7 @@ static authz_status ldapuser_check_autho
             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: {
@@ -747,7 +744,7 @@ static authz_status ldapuser_check_autho
                 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: {
@@ -934,7 +931,7 @@ static authz_status ldapgroup_check_auth
                           "[%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 { 
@@ -973,7 +970,7 @@ static authz_status ldapgroup_check_auth
                           "(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 {
@@ -1095,7 +1092,7 @@ static authz_status ldapdn_check_authori
             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: {
@@ -1224,7 +1221,7 @@ static authz_status ldapattribute_check_
                 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: {
@@ -1369,7 +1366,7 @@ static authz_status ldapfilter_check_aut
                 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: {
@@ -1396,6 +1393,80 @@ static authz_status ldapfilter_check_aut
     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_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(01738)
+                      "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()
+                      "auth_ldap authorize: require ldap-search: Can't "
+                      "evaluate require expression: %s", err);
+        return AUTHZ_DENIED;
+    }
+
+    t = require;
+
+    if (t[0]) {
+        const char **vals;
+
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+                      "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, &vals);
+
+        /* Make sure that the filtered search returned a single dn */
+        if (result == LDAP_SUCCESS && dn) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+                          "auth_ldap authorize: require ldap-search: "
+                          "authorization successful");
+            return AUTHZ_GRANTED;
+        }
+        else {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+                          "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()
+                  "auth_ldap authorize filter: 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)
 {
@@ -1899,6 +1970,12 @@ static const authz_provider authz_ldapfi
     &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);
@@ -1939,6 +2016,10 @@ static void register_hooks(apr_pool_t *p
                               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);
 



Re: svn commit: r1589993 - in /httpd/httpd/trunk: CHANGES docs/manual/expr.xml docs/manual/mod/mod_authnz_ldap.xml modules/aaa/mod_authnz_ldap.c

Posted by Yann Ylavic <yl...@gmail.com>.
On Fri, Apr 25, 2014 at 1:15 PM <mi...@apache.org> wrote:
>
> Author: minfrin
> Date: Fri Apr 25 11:14:36 2014
> New Revision: 1589993
>
> URL: http://svn.apache.org/r1589993
> Log:
> Add the ldap-search option to mod_authnz_ldap, allowing authorization
> to be based on arbitrary expressions that do not include the username.
[]
>
> --- httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml (original)
> +++ httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml Fri Apr 25 11:14:36 2014
[]
> @@ -508,6 +514,28 @@ AuthLDAPMaxSubGroupDepth 1
>
>  </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>

I get from this that there should be one match..

>
> --- httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c (original)
> +++ httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c Fri Apr 25 11:14:36 2014
[]
>
> +static authz_status ldapsearch_check_authorization(request_rec *r,
> +                                                   const char *require_args,
> +                                                   const void *parsed_require_args)
> +{
> +    int result = 0;
> +    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(01738)
> +                      "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()
> +                      "auth_ldap authorize: require ldap-search: Can't "
> +                      "evaluate require expression: %s", err);
> +        return AUTHZ_DENIED;
> +    }
> +
> +    t = require;
> +
> +    if (t[0]) {
> +        const char **vals;
> +
> +        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
> +                      "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, &vals);
> +
> +        /* Make sure that the filtered search returned a single dn */

And it's restated here..

> +        if (result == LDAP_SUCCESS && dn) {
> +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
> +                          "auth_ldap authorize: require ldap-search: "
> +                          "authorization successful");
> +            return AUTHZ_GRANTED;

I get that for "ldap-filter" (unlike for "ldap-search here) we'll do a
util_ldap_cache_comparedn() to (double) check the returned DN somehow
(sorry I don't really know how LDAP works), not here though because we
don't require a particular DN but just a single one.
But what makes sure that it's the case here?

> +        }
> +        else {
> +            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
> +                          "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()
> +                  "auth_ldap authorize filter: authorization denied for "
> +                  "to %s", r->uri);
> +
> +    return AUTHZ_DENIED;
> +}


Regards;
Yann.

Re: svn commit: r1589993 - in /httpd/httpd/trunk: CHANGES docs/manual/expr.xml docs/manual/mod/mod_authnz_ldap.xml modules/aaa/mod_authnz_ldap.c

Posted by Christophe JAILLET <ch...@wanadoo.fr>.
Le 18/11/2023 à 20:52, Yann Ylavic a écrit :
> On Wed, Apr 30, 2014 at 1:02 AM Yann Ylavic <yl...@gmail.com> wrote:
>>
>> On Tue, Apr 29, 2014 at 10:54 PM, Christophe JAILLET
>> <ch...@wanadoo.fr> wrote:
>>> Hi,
>>>
>>> doc does not build because of <SITENAME> below:
>>>
>>> CJ
>>>
>>> Le 25/04/2014 13:14, minfrin@apache.org a écrit :
>>>> +<highlight language="config">
>>>> +&lt;LocationMatch ^/dav/(?<SITENAME>[^/]+)/&gt;
>>>
>>>                                    ^ There
>>>
>>
>> Hmm, won't LocationMatch itself be broken by the inner <>s ?
> 
> Wow, fortunately I didn't hold my breath on this one :)
> Someone needs to answer to this former/younger/naive me though and
> since I'm on this commit again: look Yann, this match is double-quoted
> now so we should be fine!
> 

In fact, at that time, another solution was provided in r1591113.

But what you propose above, should have worked as well, I guess. :).

CJ

Re: svn commit: r1589993 - in /httpd/httpd/trunk: CHANGES docs/manual/expr.xml docs/manual/mod/mod_authnz_ldap.xml modules/aaa/mod_authnz_ldap.c

Posted by Yann Ylavic <yl...@gmail.com>.
On Wed, Apr 30, 2014 at 1:02 AM Yann Ylavic <yl...@gmail.com> wrote:
>
> On Tue, Apr 29, 2014 at 10:54 PM, Christophe JAILLET
> <ch...@wanadoo.fr> wrote:
> > Hi,
> >
> > doc does not build because of <SITENAME> below:
> >
> > CJ
> >
> > Le 25/04/2014 13:14, minfrin@apache.org a écrit :
> >> +<highlight language="config">
> >> +&lt;LocationMatch ^/dav/(?<SITENAME>[^/]+)/&gt;
> >
> >                                   ^ There
> >
>
> Hmm, won't LocationMatch itself be broken by the inner <>s ?

Wow, fortunately I didn't hold my breath on this one :)
Someone needs to answer to this former/younger/naive me though and
since I'm on this commit again: look Yann, this match is double-quoted
now so we should be fine!

Re: svn commit: r1589993 - in /httpd/httpd/trunk: CHANGES docs/manual/expr.xml docs/manual/mod/mod_authnz_ldap.xml modules/aaa/mod_authnz_ldap.c

Posted by Yann Ylavic <yl...@gmail.com>.
On Tue, Apr 29, 2014 at 10:54 PM, Christophe JAILLET
<ch...@wanadoo.fr> wrote:
> Hi,
>
> doc does not build because of <SITENAME> below:
>
> CJ
>
> Le 25/04/2014 13:14, minfrin@apache.org a écrit :
>> +<highlight language="config">
>> +&lt;LocationMatch ^/dav/(?<SITENAME>[^/]+)/&gt;
>
>                                   ^ There
>

Hmm, won't LocationMatch itself be broken by the inner <>s ?

Re: svn commit: r1589993 - in /httpd/httpd/trunk: CHANGES docs/manual/expr.xml docs/manual/mod/mod_authnz_ldap.xml modules/aaa/mod_authnz_ldap.c

Posted by Christophe JAILLET <ch...@wanadoo.fr>.
Hi,

doc does not build because of <SITENAME> below:

CJ

Le 25/04/2014 13:14, minfrin@apache.org a écrit :
> Author: minfrin
> Date: Fri Apr 25 11:14:36 2014
> New Revision: 1589993
>
> URL: http://svn.apache.org/r1589993
> Log:
> Add the ldap-search option to mod_authnz_ldap, allowing authorization
> to be based on arbitrary expressions that do not include the username.
>
> Modified:
>      httpd/httpd/trunk/CHANGES
>      httpd/httpd/trunk/docs/manual/expr.xml
>      httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml
>      httpd/httpd/trunk/modules/aaa/mod_authnz_ldap.c
>
> Modified: httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml
> URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml?rev=1589993&r1=1589992&r2=1589993&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml (original)
> +++ httpd/httpd/trunk/docs/manual/mod/mod_authnz_ldap.xml Fri Apr 25 11:14:36 2014
> @@ -508,6 +514,28 @@ AuthLDAPMaxSubGroupDepth 1
>   
>   </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;
                                   ^ There
> +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>