diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index cd16892e..d2d3d317 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -532,7 +532,8 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * /* Must NOT use metadata actions. */ if ((rule->actionset->id != NOT_SET_P) ||(rule->actionset->rev != NOT_SET_P) - ||(rule->actionset->msg != NOT_SET_P)) + ||(rule->actionset->msg != NOT_SET_P) + ||(rule->actionset->logdata != NOT_SET_P)) { return apr_psprintf(cmd->pool, "ModSecurity: Metadata actions (id, rev, msg) " " can only be specified by chain starter rules."); @@ -832,7 +833,8 @@ static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, const char *p /* Must not use metadata actions. */ if ((dcfg->tmp_default_actionset->id != NOT_SET_P) ||(dcfg->tmp_default_actionset->rev != NOT_SET_P) - ||(dcfg->tmp_default_actionset->msg != NOT_SET_P)) + ||(dcfg->tmp_default_actionset->msg != NOT_SET_P) + ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P)) { return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " "contain any metadata actions (id, rev, msg)."); diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 9d96c178..9c5aaece 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -450,13 +450,43 @@ char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long in unsigned long int i, j; for (i = 0, j = 0; i < text_length; i++, j += 4) { - apr_snprintf((char *)ret+j, 5, "\\x%02x", text[i]); + ret[j] = '\\'; + ret[j+1] = 'x'; + c2x(text[i], ret+j+2); } ret[text_length * 4] = '\0'; return (char *)ret; } +/** + * Transform text to ASCII printable or hex escaped + */ +char *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { + unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); + unsigned long int i, j; + + for (i = 0, j = 0; i < text_length; i++) { + if ( (text[i] == '"') + ||(text[i] == '\\') + ||(text[i] <= 0x1f) + ||(text[i] >= 0x7f)) + { + ret[j] = '\\'; + ret[j+1] = 'x'; + c2x(text[i], ret+j+2); + j += 4; + } + else { + ret[j] = text[i]; + j ++; + } + } + ret[j] = '\0'; + + return (char *)ret; +} + /** * Transform input into a form safe for logging. */ diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 5dc3b7e8..1ae7b873 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -59,7 +59,9 @@ char DSOLOCAL *log_escape_nq_ex(apr_pool_t *p, const char *text, unsigned long i char DSOLOCAL *log_escape_header_name(apr_pool_t *p, const char *text); -char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length); +char DSOLOCAL *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length); + +char DSOLOCAL *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length); char DSOLOCAL *_log_escape(apr_pool_t *p, const unsigned char *input, unsigned long int input_length, int escape_quotes, int escape_colon); diff --git a/apache2/re.c b/apache2/re.c index e7630f4e..8424c34b 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -417,6 +417,7 @@ msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, actionset->id = NOT_SET_P; actionset->rev = NOT_SET_P; actionset->msg = NOT_SET_P; + actionset->logdata = NOT_SET_P; actionset->phase = NOT_SET; actionset->severity = -1; actionset->rule = NOT_SET_P; @@ -492,6 +493,7 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent if (child->id != NOT_SET_P) merged->id = child->id; if (child->rev != NOT_SET_P) merged->rev = child->rev; if (child->msg != NOT_SET_P) merged->msg = child->msg; + if (child->logdata != NOT_SET_P) merged->logdata = child->logdata; if (child->severity != NOT_SET) merged->severity = child->severity; if (child->phase != NOT_SET) merged->phase = child->phase; if (child->rule != NOT_SET_P) merged->rule = child->rule; @@ -548,6 +550,7 @@ static void msre_actionset_set_defaults(msre_actionset *actionset) { if (actionset->id == NOT_SET_P) actionset->id = NULL; if (actionset->rev == NOT_SET_P) actionset->rev = NULL; if (actionset->msg == NOT_SET_P) actionset->msg = NULL; + if (actionset->logdata == NOT_SET_P) actionset->logdata = NULL; if (actionset->phase == NOT_SET) actionset->phase = 2; if (actionset->severity == -1); /* leave at -1 */ if (actionset->rule == NOT_SET_P) actionset->rule = NULL; @@ -973,6 +976,7 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { char *id = ""; char *rev = ""; char *msg = ""; + char *logdata = ""; char *severity = ""; char *tags = ""; char *fn = ""; @@ -1002,6 +1006,17 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { msg = apr_psprintf(msr->mp, " [msg \"%s\"]", log_escape_ex(msr->mp, var->value, var->value_len)); } + if (actionset->logdata != NULL) { + //TODO: restrict to 512 bytes + /* Expand variables in the message string. */ + msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->value = (char *)actionset->logdata; + var->value_len = strlen(actionset->logdata); + expand_macros(msr, var, NULL, msr->mp); + + logdata = apr_psprintf(msr->mp, " [data \"%s\"]", + log_escape_hex(msr->mp, (unsigned char *)var->value, var->value_len)); + } if ((actionset->severity >= 0)&&(actionset->severity <= 7)) { severity = apr_psprintf(msr->mp, " [severity \"%s\"]", msre_format_severity(actionset->severity)); @@ -1019,7 +1034,7 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { } } - return apr_pstrcat(msr->mp, fn, id, rev, msg, severity, tags, NULL); + return apr_pstrcat(msr->mp, fn, id, rev, msg, logdata, severity, tags, NULL); } /** diff --git a/apache2/re.h b/apache2/re.h index 07e6368c..f214f117 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -224,6 +224,7 @@ struct msre_actionset { const char *id; const char *rev; const char *msg; + const char *logdata; int severity; int phase; msre_rule *rule; diff --git a/apache2/re_actions.c b/apache2/re_actions.c index fd0453a2..e182d48e 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -232,6 +232,15 @@ static apr_status_t msre_action_msg_init(msre_engine *engine, msre_actionset *ac return 1; } +/* logdata */ + +static apr_status_t msre_action_logdata_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + actionset->logdata = action->param; + return 1; +} + /* severity */ static apr_status_t msre_action_severity_init(msre_engine *engine, @@ -1424,6 +1433,18 @@ void msre_engine_register_default_actions(msre_engine *engine) { NULL ); + /* logdata */ + msre_engine_action_register(engine, + "logdata", + ACTION_METADATA, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + NULL, + msre_action_logdata_init, + NULL + ); + /* severity */ msre_engine_action_register(engine, "severity", diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 70c3bb96..115fa964 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -409,6 +409,9 @@ static int var_rule_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, } else if ((strcasecmp(var->param, "msg") == 0)&&(actionset->msg != NULL)) { return var_simple_generate(var, vartab, mptmp, actionset->msg); + } else + if ((strcasecmp(var->param, "logdata") == 0)&&(actionset->logdata != NULL)) { + return var_simple_generate(var, vartab, mptmp, actionset->logdata); } return 0;