]> granicus.if.org Git - apache/commitdiff
Merge r1351020, r1370466 from trunk:
authorJim Jagielski <jim@apache.org>
Fri, 17 Aug 2012 13:45:22 +0000 (13:45 +0000)
committerJim Jagielski <jim@apache.org>
Fri, 17 Aug 2012 13:45:22 +0000 (13:45 +0000)
Add new directive LuaAuthzProvider to allow implementing an
authorization provider in lua

There is only one global provider name space, therefore allow
LuaAuthzProvider only in global scope.

Remove unnecessary server config field.

Submitted by: sf
Reviewed/backported by: jim

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1374252 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
STATUS
docs/manual/mod/mod_lua.xml
modules/lua/lua_vmprep.c
modules/lua/mod_lua.c

diff --git a/CHANGES b/CHANGES
index 07494b3368648f64372f2d31d5c0deb2db1c00c8..8ec80bd82d88e450c057ef9a33e33c7ff12d2420 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,9 @@ Changes with Apache 2.4.3
      possible XSS for a site where untrusted users can upload files to
      a location with MultiViews enabled. [Niels Heinen <heinenn google.com>]
 
+  *) mod_lua: Add new directive LuaAuthzProvider to allow implementing an
+     authorization provider in lua. [Stefan Fritsch]
+
   *) httpd.conf: Added configuration directives to set a bad_DNT environment
      variable based on User-Agent and to remove the DNT header field from
      incoming requests when a match occurs. This currently has the effect of
diff --git a/STATUS b/STATUS
index 1fadfd4b952493ca3ffb1efa0ede34473c954add..d75540f8091715f53c9667324a1a43968816ec16 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -88,13 +88,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
-   * mod_lua: Add a Lua authz provider.
-     trunk patch: http://svn.apache.org/viewvc?view=revision&revision=1351020
-                  http://svn.apache.org/viewvc?view=revision&revision=1370466
-     2.4.x patch: trunk patch works.
-     +1: humbedooh, rjung, jim
-     rjung: docs compatibility note 2.5 -> 2.4.3, drop message-tag file from patch
-
    * core: make ap_parse_form_data less strict when checking for a correct 
            Content-Type header when parsing POST, or we risk losing valid 
            data with an appended charset.
index f046c281242fc241e17ec91c4113d4d260782de8..f7de9f29d10b0fc0c9573312f0c0f6f872688d85 100644 (file)
@@ -130,6 +130,60 @@ handlers (or hooks, or filters) in the same script.
 
 </section>
 
+<section id="writingauthzproviders">
+<title>Writing Authorization Providers</title>
+
+<p><module>mod_authz_core</module> provides a high-level interface to
+authorization that is much easier to use than using into the relevant
+hooks directly. The first argument to the
+<directive module="mod_authz_core">Require</directive> directive gives
+the name of the responsible authorization provider. For any
+<directive module="mod_authz_core">Require</directive> line,
+<module>mod_authz_core</module> will call the authorization provider
+of the given name, passing the rest of the line as parameters. The
+provider will then check authorization and pass the result as return
+value.</p>
+
+<p>The authz provider is normally called before authentication. If it needs to
+know the authenticated user name (or if the user will be authenticated at
+all), the provider must return <code>apache2.AUTHZ_DENIED_NO_USER</code>.
+This will cause authentication to proceed and the authz provider to be
+called a second time.</p>
+
+<p>The following authz provider function takes two arguments, one ip
+address and one user name. It will allow access from the given ip address
+without authentication, or if the authenticated user matches the second
+argument:</p>
+
+<highlight language="lua">
+<strong>authz_provider.lua</strong>
+
+require 'apache2'
+
+function authz_check_foo(r, ip, user)
+    if r.useragent_ip == ip then
+        return apache2.AUTHZ_GRANTED
+    elseif r.user == nil then
+        return apache2.AUTHZ_DENIED_NO_USER
+    elseif r.user == user then
+        return apache2.AUTHZ_GRANTED
+    else
+        return apache2.AUTHZ_DENIED
+    end
+end
+</highlight>
+
+<p>The following configuration registers this function as provider
+<code>foo</code> and configures it for URL <code>/</code>:</p>
+<highlight language="config">
+LuaAuthzProvider foo authz_provider.lua authz_check_foo
+&lt;Location /&gt;
+  Require foo 10.1.2.3 john_doe
+&lt;/Location&gt;
+</highlight>
+
+</section>
+
 <section id="writinghooks"><title>Writing Hooks</title>
 
 <p>Hook functions are how modules (and Lua scripts) participate in the
@@ -798,4 +852,30 @@ hook function usually returns OK, DECLINED, or HTTP_FORBIDDEN.</p>
 </usage>
 </directivesynopsis>
 
