diff --git a/CHANGES b/CHANGES index 7a737747..ae44ebd4 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Exported API for registering custom variables. Example in api directory. + * Added experimental variables RESPONSE_CONTENT_LENGTH, RESPONSE_CONTENT_TYPE, and RESPONSE_CONTENT_ENCODING. diff --git a/apache2/apache2.h b/apache2/apache2.h index d20fe0d8..117e1b79 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -24,6 +24,11 @@ APR_DECLARE_OPTIONAL_FN(void, modsec_register_tfn, (const char *name, void *fn)); APR_DECLARE_OPTIONAL_FN(void, modsec_register_operator, (const char *name, void *fn_init, void *fn_exec)); +APR_DECLARE_OPTIONAL_FN(void, modsec_register_variable, + (const char *name, unsigned int type, + unsigned int argc_min, unsigned int argc_max, + void *fn_validate, void *fn_generate, + unsigned int is_cacheable, unsigned int availability)); /* Configuration functions. */ diff --git a/apache2/api/README b/apache2/api/README index e5ca7006..fcf0bc82 100644 --- a/apache2/api/README +++ b/apache2/api/README @@ -24,3 +24,13 @@ access to ModSecurity structures. For example: apxs -I -I/usr/include/libxml2 -cia mod_op_strstr.c +3) + +Module mod_var_remote_addr_port.c creates a custom variable "REMOTE_ADDR_PORT" +that combines the REMOTE_ADDR and REMOTE_PORT into a.b.c.d:port format. + +Compiling this module is more involved because it requires +access to ModSecurity structures. For example: + + apxs -I -cia mod_var_remote_addr_port.c + diff --git a/apache2/api/mod_var_remote_addr_port.c b/apache2/api/mod_var_remote_addr_port.c new file mode 100644 index 00000000..b6d1e5e6 --- /dev/null +++ b/apache2/api/mod_var_remote_addr_port.c @@ -0,0 +1,134 @@ + +#include "httpd.h" +#include "http_core.h" +#include "http_config.h" +#include "http_log.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "apr_optional.h" + +#include "re.h" + +/* -- Generic generators/validators from re_variables.c -- */ + +/** + * Generates a variable from a string and a length. + */ +static int var_simple_generate_ex(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, + const char *value, int value_len) +{ + msre_var *rvar = NULL; + + if (value == NULL) return 0; + + rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + rvar->value = value; + rvar->value_len = value_len; + apr_table_addn(vartab, rvar->name, (void *)rvar); + + return 1; +} + +/** + * Generates a variable from a NULL-terminated string. + */ +static int var_simple_generate(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, + const char *value) +{ + if (value == NULL) return 0; + return var_simple_generate_ex(var, vartab, mptmp, value, strlen(value)); +} + +/** + * Validate that a target parameter is valid. We only need to take + * care of the case when the parameter is a regular expression. + */ +static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { + /* It's OK if there's no parameter. */ + if (var->param == NULL) return NULL; + + /* Is it a regular expression? */ + if ((strlen(var->param) > 2)&&(var->param[0] == '/') + &&(var->param[strlen(var->param) - 1] == '/')) + { /* Regex. */ + msc_regex_t *regex = NULL; + const char *errptr = NULL; + const char *pattern = NULL; + int erroffset; + + pattern = apr_pstrmemdup(ruleset->mp, var->param + 1, strlen(var->param + 1) - 1); + if (pattern == NULL) return FATAL_ERROR; + + regex = msc_pregcomp(ruleset->mp, pattern, PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); + if (regex == NULL) { + return apr_psprintf(ruleset->mp, "Error compiling pattern (pos %i): %s", + erroffset, errptr); + } + + /* Store the compiled regex for later. */ + var->param_data = regex; + } + + /* Simple string */ + return NULL; +} + + +/* -- Module specific code -- */ + +/** + * Create a silly variable with value = a.b.c.d:port + */ +static int var_remote_addr_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + const char *value = apr_psprintf(mptmp, "%s:%d", msr->remote_addr, msr->remote_port); + msre_var *rvar = NULL; + + return var_simple_generate(var, vartab, mptmp, value); +} + +static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) { + void (*register_fn)(const char *name, unsigned int type, + unsigned int argc_min, unsigned int argc_max, + void *fn_validate, void *fn_generate, + unsigned int is_cacheable, unsigned int availability); + + /* Look for the registration function + * exported by ModSecurity. + */ + register_fn = APR_RETRIEVE_OPTIONAL_FN(modsec_register_variable); + if (register_fn) { + /* Use it to register our new + * variable under the + * name "REMOTE_ADDR_PORT". + */ + register_fn( + "REMOTE_ADDR_PORT", + VAR_SIMPLE, + 0, 0, + NULL, + var_remote_addr_port_generate, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + } + + return OK; +} + +static void register_hooks(apr_pool_t *p) { + ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_LAST); +} + +/* Dispatch list for API hooks */ +module AP_MODULE_DECLARE_DATA var_remote_addr_port_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + register_hooks /* register hooks */ +}; + diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index b28422db..03dcbdfe 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -922,6 +922,22 @@ static void modsec_register_operator(const char *name, void *fn_init, void *fn_e } } +/** + * This function is exported for other Apache modules to + * register new variables. + */ +static void modsec_register_variable(const char *name, unsigned int type, + unsigned int argc_min, unsigned int argc_max, + void *fn_validate, void *fn_generate, + unsigned int is_cacheable, unsigned int availability) { + if (modsecurity != NULL) { + msre_engine_variable_register(modsecurity->msre, name, type, argc_min, argc_max, fn_validate, fn_generate, is_cacheable, availability); + } + else { + fprintf(stderr,"modsecurity is NULL\n"); + } +} + /** * Registers module hooks with Apache. */ @@ -948,6 +964,7 @@ static void register_hooks(apr_pool_t *mp) { /* Export optional functions. */ APR_REGISTER_OPTIONAL_FN(modsec_register_tfn); APR_REGISTER_OPTIONAL_FN(modsec_register_operator); + APR_REGISTER_OPTIONAL_FN(modsec_register_variable); /* Main hooks */ ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST); diff --git a/apache2/re.h b/apache2/re.h index f677eceb..785f1ccc 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -243,6 +243,11 @@ struct msre_actionset { int auditlog; }; +void msre_engine_variable_register(msre_engine *engine, const char *name, + unsigned int type, unsigned int argc_min, unsigned int argc_max, + FN_VAR_VALIDATE(validate), FN_VAR_GENERATE(generate), + unsigned int is_cacheable, unsigned int availability); + msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, char **error_msg); diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 58792648..ff54a850 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -1683,7 +1683,7 @@ static int var_webappid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule /** * */ -static void msre_engine_variable_register(msre_engine *engine, const char *name, +void msre_engine_variable_register(msre_engine *engine, const char *name, unsigned int type, unsigned int argc_min, unsigned int argc_max, FN_VAR_VALIDATE(validate), FN_VAR_GENERATE(generate), unsigned int is_cacheable, unsigned int availability)