diff --git a/CHANGES b/CHANGES index c17b81e9..4d9147a0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,14 @@ -20 Dec 2007 - 2.5.0-rc1 +21 Dec 2007 - 2.5.0-rc1 ----------------------- Changes since 2.5.0-dev2: + * Added support for Lua scripting in the following ways: SecRuleScript + can be used to specify a script to execute as a rule, the exec + action processes Lua scripts internally, and does the @inspectFile + operator. Refer to the documentation for more details. + * Updated included Core Ruleset to version 1.5.1. * Changed how allow works. Used on its own it now allows phases 1-4. Used diff --git a/apache2/Makefile b/apache2/Makefile index 30380d8e..dd9832c2 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -39,8 +39,6 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 -I /usr/include/lua5.1 # INCLUDES = -I /usr/include/libxml2 -I /path/to/httpd-x.y/srclib/pcre -DEFS = -DWITH_LUA -# DEFS = -DWITH_ICONV # DEFS = -DPERFORMANCE_MEASUREMENT # DEFS = -DNO_MODSEC_API # DEFS = -DDEBUG_CONF diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index bbdeb369..1a0904fa 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1283,14 +1283,12 @@ static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) { return NULL; } -#ifdef WITH_LUA static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL); } -#endif static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -1887,7 +1885,6 @@ const command_rec module_directives[] = { "On or Off" ), - #ifdef WITH_LUA AP_INIT_TAKE12 ( "SecRuleScript", cmd_rule_script, @@ -1895,7 +1892,6 @@ const command_rec module_directives[] = { CMD_SCOPE_ANY, "" // TODO ), - #endif AP_INIT_ITERATE ( "SecRuleRemoveById", diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c index 67fcb992..98b5ab9b 100644 --- a/apache2/msc_lua.c +++ b/apache2/msc_lua.c @@ -1,9 +1,6 @@ #include "msc_lua.h" - -#ifdef WITH_LUA - #include "apr_strings.h" typedef struct { @@ -123,18 +120,60 @@ static int l_log(lua_State *L) { return 0; } +/** + * + */ +static apr_array_header_t *resolve_tfns(lua_State *L, int idx, modsec_rec *msr, apr_pool_t *mp) { + apr_array_header_t *tfn_arr = NULL; + msre_tfn_metadata *tfn = NULL; + char *name = NULL; + + tfn_arr = apr_array_make(mp, 25, sizeof(msre_tfn_metadata *)); + if (tfn_arr == NULL) return NULL; + + if (lua_istable(L, idx)) { /* Is the second parameter an array? */ + int i, n = lua_objlen(L, idx); + + for(i = 1; i <= n; i++) { + lua_rawgeti(L, idx, i); + name = (char *)luaL_checkstring(L, -1); + tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); + if (tfn == NULL) { + msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name); + } else { + *(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn; + } + } + } else if (lua_isstring(L, idx)) { /* The second parameter may be a simple string? */ + name = (char *)luaL_checkstring(L, idx); + tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); + if (tfn == NULL) { + msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name); + } else { + *(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn; + } + } else { + // TODO Error + return NULL; + } + + return tfn_arr; +} + /** * */ static int l_getvar(lua_State *L) { - char *varname = NULL; - char *param = NULL; + char *varname = NULL, *param = NULL; modsec_rec *msr = NULL; msre_rule *rule = NULL; char *my_error_msg = NULL; + char *p1 = NULL; + apr_array_header_t *tfn_arr = NULL; + msre_var *vx = NULL; /* Retrieve parameters. */ - varname = (char *)luaL_checkstring(L, 1); + p1 = (char *)luaL_checkstring(L, 1); /* Retrieve msr. */ lua_getglobal(L, "__msr"); @@ -144,100 +183,123 @@ static int l_getvar(lua_State *L) { lua_getglobal(L, "__rule"); rule = (msre_rule *)lua_topointer(L, -1); - /* Resolve variable $varname. */ + /* Extract the variable name and its parameter from the script. */ + varname = apr_pstrdup(msr->msc_rule_mptmp, p1); param = strchr(varname, '.'); if (param != NULL) { *param = '\0'; param++; } + /* Resolve variable. */ msre_var *var = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre, varname, param, msr, &my_error_msg); if (var == NULL) { - msr_log(msr, 1, "SecRuleScript: Failed to resolve variable: %s", varname); + msr_log(msr, 1, "%s", my_error_msg); + + lua_pushnil(L); + return 0; - } else { - msre_var *vx = NULL; - - vx = generate_single_var(msr, var, rule, msr->msc_rule_mptmp); - if (vx != NULL) { - char *rval = NULL; - long int rval_len = -1; - - /* Transform the variable if a list of transformation - * functions has been supplied. - */ - if (lua_istable(L, 2)) { /* Is the second parameter an array? */ - int i, n = lua_objlen(L, 2); - - /* Make a copy so that we don't ruin the original value. */ - vx->value = apr_pstrmemdup(msr->msc_rule_mptmp, vx->value, vx->value_len); - - for(i = 1; i <= n; i++) { - msre_tfn_metadata *tfn = NULL; - char *name = NULL; - int rc = 0; - - lua_rawgeti(L, 2, i); - name = (char *)luaL_checkstring(L, -1); - tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); - if (tfn == NULL) { - msr_log(msr, 1, "SecRuleScript: Invalid transformation function in getvar() call: %s", name); - return 0; - } - - rc = tfn->execute(msr->msc_rule_mptmp, (unsigned char*)vx->value, - vx->value_len, &rval, &rval_len); - - vx->value = rval; - vx->value_len = rval_len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, - log_escape_nq_ex(msr->msc_rule_mptmp, vx->value, vx->value_len)); - } - } - } - else if (lua_isstring(L, 2)) { /* The second parameter may be a simple string? */ - msre_tfn_metadata *tfn = NULL; - char *name = NULL; - int rc = 0; - - /* Make a copy so that we don't ruin the original value. */ - vx->value = apr_pstrmemdup(msr->msc_rule_mptmp, vx->value, vx->value_len); - - name = (char *)luaL_checkstring(L, 2); - tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name); - if (tfn == NULL) { - msr_log(msr, 1, "SecRuleScript: Invalid transformation function in getvar() call: %s", name); - return 0; - } - - rc = tfn->execute(msr->msc_rule_mptmp, (unsigned char *)vx->value, - vx->value_len, &rval, &rval_len); - - vx->value = rval; - vx->value_len = rval_len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, - log_escape_nq_ex(msr->msc_rule_mptmp, vx->value, vx->value_len)); - } - } - - lua_pushlstring(L, vx->value, vx->value_len); - - return 1; - } } - return 0; + /* Resolve transformation functions. */ + tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp); + + /* Generate variable. */ + vx = generate_single_var(msr, var, tfn_arr, rule, msr->msc_rule_mptmp); + if (vx == NULL) { + lua_pushnil(L); + + return 0; + } + + /* Return variable value. */ + lua_pushlstring(L, vx->value, vx->value_len); + + return 1; +} + +/** + * + */ +static int l_getvars(lua_State *L) { + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartable = NULL; + apr_array_header_t *tfn_arr = NULL; + char *varname = NULL, *param = NULL; + modsec_rec *msr = NULL; + msre_rule *rule = NULL; + msre_var *vartemplate = NULL; + char *my_error_msg = NULL; + char *p1 = NULL; + int i; + + /* Retrieve parameters. */ + p1 = (char *)luaL_checkstring(L, 1); + + /* Retrieve msr. */ + lua_getglobal(L, "__msr"); + msr = (modsec_rec *)lua_topointer(L, -1); + + /* Retrieve rule. */ + lua_getglobal(L, "__rule"); + rule = (msre_rule *)lua_topointer(L, -1); + + /* Extract the variable name and its parameter from the script. */ + varname = apr_pstrdup(msr->msc_rule_mptmp, p1); + param = strchr(varname, '.'); + if (param != NULL) { + *param = '\0'; + param++; + } + + /* Resolve transformation functions. */ + tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp); + + lua_newtable(L); + + /* Resolve variable. */ + vartemplate = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre, + varname, param, msr, &my_error_msg); + + if (vartemplate == NULL) { + msr_log(msr, 1, "%s", my_error_msg); + + /* Returning empty table. */ + return 1; + } + + vartable = generate_multi_var(msr, vartemplate, tfn_arr, rule, msr->msc_rule_mptmp); + + tarr = apr_table_elts(vartable); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_var *var = (msre_var *)telts[i].val; + + lua_pushnumber(L, i + 1); /* Index is not zero-based. */ + + lua_newtable(L); /* Per-parameter table. */ + + lua_pushstring(L, "name"); + lua_pushlstring(L, var->name, strlen(var->name)); + lua_settable(L, -3); + + lua_pushstring(L, "value"); + lua_pushlstring(L, var->value, var->value_len); + lua_settable(L, -3); + + lua_settable(L, -3); /* Push one parameter into the results table. */ + } + + return 1; } static const struct luaL_Reg mylib[] = { { "log", l_log }, { "getvar", l_getvar }, + { "getvars", l_getvars }, { NULL, NULL } }; @@ -317,4 +379,3 @@ int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rul return ((*error_msg != NULL) ? RULE_MATCH : RULE_NO_MATCH); } -#endif diff --git a/apache2/msc_lua.h b/apache2/msc_lua.h index b0d28891..a2684dda 100644 --- a/apache2/msc_lua.h +++ b/apache2/msc_lua.h @@ -11,8 +11,6 @@ #ifndef _MSC_LUA_H_ #define _MSC_LUA_H_ -#if defined(WITH_LUA) - typedef struct msc_script msc_script; typedef struct msc_script_part msc_script_part; @@ -39,4 +37,3 @@ char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t int DSOLOCAL lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg); #endif -#endif diff --git a/apache2/re.c b/apache2/re.c index 4418d6f3..586d0cfe 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1314,7 +1314,6 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, return rule; } -#ifdef WITH_LUA /** * */ @@ -1362,7 +1361,6 @@ msre_rule *msre_rule_lua_create(msre_ruleset *ruleset, return rule; } -#endif /** * Perform non-disruptive actions associated with the provided actionset. @@ -2001,11 +1999,9 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { apr_pool_clear(msr->msc_rule_mptmp); } - #if defined(WITH_LUA) if (rule->type == RULE_TYPE_LUA) { return msre_rule_process_lua(rule, msr); } - #endif return msre_rule_process_normal(rule, msr); } diff --git a/apache2/re.h b/apache2/re.h index d0c6bda0..80edc252 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -65,9 +65,11 @@ int DSOLOCAL msre_parse_generic(apr_pool_t *pool, const char *text, apr_table_t int DSOLOCAL rule_id_in_range(int ruleid, const char *range); -msre_var DSOLOCAL *generate_single_var(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_pool_t *mptmp); +msre_var DSOLOCAL *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp); +apr_table_t DSOLOCAL *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp); /* Structures with the corresponding functions */ @@ -143,9 +145,8 @@ struct msre_rule { unsigned int execution_time; #endif - #if defined(WITH_LUA) + /* Compiled Lua script. */ msc_script *script; - #endif }; msre_rule DSOLOCAL *msre_rule_create(msre_ruleset *ruleset, diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 29d08779..b826c642 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -39,21 +39,118 @@ static void msre_engine_action_register(msre_engine *engine, const char *name, u /** * Generates a single variable (from the supplied metadata). */ -msre_var *generate_single_var(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_pool_t *mptmp) +msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp) { apr_table_t *vartab = NULL; const apr_table_entry_t *te = NULL; const apr_array_header_t *arr = NULL; + msre_var *rvar = NULL; + int i; + + /* Sanity check. */ + if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; - if (var->metadata->generate == NULL) return NULL; vartab = apr_table_make(mptmp, 16); var->metadata->generate(msr, var, rule, vartab, mptmp); + arr = apr_table_elts(vartab); if (arr->nelts == 0) return NULL; - te = (apr_table_entry_t *)arr->elts; + te = (apr_table_entry_t *)arr->elts; - return (msre_var *)te[0].val; + rvar = (msre_var *)te[0].val; + + /* Return straight away if there were no + * transformation functions supplied. + */ + if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { + return rvar; + } + + /* Copy the value so that we can transform it in piece. */ + rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); + + /* Transform rvar in a loop. */ + for (i = 0; i < tfn_arr->nelts; i++) { + msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; + char *rval; + int rc; + long int rval_len; + + rc = tfn->execute(mptmp, (unsigned char *)rvar->value, + rvar->value_len, &rval, &rval_len); + + rvar->value = rval; + rvar->value_len = rval_len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, + log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); + } + } + + return rvar; +} + +/** + * + */ +apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, + msre_rule *rule, apr_pool_t *mptmp) +{ + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + apr_table_t *vartab = NULL, *tvartab = NULL; + msre_var *rvar = NULL; + int i, j; + + /* Sanity check. */ + if ((var == NULL)||(var->metadata == NULL)||(var->metadata->generate == NULL)) return NULL; + + /* Generate variables. */ + vartab = apr_table_make(mptmp, 16); + var->metadata->generate(msr, var, rule, vartab, mptmp); + + /* Return straight away if there were no + * transformation functions supplied. + */ + if ((tfn_arr == NULL)||(tfn_arr->nelts == 0)) { + return vartab; + } + + tvartab = apr_table_make(mptmp, 16); + + tarr = apr_table_elts(vartab); + telts = (const apr_table_entry_t*)tarr->elts; + for (j = 0; j < tarr->nelts; j++) { + rvar = (msre_var *)telts[j].val; + + /* Copy the value so that we can transform it in piece. */ + rvar->value = apr_pstrndup(mptmp, rvar->value, rvar->value_len); + + /* Transform rvar in a loop. */ + for (i = 0; i < tfn_arr->nelts; i++) { + msre_tfn_metadata *tfn = ((msre_tfn_metadata **)tfn_arr->elts)[i]; + char *rval; + int rc; + long int rval_len; + + rc = tfn->execute(mptmp, (unsigned char *)rvar->value, + rvar->value_len, &rval, &rval_len); + + rvar->value = rval; + rvar->value_len = rval_len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, + log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); + } + } + + apr_table_addn(tvartab, rvar->name, (void *)rvar); + } + + return tvartab; } /** @@ -137,7 +234,7 @@ int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t var_resolved = msre_create_var_ex(mptmp, msr->modsecurity->msre, var_name, var_value, msr, &my_error_msg); if (var_resolved != NULL) { - var_generated = generate_single_var(msr, var_resolved, rule, mptmp); + var_generated = generate_single_var(msr, var_resolved, NULL, rule, mptmp); if (var_generated != NULL) { part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); if (part == NULL) return -1; @@ -1457,7 +1554,6 @@ static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) /* TODO Support relative filenames. */ - #ifdef WITH_LUA /* Process Lua scripts internally. */ if (strlen(filename) > 4) { char *p = filename + strlen(filename) - 4; @@ -1472,7 +1568,6 @@ static char *msre_action_exec_validate(msre_engine *engine, msre_action *action) action->param_data = script; } } - #endif return NULL; } diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 893b05ce..bee3a803 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@