]> granicus.if.org Git - apache/commitdiff
Add the ldap-search option to mod_authnz_ldap, allowing authorization
authorGraham Leggett <minfrin@apache.org>
Fri, 25 Apr 2014 11:14:36 +0000 (11:14 +0000)
committerGraham Leggett <minfrin@apache.org>
Fri, 25 Apr 2014 11:14:36 +0000 (11:14 +0000)
to be based on arbitrary expressions that do not include the username.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1589993 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
docs/manual/expr.xml
docs/manual/mod/mod_authnz_ldap.xml
modules/aaa/mod_authnz_ldap.c

diff --git a/CHANGES b/CHANGES
index d7e90a06b713333b73251c34912b5b9a376dfc74..ee51512596497c404c678a364b89fca0dd7ab4ee 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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]
index bd3956f46ad3035ba51de251626b09a3691b6072..ba5b2b7af1220fa7b166e75732f43db04c7994b9 100644 (file)
@@ -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>
index 2e29e5d14e07243d8aea1b27b97d2e45eb3f5bc2..970e35537fdd27a85f0e1f5bb22cc4f5678e6a9e 100644 (file)
@@ -88,6 +88,7 @@ for HTTP Basic authentication.</description>
           <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.</description>
       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>
index 4ed6c21b8a452510bef93f9edcbe34b672c905ee..8d4c1ee22886bf724c901d899e3bd46c504819c2 100644 (file)
@@ -367,15 +367,12 @@ static apr_status_t authnz_ldap_cleanup_connection_close(void *param)
     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_password(request_rec *r, const char *user,
     }
 
     /* 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_authorization(request_rec *r,
             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_authorization(request_rec *r,
                 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_authorization(request_rec *r,
                           "[%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_authorization(request_rec *r,
                           "(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_authorization(request_rec *r,
             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_authorization(request_rec *r,
                 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_authorization(request_rec *r,
                 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_authorization(request_rec *r,
     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_ldapfilter_provider =
     &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);