+<directivesynopsis>
+<name>LuaAuthzProvider</name>
+<description>Plug an authorization provider function into <module>mod_authz_core</module>
+</description>
+<syntax>LuaAuthzProvider provider_name /path/to/lua/script.lua function_name</syntax>
+<contextlist><context>server config</context> </contextlist>
+<compatibility>2.5.0 and later</compatibility>
+
+<usage>
+<p>After a lua function has been registered as authorization provider, it can be used
+with the <directive module="mod_authz_core">Require</directive> directive:</p>
+
+<example>
+<highlight language="config">
+LuaRoot /usr/local/apache2/lua
+LuaAuthzProvider foo authz.lua authz_check_foo
+&lt;Location /&gt;
+  Require foo bar
+&lt;/Location&gt;
+</highlight>
+</example>
+
+</usage>
+</directivesynopsis>
+
+
 </modulesynopsis>
index e6f4ff990bc65e1b2306e74e227d6b2c40ca9926..e821fee3d16bb5bc9241712353bb38ecff0f8241 100644 (file)
@@ -19,6 +19,7 @@
 #include "apr_uuid.h"
 #include "lua_config.h"
 #include "apr_file_info.h"
+#include "mod_auth.h"
 
 APLOG_USE_MODULE(lua);
 
@@ -121,6 +122,11 @@ AP_LUA_DECLARE(void) ap_lua_load_apache2_lmodule(lua_State *L)
     makeintegerfield(L, PROXYREQ_REVERSE);
     makeintegerfield(L, PROXYREQ_RESPONSE);
     makeintegerfield(L, PROXYREQ_RESPONSE);
+    makeintegerfield(L, AUTHZ_DENIED);
+    makeintegerfield(L, AUTHZ_GRANTED);
+    makeintegerfield(L, AUTHZ_NEUTRAL);
+    makeintegerfield(L, AUTHZ_GENERAL_ERROR);
+    makeintegerfield(L, AUTHZ_DENIED_NO_USER);
 
     /*
        makeintegerfield(L, HTTP_CONTINUE);
index c63508d0dc73a217fe10c3a860ad36d2c57b718e..b5b626d1eb5250c2249af025f6e74693842bd958 100644 (file)
@@ -24,6 +24,7 @@
 #include "lua_config.h"
 #include "apr_optional.h"
 #include "mod_ssl.h"
+#include "mod_auth.h"
 
 #ifdef APR_HAS_THREADS
 #include "apr_thread_proc.h"
@@ -39,11 +40,22 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ap_lua, AP_LUA, int, lua_request,
 static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *lua_ssl_val = NULL;
 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *lua_ssl_is_https = NULL;
 
-     module AP_MODULE_DECLARE_DATA lua_module;
+module AP_MODULE_DECLARE_DATA lua_module;
 
 #define AP_LUA_HOOK_FIRST (APR_HOOK_FIRST - 1)
 #define AP_LUA_HOOK_LAST  (APR_HOOK_LAST  + 1)
 
+typedef struct {
+    const char *name;
+    const char *file_name;
+    const char *function_name;
+    ap_lua_vm_spec *spec;
+    apr_array_header_t *args;
+} lua_authz_provider_spec;
+
+apr_hash_t *lua_authz_providers;
+
+
 /**
  * error reporting if lua has an error.
  * Extracts the error from lua stack and prints
@@ -128,7 +140,7 @@ static ap_lua_vm_spec *create_vm_spec(apr_pool_t **lifecycle_pool,
     else {
         spec->file = r->filename;
     }
-    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO()
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, APLOGNO(02313)
                   "%s details: scope: %s, file: %s, func: %s",
                   what, scope_to_string(spec->scope), spec->file,
                   function ? function : "-");
@@ -964,6 +976,131 @@ AP_LUA_DECLARE(int) ap_lua_ssl_is_https(conn_rec *c)
 
 /*******************************/
 
