From: Jim Jagielski Date: Fri, 17 Aug 2012 13:45:22 +0000 (+0000) Subject: Merge r1351020, r1370466 from trunk: X-Git-Tag: 2.4.3~12 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=30ed35311d64f0bf5ff545d790f2be502fa249f1;p=apache Merge r1351020, r1370466 from trunk: 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 --- diff --git a/CHANGES b/CHANGES index 07494b3368..8ec80bd82d 100644 --- 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 ] + *) 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 1fadfd4b95..d75540f809 100644 --- 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. diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml index f046c28124..f7de9f29d1 100644 --- a/docs/manual/mod/mod_lua.xml +++ b/docs/manual/mod/mod_lua.xml @@ -130,6 +130,60 @@ handlers (or hooks, or filters) in the same script. +
+Writing Authorization Providers + +

mod_authz_core 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 +Require directive gives +the name of the responsible authorization provider. For any +Require line, +mod_authz_core 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.

+ +

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 apache2.AUTHZ_DENIED_NO_USER. +This will cause authentication to proceed and the authz provider to be +called a second time.

+ +

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:

+ + +authz_provider.lua + +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 + + +

The following configuration registers this function as provider +foo and configures it for URL /:

+ +LuaAuthzProvider foo authz_provider.lua authz_check_foo +<Location /> + Require foo 10.1.2.3 john_doe +</Location> + + +
+
Writing Hooks

Hook functions are how modules (and Lua scripts) participate in the @@ -798,4 +852,30 @@ hook function usually returns OK, DECLINED, or HTTP_FORBIDDEN.

+ +LuaAuthzProvider +Plug an authorization provider function into mod_authz_core + +LuaAuthzProvider provider_name /path/to/lua/script.lua function_name +server config +2.5.0 and later + + +

After a lua function has been registered as authorization provider, it can be used +with the Require directive:

+ + + +LuaRoot /usr/local/apache2/lua +LuaAuthzProvider foo authz.lua authz_check_foo +<Location /> + Require foo bar +</Location> + + + +
+
+ + diff --git a/modules/lua/lua_vmprep.c b/modules/lua/lua_vmprep.c index e6f4ff990b..e821fee3d1 100644 --- a/modules/lua/lua_vmprep.c +++ b/modules/lua/lua_vmprep.c @@ -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); diff --git a/modules/lua/mod_lua.c b/modules/lua/mod_lua.c index c63508d0dc..b5b626d1eb 100644 --- a/modules/lua/mod_lua.c +++ b/modules/lua/mod_lua.c @@ -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) = {