1
0
mirror of https://github.com/apache/httpd.git synced 2025-08-07 04:02:58 +03:00

Add new directive LuaAuthzProvider to allow implementing an

authorization provider in lua


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1351020 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Fritsch
2012-06-16 22:51:19 +00:00
parent 2385ef2c2f
commit 99d37b2e33
6 changed files with 236 additions and 3 deletions

View File

@@ -6,6 +6,9 @@ Changes with Apache 2.5.0
possible XSS for a site where untrusted users can upload files to possible XSS for a site where untrusted users can upload files to
a location with MultiViews enabled. [Niels Heinen <heinenn google.com>] 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]
*) mod_lua: Add a few missing request_rec fields. Rename remote_ip to *) mod_lua: Add a few missing request_rec fields. Rename remote_ip to
client_ip to match conn_rec. [Stefan Fritsch] client_ip to match conn_rec. [Stefan Fritsch]

View File

@@ -1 +1 @@
2313 2320

View File

@@ -130,6 +130,60 @@ handlers (or hooks, or filters) in the same script.
</section> </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> <section id="writinghooks"><title>Writing Hooks</title>
<p>Hook functions are how modules (and Lua scripts) participate in the <p>Hook functions are how modules (and Lua scripts) participate in the
@@ -795,4 +849,30 @@ hook function usually returns OK, DECLINED, or HTTP_FORBIDDEN.</p>
</usage> </usage>
</directivesynopsis> </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> </modulesynopsis>

View File

@@ -19,6 +19,7 @@
#include "apr_uuid.h" #include "apr_uuid.h"
#include "lua_config.h" #include "lua_config.h"
#include "apr_file_info.h" #include "apr_file_info.h"
#include "mod_auth.h"
APLOG_USE_MODULE(lua); 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_REVERSE);
makeintegerfield(L, PROXYREQ_RESPONSE); makeintegerfield(L, PROXYREQ_RESPONSE);
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); makeintegerfield(L, HTTP_CONTINUE);

View File

@@ -24,6 +24,7 @@
#include "lua_config.h" #include "lua_config.h"
#include "apr_optional.h" #include "apr_optional.h"
#include "mod_ssl.h" #include "mod_ssl.h"
#include "mod_auth.h"
#ifdef APR_HAS_THREADS #ifdef APR_HAS_THREADS
#include "apr_thread_proc.h" #include "apr_thread_proc.h"
@@ -44,6 +45,17 @@ static APR_OPTIONAL_FN_TYPE(ssl_is_https) *lua_ssl_is_https = NULL;
#define AP_LUA_HOOK_FIRST (APR_HOOK_FIRST - 1) #define AP_LUA_HOOK_FIRST (APR_HOOK_FIRST - 1)
#define AP_LUA_HOOK_LAST (APR_HOOK_LAST + 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. * error reporting if lua has an error.
* Extracts the error from lua stack and prints * 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 { else {
spec->file = r->filename; 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", "%s details: scope: %s, file: %s, func: %s",
what, scope_to_string(spec->scope), spec->file, what, scope_to_string(spec->scope), spec->file,
function ? function : "-"); 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[] = { command_rec lua_commands[] = {
AP_INIT_TAKE1("LuaRoot", register_lua_root, NULL, OR_ALL, 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, AP_INIT_TAKE1("LuaPackageCPath", register_package_cdir, NULL, OR_ALL,
"Add a directory to lua's package.cpath"), "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, AP_INIT_TAKE23("LuaHookTranslateName", register_translate_name_hook, NULL,
OR_ALL, 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_OPTIONAL_HOOK(ap_lua, lua_request, lua_request_hook, NULL, NULL,
APR_HOOK_REALLY_FIRST); APR_HOOK_REALLY_FIRST);
/* providers */
lua_authz_providers = apr_hash_make(p);
} }
AP_DECLARE_MODULE(lua) = { AP_DECLARE_MODULE(lua) = {

View File

@@ -127,6 +127,8 @@ typedef struct
/* value of the LuaRoot directive */ /* value of the LuaRoot directive */
const char *root_path; const char *root_path;
apr_hash_t *authz_providers;
} ap_lua_server_cfg; } ap_lua_server_cfg;
typedef struct typedef struct