diff --git a/apache2/Makefile b/apache2/Makefile index 85720226..c9dcd97e 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -39,7 +39,7 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 #INCLUDES = -I /usr/include/libxml2 -I /path/to/httpd-x.y/srclib/pcre -DEFS = -DWITH_LIBXML2 +DEFS = -DWITH_LIBXML2 -DWITH_LUA # DEFS = -DWITH_ICONV #DEFS = -DWITH_LIBXML2 -DPERFORMANCE_MEASUREMENT #DEFS = -DWITH_LIBXML2 -DNO_MODSEC_API diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index f3bcc277..cd5bed72 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -15,6 +15,8 @@ #include "pdf_protect.h" #include "http_log.h" +#include "msc_lua.h" + /* -- Directory context creation and initialisation -- */ /** @@ -527,10 +529,10 @@ void init_directory_config(directory_config *dcfg) { } /** - * TODO + * */ -static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char *p1, - const char *p2, const char *p3) +static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, + const char *p1, const char *p2, const char *p3) { char *my_error_msg = NULL; msre_rule *rule = NULL; @@ -543,7 +545,17 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * } /* Create the rule now. */ - rule = msre_rule_create(dcfg->ruleset, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg); + switch(type) { + case RULE_TYPE_LUA : + rule = msre_rule_lua_create(dcfg->ruleset, cmd->directive->filename, + cmd->directive->line_num, p1, p2, &my_error_msg); + break; + default : + rule = msre_rule_create(dcfg->ruleset, cmd->directive->filename, + cmd->directive->line_num, p1, p2, p3, &my_error_msg); + break; + } + if (rule == NULL) { return my_error_msg; } @@ -627,7 +639,7 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * } /* Optimisation */ - if (strcasecmp(rule->op_name, "inspectFile") == 0) { + if ((rule->op_name != NULL)&&(strcasecmp(rule->op_name, "inspectFile") == 0)) { dcfg->upload_validates_files = 1; } @@ -643,13 +655,16 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * apr_table_setn(dcfg->tmp_rule_placeholders, tmp_id, tmp_id); #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Watching for skipafter target rule id=\"%s\".", tmp_id); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Watching for skipafter target rule id=\"%s\".", tmp_id); #endif } #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Adding rule %pp id=\"%s\".", rule, (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id)); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Adding rule %pp id=\"%s\".", rule, (rule->actionset->id == NOT_SET_P + ? "(none)" : rule->actionset->id)); #endif /* Add rule to the recipe. */ @@ -662,7 +677,8 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule)); #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id); #endif /* shallow copy of original rule with placeholder marked as target */ @@ -723,7 +739,7 @@ static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, const char /* -- Configuration directives -- */ static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) { - return add_rule(cmd, (directory_config *)_dcfg, SECACTION_TARGETS, SECACTION_ARGS, p1); + return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, SECACTION_TARGETS, SECACTION_ARGS, p1); } static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) { @@ -1213,7 +1229,7 @@ static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, void *_dcf static const char *cmd_rule(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2, const char *p3) { - return add_rule(cmd, (directory_config *)_dcfg, p1, p2, p3); + return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, p1, p2, p3); } static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { @@ -1266,6 +1282,14 @@ 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) +{ + return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, p1, 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; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); @@ -1861,6 +1885,16 @@ const command_rec module_directives[] = { "On or Off" ), + #ifdef WITH_LUA + AP_INIT_TAKE12 ( + "SecRuleScript", + cmd_rule_script, + NULL, + CMD_SCOPE_ANY, + "" // TODO + ), + #endif + AP_INIT_ITERATE ( "SecRuleRemoveById", cmd_rule_remove_by_id, diff --git a/apache2/modules.mk b/apache2/modules.mk index 389cc5a5..d201d566 100644 --- a/apache2/modules.mk +++ b/apache2/modules.mk @@ -1,11 +1,11 @@ MOD_SECURITY2 = mod_security2 apache2_config apache2_io apache2_util \ re re_operators re_actions re_tfns re_variables \ msc_logging msc_xml msc_multipart modsecurity msc_parsers msc_util msc_pcre \ - persist_dbm msc_reqbody pdf_protect msc_geo acmp + persist_dbm msc_reqbody pdf_protect msc_geo acmp msc_lua H = re.h modsecurity.h msc_logging.h msc_multipart.h msc_parsers.h \ msc_pcre.h msc_util.h msc_xml.h persist_dbm.h apache2.h pdf_protect.h \ - msc_geo.h acmp.h utf8tables.h + msc_geo.h acmp.h utf8tables.h msc_lua.h ${MOD_SECURITY2:=.slo}: ${H} ${MOD_SECURITY2:=.lo}: ${H} diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c new file mode 100644 index 00000000..6ffa9a96 --- /dev/null +++ b/apache2/msc_lua.c @@ -0,0 +1,107 @@ + +#include "msc_lua.h" + +#ifdef WITH_LUA + +#include "apr_strings.h" + +typedef struct { + apr_array_header_t *parts; + apr_pool_t *pool; +} msc_lua_dumpw_t; + +typedef struct { + msc_script *script; + int index; +} msc_lua_dumpr_t; + +/** + * + */ +static const char* dump_reader(lua_State* L, void* user_data, size_t* size) { + msc_lua_dumpr_t *dumpr = (msc_lua_dumpr_t *)user_data; + + /* Do we have more chunks to return? */ + if (dumpr->index == dumpr->script->parts->nelts) { + return NULL; + } + + /* Get one chunk. */ + msc_script_part *part = ((msc_script_part **)dumpr->script->parts->elts)[dumpr->index]; + *size = part->len; + + dumpr->index++; + + return part->data; +} + +/** + * + */ +static int dump_writer(lua_State *L, const void* data, size_t len, void* user_data) { + msc_lua_dumpw_t *dump = (msc_lua_dumpw_t *)user_data; + + /* Allocate new part, copy the data into it. */ + msc_script_part *part = apr_palloc(dump->pool, sizeof(msc_script_part)); + part->data = apr_palloc(dump->pool, len); + part->len = len; + memcpy((void *)part->data, data, len); + + /* Then add it to the list of parsts. */ + *(const msc_script_part **)apr_array_push(dump->parts) = part; + + return 0; +} + +/** + * + */ +int lua_restore(lua_State *L, msc_script *script) { + msc_lua_dumpr_t dumpr; + + dumpr.script = script; + dumpr.index = 0; + + return lua_load(L, dump_reader, &dumpr, script->name); +} + +/** + * + */ +char *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool) { + lua_State *L = NULL; + msc_lua_dumpw_t dump; + + /* Initialise state. */ + L = lua_open(); + luaL_openlibs(L); + + /* Find script. */ + if (luaL_loadfile(L, filename)) { + return apr_psprintf(pool, "ModSecurity: Failed to open script %s: %s", + filename, lua_tostring(L, -1)); + } + + /* Compile script. */ + lua_pcall(L, 0, 0, 0); + + /* Find the execution entry point. */ + lua_getglobal(L, "main"); + + /* Dump the script into binary form. */ + dump.pool = pool; + dump.parts = apr_array_make(pool, 128, sizeof(msc_script_part *)); + + lua_dump(L, dump_writer, &dump); + + (*script) = apr_pcalloc(pool, sizeof(msc_script)); + (*script)->name = filename; + (*script)->parts = dump.parts; + + /* Destroy state. */ + lua_close(L); + + return NULL; +} + +#endif diff --git a/apache2/msc_lua.h b/apache2/msc_lua.h new file mode 100644 index 00000000..d697f385 --- /dev/null +++ b/apache2/msc_lua.h @@ -0,0 +1,42 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) + * + * You should have received a copy of the licence along with this + * program (stored in the file "LICENSE"). If the file is missing, + * or if you have any other questions related to the licence, please + * write to Breach Security, Inc. at support@breach.com. + * + */ +#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; + +#include +#include +#include + +#include "apr_general.h" +#include "apr_tables.h" +#include "modsecurity.h" + +struct msc_script { + const char *name; + apr_array_header_t *parts; +}; + +struct msc_script_part { + const void *data; + size_t len; +}; + +char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool); + +int DSOLOCAL lua_restore(lua_State *L, msc_script *script); + +#endif +#endif diff --git a/apache2/re.c b/apache2/re.c index 6230167e..95d5ec67 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -12,6 +12,8 @@ #include "re.h" +#include "msc_lua.h" + /* -- Actions, variables, functions and operator functions ----------------- */ @@ -1299,6 +1301,51 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, return rule; } +#ifdef WITH_LUA +/** + * + */ +msre_rule *msre_rule_lua_create(msre_ruleset *ruleset, + const char *fn, int line, const char *script_filename, + const char *actions, char **error_msg) +{ + msre_rule *rule; + char *my_error_msg; + + if (error_msg == NULL) return NULL; + *error_msg = NULL; + + rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule)); + if (rule == NULL) return NULL; + rule->ruleset = ruleset; + rule->filename = apr_pstrdup(ruleset->mp, fn); + rule->line_num = line; + + rule->type = RULE_TYPE_LUA; + + rule->unparsed = apr_pstrcat(ruleset->mp, "SecRuleScript ", script_filename, + (actions != NULL ? " ACTIONS" : ""), NULL); + + /* Compile script. */ + *error_msg = lua_compile(&rule->script, script_filename, ruleset->mp); + if (*error_msg != NULL) { + return NULL; + } + + /* Parse actions */ + if (actions != NULL) { + /* Create per-rule actionset */ + rule->actionset = msre_actionset_create(ruleset->engine, actions, &my_error_msg); + if (rule->actionset == NULL) { + *error_msg = apr_psprintf(ruleset->mp, "Error parsing actions: %s", my_error_msg); + return NULL; + } + } + + return rule; +} +#endif + /** * Perform non-disruptive actions associated with the provided actionset. */ @@ -1480,7 +1527,7 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, /** * Executes rule against the given transaction. */ -apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { +static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; msre_actionset *acting_actionset = NULL; @@ -1900,6 +1947,76 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { return (match_count ? RULE_MATCH : RULE_NO_MATCH); } +/** + * + */ +#if defined(WITH_LUA) +static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) { + apr_time_t time_before; + lua_State *L = NULL; + int rc; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Lua: Executing script: %s", rule->script->name); + } + + time_before = apr_time_now(); + + /* Create new state. */ + L = lua_open(); + luaL_openlibs(L); + + /* Associate msr with the state. */ + lua_pushlightuserdata(L, (void *)msr); + lua_setglobal(L, "__msr"); + + rc = lua_restore(L, rule->script); + if (rc) { + msr_log(msr, 1, "Lua: Failed to restore script with %i.", rc); + return -1; + } + + /* Execute script. */ + if (lua_pcall(L, 0, 1, 0)) { + msr_log(msr, 1, "Lua: Script execution failed: %s", lua_tostring(L, -1)); + return -1; + } + + /* Obtain the result code. */ + if (!lua_isnumber(L, -1)) { + msr_log(msr, 1, "Lua: Script failed to return a number value."); + return -1; + } + + rc = lua_tonumber(L, -1); + + /* Destroy state. */ + lua_pop(L, 1); + lua_close(L); + + /* Returns status code to caller. */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Lua: Script completed in %" APR_TIME_T_FMT " usec, returning %i.", + (apr_time_now() - time_before), rc); + } + + return (rc ? RULE_MATCH : RULE_NO_MATCH); +} +#endif + +/** + * + */ +apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { + #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); +} + /** * Checks whether the given rule ID is in the given range. */ diff --git a/apache2/re.h b/apache2/re.h index ab314329..17012b60 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -35,6 +35,8 @@ typedef struct msre_cache_rec msre_cache_rec; #include "persist_dbm.h" #include "apache2.h" +#include "msc_lua.h" + /* Actions, variables, functions and operator functions */ int DSOLOCAL expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp); @@ -113,6 +115,10 @@ int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset #define RULE_PH_SKIPAFTER 1 /* Implicit placeholder for skipAfter */ #define RULE_PH_MARKER 2 /* Explicit placeholder for SecMarker */ +#define RULE_TYPE_NORMAL 0 +#define RULE_TYPE_ACTION 1 +#define RULE_TYPE_LUA 2 + struct msre_rule { apr_array_header_t *targets; const char *op_name; @@ -126,18 +132,27 @@ struct msre_rule { const char *filename; int line_num; int placeholder; + int type; msre_ruleset *ruleset; msre_rule *chain_starter; -#if defined(PERFORMANCE_MEASUREMENT) + #if defined(PERFORMANCE_MEASUREMENT) unsigned int execution_time; -#endif + #endif + + #if defined(WITH_LUA) + msc_script *script; + #endif }; msre_rule DSOLOCAL *msre_rule_create(msre_ruleset *ruleset, const char *fn, int line, const char *targets, const char *args, const char *actions, char **error_msg); +msre_rule DSOLOCAL *msre_rule_lua_create(msre_ruleset *ruleset, + const char *fn, int line, const char *script_filename, + const char *actions, char **error_msg); + void DSOLOCAL msre_rule_actionset_init(msre_rule *rule); apr_status_t DSOLOCAL msre_rule_process(msre_rule *rule, modsec_rec *msr);