diff --git a/CHANGES b/CHANGES index e584188ba5..e43b479346 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ Changes with Apache 2.5.0 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] + *) mod_lua: Add a few missing request_rec fields. Rename remote_ip to client_ip to match conn_rec. [Stefan Fritsch] diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number index 2612554b2d..ed5edbf7f2 100644 --- a/docs/log-message-tags/next-number +++ b/docs/log-message-tags/next-number @@ -1 +1 @@ -2313 +2320 diff --git a/docs/manual/mod/mod_lua.xml b/docs/manual/mod/mod_lua.xml index fe3b0b8f13..abb531d34f 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 @@ -795,4 +849,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 e3c5be99f7..7447337fae 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 18553eb212..115afc64b6 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 : "-"); @@ -965,6 +977,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, NOT_IN_DIR_LOC_FILE); + 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, @@ -976,6 +1113,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, @@ -1208,6 +1347,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) = { diff --git a/modules/lua/mod_lua.h b/modules/lua/mod_lua.h index 2504b029a7..77020ab16f 100644 --- a/modules/lua/mod_lua.h +++ b/modules/lua/mod_lua.h @@ -127,6 +127,8 @@ typedef struct /* value of the LuaRoot directive */ const char *root_path; + + apr_hash_t *authz_providers; } ap_lua_server_cfg; typedef struct