+static const char *lua_authz_parse(cmd_parms *cmd, const char *require_line,
+                                   const void **parsed_require_line)
+{
+    const char *provider_name;
+    lua_authz_provider_spec *spec;
+
+    apr_pool_userdata_get((void**)&provider_name, AUTHZ_PROVIDER_NAME_NOTE,
+                          cmd->temp_pool);
+    ap_assert(provider_name != NULL);
+
+    spec = apr_hash_get(lua_authz_providers, provider_name, APR_HASH_KEY_STRING);
+    ap_assert(spec != NULL);
+
+    if (require_line && *require_line) {
+        const char *arg;
+        spec->args = apr_array_make(cmd->pool, 2, sizeof(const char *));
+        while ((arg = ap_getword_conf(cmd->pool, &require_line)) && *arg) {
+            APR_ARRAY_PUSH(spec->args, const char *) = arg;
+        }
+    }
+
+    *parsed_require_line = spec;
+    return NULL;
+}
+
+static authz_status lua_authz_check(request_rec *r, const char *require_line,
+                                    const void *parsed_require_line)
+{
+    apr_pool_t *pool;
+    ap_lua_vm_spec *spec;
+    lua_State *L;
+    ap_lua_server_cfg *server_cfg = ap_get_module_config(r->server->module_config,
+                                                         &lua_module);
+    const ap_lua_dir_cfg *cfg = ap_get_module_config(r->per_dir_config,
+                                                     &lua_module);
+    const lua_authz_provider_spec *prov_spec = parsed_require_line;
+    int result;
+    int nargs = 0;
+
+    spec = create_vm_spec(&pool, r, cfg, server_cfg, prov_spec->file_name,
+                          NULL, 0, prov_spec->function_name, "authz provider");
+
+    L = ap_lua_get_lua_state(pool, spec);
+    if (L == NULL) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02314)
+                      "Unable to compile VM for authz provider %s", prov_spec->name);
+        return AUTHZ_GENERAL_ERROR;
+    }
+    lua_getglobal(L, prov_spec->function_name);
+    if (!lua_isfunction(L, -1)) {
+        ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(02319)
+                      "Unable to find function %s in %s",
+                      prov_spec->function_name, prov_spec->file_name);
+        return AUTHZ_GENERAL_ERROR;
+    }
+    ap_lua_run_lua_request(L, r);
+    if (prov_spec->args) {
+        int i;
+        if (!lua_checkstack(L, prov_spec->args->nelts)) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02315)
+                          "Error: authz provider %s: too many arguments", prov_spec->name);
+            return AUTHZ_GENERAL_ERROR;
+        }
+        for (i = 0; i < prov_spec->args->nelts; i++) {
+            const char *arg = APR_ARRAY_IDX(prov_spec->args, i, const char *);
+            lua_pushstring(L, arg);
+        }
+        nargs = prov_spec->args->nelts;
+    }
+    if (lua_pcall(L, 1 + nargs, 1, 0)) {
+        const char *err = lua_tostring(L, -1);
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02316)
+                      "Error executing authz provider %s: %s", prov_spec->name, err);
+        return AUTHZ_GENERAL_ERROR;
+    }
+    if (!lua_isnumber(L, -1)) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02317)
+                      "Error: authz provider %s did not return integer", prov_spec->name);
+        return AUTHZ_GENERAL_ERROR;
+    }
+    result = lua_tointeger(L, -1);
+    switch (result) {
+        case AUTHZ_DENIED:
+        case AUTHZ_GRANTED:
+        case AUTHZ_NEUTRAL:
+        case AUTHZ_GENERAL_ERROR:
+        case AUTHZ_DENIED_NO_USER:
+            return result;
+        default:
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02318)
+                          "Error: authz provider %s: invalid return value %d",
+                          prov_spec->name, result);
+    }
+    return AUTHZ_GENERAL_ERROR;
+}
+
+static const authz_provider lua_authz_provider =
+{
+    &lua_authz_check,
+    &lua_authz_parse,
+};
+
+static const char *register_authz_provider(cmd_parms *cmd, void *_cfg,
+                                           const char *name, const char *file,
+                                           const char *function)
+{
+    lua_authz_provider_spec *spec;
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err)
+        return err;
+
+    spec = apr_pcalloc(cmd->pool, sizeof(*spec));
+    spec->name = name;
+    spec->file_name = file;
+    spec->function_name = function;
+
+    apr_hash_set(lua_authz_providers, name, APR_HASH_KEY_STRING, spec);
+    ap_register_auth_provider(cmd->pool, AUTHZ_PROVIDER_GROUP, name,
+                              AUTHZ_PROVIDER_VERSION,
+                              &lua_authz_provider,
+                              AP_AUTH_INTERNAL_PER_CONF);
+    return NULL;
+}
+
+
 command_rec lua_commands[] = {
 
     AP_INIT_TAKE1("LuaRoot", register_lua_root, NULL, OR_ALL,
@@ -975,6 +1112,8 @@ command_rec lua_commands[] = {
     AP_INIT_TAKE1("LuaPackageCPath", register_package_cdir, NULL, OR_ALL,
                   "Add a directory to lua's package.cpath"),
 
+    AP_INIT_TAKE3("LuaAuthzProvider", register_authz_provider, NULL, RSRC_CONF|EXEC_ON_READ,
+                  "Provide an authorization provider"),
 
     AP_INIT_TAKE23("LuaHookTranslateName", register_translate_name_hook, NULL,
                   OR_ALL,
@@ -1207,6 +1346,9 @@ static void lua_register_hooks(apr_pool_t *p)
 
     APR_OPTIONAL_HOOK(ap_lua, lua_request, lua_request_hook, NULL, NULL,
                       APR_HOOK_REALLY_FIRST);
+
+    /* providers */
+    lua_authz_providers = apr_hash_make(p);
 }
 
 AP_DECLARE_MODULE(lua) = {