From d170dd4ebe25297eda1f55424dc0fe9aff9c2080 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Thu, 24 Feb 2011 15:03:50 +0000 Subject: [PATCH] MODSEC-57 --- apache2/apache2_config.c | 228 ++++++++++++++++++++++++++++++++++----- apache2/re.c | 61 +++++++---- apache2/re.h | 9 +- apache2/re_actions.c | 59 +++++++--- 4 files changed, 294 insertions(+), 63 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 0491e331..8c0c2013 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -121,7 +121,6 @@ void *create_directory_config(apr_pool_t *mp, char *path) dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *)); dcfg->request_encoding = NOT_SET_P; - dcfg->disable_backend_compression = NOT_SET; return dcfg; @@ -477,7 +476,7 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) merged->request_encoding = (child->request_encoding == NOT_SET_P ? parent->request_encoding : child->request_encoding); - + merged->disable_backend_compression = (child->disable_backend_compression == NOT_SET ? parent->disable_backend_compression : child->disable_backend_compression); @@ -905,6 +904,156 @@ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, return NULL; } +char *update_rule_target(cmd_parms *cmd, directory_config *dcfg, + msre_ruleset *rset, const char *p1, const char *p2, const char *p3) +{ + msre_var **targets = NULL; + msre_rule *rule = NULL; + msre_ruleset *ruleset = NULL; + const char *curr_targets = NULL; + char *my_error_msg = NULL; + char *p = NULL; + char *savedptr = NULL; + char *target_list = NULL; + char *replace = NULL; + int is_negated = 0; + int is_counting = 0; + int name_len = 0; + int value_len = 0; + char *name = NULL, *value = NULL; + char *opt = NULL; + char *param = NULL; + int i, rc, match = 0; + + if(p1 == NULL || p2 == NULL || (dcfg == NULL && rset == NULL)) { + return NULL; + } + + if(dcfg != NULL) + ruleset = dcfg->ruleset; + else if (rset != NULL) + ruleset = rset; + + /* Get the ruleset if one exists */ + if ((ruleset == NULL)||(ruleset == NOT_SET_P)) { + return NULL; + } + + rule = msre_ruleset_fetch_rule(ruleset, p1); + if (rule == NULL) { + if (cmd != NULL) { + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, + "Update rule id=\"%s\" with targets \"%s\" failed: Rule not found.", p1, p2); + } + return NULL; + } + + target_list = strdup(p2); + if(target_list == NULL) + return NULL; + + if(p3 != NULL) { + replace = strdup(p3); + if(replace == NULL) { + free(target_list); + return NULL; + } + } + + if(replace != NULL) { + + opt = strchr(replace,'!'); + + if(opt != NULL) { + *opt = '\0'; + opt++; + param = opt; + is_negated = 1; + } else if ((opt = strchr(replace,'&')) != NULL) { + *opt = '\0'; + opt++; + param = opt; + is_counting = 1; + } else { + param = replace; + } + + opt = strchr(param,':'); + + if(opt != NULL) { + name = apr_strtok(param,":",&value); + if(strchr(value,':') != NULL) { + goto end; + } + } else { + name = param; + } + + name_len = strlen(name); + + if(value != NULL) + value_len = strlen(value); + + targets = (msre_var **)rule->targets->elts; + // TODO need a good way to remove the element from array, maybe change array by tables or rings + for (i = 0; i < rule->targets->nelts; i++) { + if((strncasecmp(targets[i]->name,name,name_len) == 0) && + (targets[i]->is_negated == is_negated) && + (targets[i]->is_counting == is_counting)) { + + if(value != NULL && targets[i]->param != NULL) { + if(strncasecmp(targets[i]->param,value,value_len) == 0) { + memset(targets[i]->name,0,strlen(targets[i]->name)); + memset(targets[i]->param,0,strlen(targets[i]->param)); + match = 1; + } + } else if (value == NULL && targets[i]->param == NULL){ + memset(targets[i]->name,0,strlen(targets[i]->name)); + match = 1; + } else + continue; + + } + } + } + + p = apr_strtok(target_list, ",", &savedptr); + + while(p != NULL) { + + if(replace != NULL) { + if(match == 1) { + rc = msre_parse_targets(ruleset, p, rule->targets, &my_error_msg); + if (rc < 0) { + goto end; + } + } else { + goto end; + } + } else { + + rc = msre_parse_targets(ruleset, p, rule->targets, &my_error_msg); + if (rc < 0) { + goto end; + } + + } + + p = apr_strtok(NULL,",",&savedptr); + } + + curr_targets = msre_generate_target_string(ruleset->mp, rule); + + rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, curr_targets, NULL, NULL); + +end: + if(target_list != NULL) + free(target_list); + if(replace != NULL) + free(replace); + return NULL; +} + /* -- Configuration directives -- */ static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) @@ -920,7 +1069,7 @@ static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) } static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -939,12 +1088,12 @@ static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) if (strcasecmp(p1, "On") == 0) dcfg->auditlog_flag = AUDITLOG_ON; else - if (strcasecmp(p1, "Off") == 0) dcfg->auditlog_flag = AUDITLOG_OFF; - else - if (strcasecmp(p1, "RelevantOnly") == 0) dcfg->auditlog_flag = AUDITLOG_RELEVANT; - else - return (const char *)apr_psprintf(cmd->pool, - "ModSecurity: Unrecognised parameter value for SecAuditEngine: %s", p1); + if (strcasecmp(p1, "Off") == 0) dcfg->auditlog_flag = AUDITLOG_OFF; + else + if (strcasecmp(p1, "RelevantOnly") == 0) dcfg->auditlog_flag = AUDITLOG_RELEVANT; + else + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecAuditEngine: %s", p1); return NULL; } @@ -962,7 +1111,7 @@ static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) pipe_log = ap_open_piped_log(cmd->pool, pipe_name); if (pipe_log == NULL) { return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log pipe: %s", - pipe_name); + pipe_name); } dcfg->auditlog_fd = ap_piped_log_write_fd(pipe_log); } @@ -971,12 +1120,12 @@ static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) apr_status_t rc; rc = apr_file_open(&dcfg->auditlog_fd, file_name, - APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, - CREATEMODE, cmd->pool); + APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, + CREATEMODE, cmd->pool); if (rc != APR_SUCCESS) { return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log file: %s", - file_name); + file_name); } } @@ -1000,7 +1149,7 @@ static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) pipe_log = ap_open_piped_log(cmd->pool, pipe_name); if (pipe_log == NULL) { return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log pipe: %s", - pipe_name); + pipe_name); } dcfg->auditlog2_fd = ap_piped_log_write_fd(pipe_log); } @@ -1009,12 +1158,12 @@ static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) apr_status_t rc; rc = apr_file_open(&dcfg->auditlog2_fd, file_name, - APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, - CREATEMODE, cmd->pool); + APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY, + CREATEMODE, cmd->pool); if (rc != APR_SUCCESS) { return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log file: %s", - file_name); + file_name); } } @@ -1022,7 +1171,7 @@ static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) } static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { directory_config *dcfg = _dcfg; @@ -1035,7 +1184,7 @@ static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, } static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { directory_config *dcfg = _dcfg; @@ -1048,22 +1197,22 @@ static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, } static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { directory_config *dcfg = _dcfg; if (strcasecmp(p1, "Serial") == 0) dcfg->auditlog_type = AUDITLOG_SERIAL; else - if (strcasecmp(p1, "Concurrent") == 0) dcfg->auditlog_type = AUDITLOG_CONCURRENT; - else - return (const char *)apr_psprintf(cmd->pool, - "ModSecurity: Unrecognised parameter value for SecAuditLogType: %s", p1); + if (strcasecmp(p1, "Concurrent") == 0) dcfg->auditlog_type = AUDITLOG_CONCURRENT; + else + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecAuditLogType: %s", p1); return NULL; } static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -1085,7 +1234,7 @@ static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, } static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, - const char *p1) + const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -1620,8 +1769,25 @@ static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, return NULL; } +/* +* \brief Add SecRuleUpdateTargetById +* +* \param cmd Pointer to configuration data +* \param _dcfg Pointer to directory configuration +* \param p1 Pointer to configuration option +* \param p2 Pointer to configuration option +* \param p3 Pointer to configuration option +* +* \retval NULL On failure|Success +*/ +static const char *cmd_rule_update_target_by_id(cmd_parms *cmd, void *_dcfg, + const char *p1, const char *p2, const char *p3) +{ + return update_rule_target(cmd, (directory_config *)_dcfg, NULL, p1, p2, p3); +} + static const char *cmd_rule(cmd_parms *cmd, void *_dcfg, - const char *p1, const char *p2, const char *p3) + const char *p1, const char *p2, const char *p3) { return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, p1, p2, p3); } @@ -2374,6 +2540,14 @@ const command_rec module_directives[] = { "updated action list" ), + AP_INIT_TAKE23 ( + "SecRuleUpdateTargetById", + cmd_rule_update_target_by_id, + NULL, + CMD_SCOPE_ANY, + "updated target list" + ), + AP_INIT_TAKE1 ( "SecServerSignature", cmd_server_signature, diff --git a/apache2/re.c b/apache2/re.c index 24dca07d..a364d7d2 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -60,11 +60,35 @@ static void msre_actionset_cardinality_fixup(msre_actionset *actionset, msre_act } } +char *msre_generate_target_string(apr_pool_t *pool, msre_rule *rule) { + + char *target_str = NULL; + msre_var **targets = NULL; + int i = 0, count = 0; + + targets = (msre_var **)rule->targets->elts; + + for (i = 0; i < rule->targets->nelts; i++) { + + if(targets[i]->name != NULL && strlen(targets[i]->name) > 0) { + target_str = apr_pstrcat(pool, + (target_str == NULL) ? "" : apr_psprintf(pool, "%s|", target_str), + (targets[i]->is_negated == 0) ? "" : "!", + (targets[i]->is_counting == 0) ? "" : "&", + (targets[i]->name == NULL) ? "" : targets[i]->name, + (targets[i]->param == NULL) ? "" : apr_psprintf(pool, ":%s", targets[i]->param), + NULL); + } + + } + + return target_str; +} + /** * Generate an action string from an actionset. */ -char *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset) -{ +char *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset) { const apr_array_header_t *tarr = NULL; const apr_table_entry_t *telts = NULL; char *actions = NULL; @@ -268,7 +292,7 @@ msre_reqbody_processor_metadata *msre_resolve_reqbody_processor( * and an (optional) parameter. */ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char *name, const char *param, - modsec_rec *msr, char **error_msg) + modsec_rec *msr, char **error_msg) { const char *varparam = param; msre_var *var = apr_pcalloc(pool, sizeof(msre_var)); @@ -280,21 +304,21 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * /* Handle negation and member counting */ if (name[0] == '!') { var->is_negated = 1; - var->name = name + 1; + var->name = (char *)name + 1; } else - if (name[0] == '&') { - var->is_counting = 1; - var->name = name + 1; - } - else { - var->name = name; - } + if (name[0] == '&') { + var->is_counting = 1; + var->name = (char *)name + 1; + } + else { + var->name = (char *)name; + } /* Treat HTTP_* targets as an alias for REQUEST_HEADERS:* */ if ( (var->name != NULL) - && (strlen(var->name) > 5) - && (strncmp("HTTP_", var->name, 5) == 0)) + && (strlen(var->name) > 5) + && (strncmp("HTTP_", var->name, 5) == 0)) { const char *oldname = var->name; var->name = apr_pstrdup(pool, "REQUEST_HEADERS"); @@ -313,7 +337,7 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * if (var->is_counting) { if (var->metadata->type == VAR_SIMPLE) { *error_msg = apr_psprintf(engine->mp, "The & modificator does not apply to " - "non-collection variables."); + "non-collection variables."); return NULL; } } @@ -322,7 +346,7 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * if (varparam == NULL) { if (var->metadata->argc_min > 0) { *error_msg = apr_psprintf(engine->mp, "Missing mandatory parameter for variable %s.", - name); + name); return NULL; } } else { /* Parameter present */ @@ -330,11 +354,12 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * /* Do we allow a parameter? */ if (var->metadata->argc_max == 0) { *error_msg = apr_psprintf(engine->mp, "Variable %s does not support parameters.", - name); + name); return NULL; } - var->param = varparam; + var->param = (char *)varparam; + } return var; @@ -347,7 +372,7 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * * per-request */ msre_var *msre_create_var(msre_ruleset *ruleset, const char *name, const char *param, - modsec_rec *msr, char **error_msg) + modsec_rec *msr, char **error_msg) { msre_var *var = msre_create_var_ex(ruleset->engine->mp, ruleset->engine, name, param, msr, error_msg); if (var == NULL) return NULL; diff --git a/apache2/re.h b/apache2/re.h index a74a4f73..ec4a94cb 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -45,12 +45,14 @@ typedef struct msre_cache_rec msre_cache_rec; #include "msc_pcre.h" #include "persist_dbm.h" #include "apache2.h" +#include "http_config.h" #if defined(WITH_LUA) #include "msc_lua.h" #endif /* Actions, variables, functions and operator functions */ +char DSOLOCAL *update_rule_target(cmd_parms *cmd, directory_config *dcfg, msre_ruleset *rset, const char *p1, const char *p2, const char *p3); apr_status_t DSOLOCAL collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var); @@ -277,10 +279,10 @@ struct msre_var_metadata { }; struct msre_var { - const char *name; + char *name; const char *value; unsigned int value_len; - const char *param; + char *param; const void *param_data; msre_var_metadata *metadata; msc_regex_t *param_regex; @@ -327,6 +329,8 @@ struct msre_actionset { char DSOLOCAL *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset); +char DSOLOCAL *msre_generate_target_string(apr_pool_t *pool, msre_rule *rule); + void DSOLOCAL msre_engine_variable_register(msre_engine *engine, const char *name, unsigned int type, unsigned int argc_min, unsigned int argc_max, fn_var_validate_t validate, fn_var_generate_t generate, @@ -411,7 +415,6 @@ apr_status_t DSOLOCAL msre_parse_vars(msre_ruleset *ruleset, const char *text, char DSOLOCAL *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset); - /* -- Data Cache -- */ struct msre_cache_rec { diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 838b5c3d..364e8a6c 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -816,14 +816,25 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) } return NULL; - } - else { - return apr_psprintf(engine->mp, "Invalid ctl name setting: %s", name); - } + } else + if (strcasecmp(name, "ruleUpdateTargetById") == 0) { + char *parm = NULL; + char *savedptr = NULL; + + parm = apr_strtok(value,";",&savedptr); + + if(parm == NULL && savedptr == NULL) + return apr_psprintf(engine->mp, "ruleUpdateTargetById must has at least id;append_value"); + + return NULL; + } + else { + return apr_psprintf(engine->mp, "Invalid ctl name setting: %s", name); + } } static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) + msre_action *action) { /* Do nothing. */ return 1; @@ -855,7 +866,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, msr->txcfg->is_enabled = MODSEC_DETECTION_ONLY; msr->usercfg->is_enabled = MODSEC_DETECTION_ONLY; } - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set ruleEngine to %s.", value); } @@ -864,11 +875,11 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, } else if (strcasecmp(name, "ruleRemoveById") == 0) { *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value); - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Removed rule %s.", value); } - + return 1; } else if (strcasecmp(name, "requestBodyAccess") == 0) { @@ -877,7 +888,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (pv == -1) return -1; msr->txcfg->reqbody_access = pv; msr->usercfg->reqbody_access = pv; - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", pv); } @@ -903,7 +914,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, } else if (strcasecmp(name, "requestBodyProcessor") == 0) { msr->msc_reqbody_processor = value; - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value); } @@ -916,7 +927,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (pv == -1) return -1; msr->txcfg->resbody_access = pv; msr->usercfg->resbody_access = pv; - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set responseBodyAccess to %d.", pv); } @@ -977,7 +988,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, /* Set the new value. */ msr->txcfg->auditlog_parts = new_value; msr->usercfg->auditlog_parts = new_value; - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set auditLogParts to %s.", msr->txcfg->auditlog_parts); } @@ -987,7 +998,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (strcasecmp(name, "debugLogLevel") == 0) { msr->txcfg->debuglog_level = atoi(value); msr->usercfg->debuglog_level = atoi(value); - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set debugLogLevel to %d.", msr->txcfg->debuglog_level); } @@ -1000,7 +1011,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, /* ENH Accept only in correct phase warn otherwise. */ msr->txcfg->reqbody_limit = limit; msr->usercfg->reqbody_limit = limit; - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set requestBodyLimit to %ld.", limit); } @@ -1013,11 +1024,29 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, /* ENH Accept only in correct phase warn otherwise. */ msr->txcfg->of_limit = limit; msr->usercfg->of_limit = limit; - + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set responseBodyLimit to %ld.", limit); } + return 1; + } else + if (strcasecmp(name, "ruleUpdateTargetById") == 0) { + char *p1 = NULL, *p2 = NULL, *p3 = NULL; + char *savedptr = NULL; + + p1 = apr_strtok(value,";",&savedptr); + + p2 = apr_strtok(NULL,";",&savedptr); + + p3 = apr_strtok(NULL,";",&savedptr); + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Ctl: ruleUpdateTargetById id=%s append=%s replace=%s", p1, p2, p3); + } + + update_rule_target(NULL, NULL, rule->ruleset, p1, p2, p3); + return 1; } else {