in apache2/re.c [1474:1936]
apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) {
#endif
apr_array_header_t *arr = NULL;
msre_rule **rules;
apr_status_t rc;
const char *skip_after = NULL;
msre_rule *last_rule = NULL;
msre_rule *rule_starter = NULL;
int i, mode, skip, skipped, saw_starter;
/* First determine which set of rules we need to use. */
switch (msr->phase) {
case PHASE_REQUEST_HEADERS :
arr = ruleset->phase_request_headers;
break;
case PHASE_REQUEST_BODY :
arr = ruleset->phase_request_body;
break;
case PHASE_RESPONSE_HEADERS :
arr = ruleset->phase_response_headers;
break;
case PHASE_RESPONSE_BODY :
arr = ruleset->phase_response_body;
break;
case PHASE_LOGGING :
arr = ruleset->phase_logging;
break;
default :
msr_log(msr, 1, "Internal Error: Invalid phase %d", msr->phase);
return -1;
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "This phase consists of %d rule(s).", arr->nelts);
}
apr_table_clear(msr->matched_vars);
/* Loop through the rules in the selected set. */
skip = 0;
skipped = 0;
saw_starter = 0;
mode = NEXT_RULE;
rules = (msre_rule **)arr->elts;
for (i = 0; i < arr->nelts; i++) {
msre_rule *rule = rules[i];
#if defined(PERFORMANCE_MEASUREMENT)
apr_time_t time1 = 0;
#endif
/* Reset the rule interception flag */
msr->rule_was_intercepted = 0;
/* SKIP_RULES is used to skip all rules until we hit a placeholder
* with the specified rule ID and then resume execution after that.
*/
if (mode == SKIP_RULES) {
/* Go to the next rule if we have not yet hit the skip_after ID */
if ((rule->placeholder == RULE_PH_NONE) || (rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) {
if(i-1 >=0)
last_rule = rules[i-1];
else
last_rule = rules[0];
if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained && (saw_starter == 1)) {
mode = NEXT_RULE;
skipped = 1;
--i;
} else {
mode = SKIP_RULES;
skipped = 0;
saw_starter = 0;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Current rule is id=\"%s\" [chained %d] is trying to find the SecMarker=\"%s\" [stater %d]",rule->actionset->id,last_rule->actionset->is_chained,skip_after,saw_starter);
}
}
continue;
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Found rule %pp id=\"%s\".", rule, skip_after);
}
/* Go to the rule *after* this one to continue execution. */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Continuing execution after rule id=\"%s\".", skip_after);
}
saw_starter = 0;
skipped = 0;
skip_after = NULL;
mode = NEXT_RULE;
apr_table_clear(msr->matched_vars);
continue;
}
/* Skip any rule marked as a placeholder */
if (rule->placeholder != RULE_PH_NONE) {
continue;
}
/* NEXT_CHAIN is used when one of the rules in a chain
* fails to match and then we need to skip the remaining
* rules in that chain in order to get to the next
* rule that can execute.
*/
if (mode == NEXT_CHAIN) {
if (rule->actionset->is_chained == 0) {
mode = NEXT_RULE;
}
/* Go to the next rule. */
apr_table_clear(msr->matched_vars);
continue;
}
/* If we are here that means the mode is NEXT_RULE, which
* then means we have done processing any chains. However,
* if the "skip" parameter is set we need to skip over.
*/
if ((mode == NEXT_RULE)&&(skip > 0)) {
/* Decrement the skip counter by one. */
skip--;
/* If the current rule is part of a chain then
* we need to skip over the entire chain. Thus
* we change the mode to NEXT_CHAIN. The skip
* counter will not decrement as we are moving
* over the rules belonging to the chain.
*/
if (rule->actionset->is_chained) {
mode = NEXT_CHAIN;
}
/* Go to the next rule. */
apr_table_clear(msr->matched_vars);
continue;
}
/* Check if this rule was removed at runtime */
if (((rule->actionset->id !=NULL) && !apr_is_empty_array(msr->removed_rules)) ||
(apr_is_empty_array(msr->removed_rules_tag)==0) || (apr_is_empty_array(msr->removed_rules_msg)==0)) {
int j, act, rc;
int do_process = 1;
const char *range = NULL;
rule_exception *re = NULL;
char *my_error_msg;
const apr_array_header_t *tag_tarr = NULL;
const apr_table_entry_t *tag_telts = NULL;
for(j = 0; j < msr->removed_rules_msg->nelts; j++) {
re = ((rule_exception **)msr->removed_rules_msg->elts)[j];
if(rule->actionset->msg !=NULL) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Checking removal of rule msg=\"%s\" against: %s", rule->actionset->msg, re->param);
}
rc = msc_regexec(re->param_data,
rule->actionset->msg, strlen(rule->actionset->msg),
&my_error_msg);
if (rc >= 0) {
do_process = 0;
break;
}
}
}
for(j = 0; j < msr->removed_rules->nelts; j++) {
range = ((const char**)msr->removed_rules->elts)[j];
if(rule->actionset->id !=NULL) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Checking removal of rule id=\"%s\" against: %s", rule->actionset->id, range);
}
if (rule_id_in_range(atoi(rule->actionset->id), range)) {
do_process = 0;
break;
}
}
}
tag_tarr = apr_table_elts(rule->actionset->actions);
tag_telts = (const apr_table_entry_t*)tag_tarr->elts;
for (act = 0; act < tag_tarr->nelts; act++) {
msre_action *action = (msre_action *)tag_telts[act].val;
if((action != NULL) && (action->metadata != NULL ) && strcmp("tag", action->metadata->name) == 0) {
for(j = 0; j < msr->removed_rules_tag->nelts; j++) {
re = ((rule_exception **)msr->removed_rules_tag->elts)[j];
if(action->param != NULL) {
/* Expand variables in the tag argument. */
msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
var->value = (char *)action->param;
var->value_len = strlen(action->param);
expand_macros(msr, var, NULL, msr->mp);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Checking removal of rule tag=\"%s\" against: %s", var->value, re->param);
}
rc = msc_regexec(re->param_data,
var->value, strlen(var->value),
&my_error_msg);
if (rc >= 0) {
do_process = 0;
break;
}
}
}
}
}
/* Go to the next rule if this one has been removed. */
if (do_process == 0) {
if (msr->txcfg->debuglog_level >= 5) {
msr_log(msr, 5, "Not processing %srule id=\"%s\": "
"removed by ctl action",
rule->actionset->is_chained ? "chained " : "",
rule->actionset->id);
}
/* Skip the whole chain, if this is a chained rule */
if (rule->actionset->is_chained) {
mode = NEXT_CHAIN;
}
skipped = 0;
saw_starter = 0;
apr_table_clear(msr->matched_vars);
continue;
}
}
if(msr->txcfg->is_enabled == MODSEC_DISABLED) {
saw_starter = 0;
skipped = 0;
skip_after = NULL;
mode = NEXT_RULE;
apr_table_clear(msr->matched_vars);
continue;
}
if (msr->txcfg->debuglog_level >= 4) {
apr_pool_t *p = msr->mp;
const char *fn = NULL;
const char *id = NULL;
const char *rev = NULL;
if (rule->filename != NULL) {
fn = apr_psprintf(p, " [file \"%s\"] [line \"%d\"]", rule->filename, rule->line_num);
}
if (rule->actionset != NULL && rule->actionset->id != NULL) {
id = apr_psprintf(p, " [id \"%s\"]", rule->actionset->id);
}
if (rule->actionset != NULL && rule->actionset->rev != NULL) {
rev = apr_psprintf(p, " [rev \"%s\"]", rule->actionset->rev);
}
msr_log(msr, 4, "Recipe: Invoking rule %pp;%s%s%s.",
rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : ""));
msr_log(msr, 5, "Rule %pp: %s", rule, rule->unparsed);
}
#if defined(PERFORMANCE_MEASUREMENT)
time1 = apr_time_now();
#endif
rc = msre_rule_process(rule, msr);
#if defined(PERFORMANCE_MEASUREMENT)
rule->execution_time += (apr_time_now() - time1);
#endif
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Rule returned %d.", rc);
}
if (rc == RULE_NO_MATCH) {
if (rule->actionset->is_chained) {
/* If the current rule is part of a chain then
* we need to skip over all the rules in the chain.
*/
mode = NEXT_CHAIN;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "No match, chained -> mode NEXT_CHAIN.");
}
} else {
/* This rule is not part of a chain so we simply
* move to the next rule.
*/
mode = NEXT_RULE;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "No match, not chained -> mode NEXT_RULE.");
}
}
apr_table_clear(msr->matched_vars);
skipped = 0;
saw_starter = 0;
}
else if (rc == RULE_MATCH) {
if (msr->rule_was_intercepted) {
/* If the transaction was intercepted by this rule we will
* go back. Do note that we are relying on the
* rule to know if it is a part of a chain and
* not intercept if it is.
*/
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Match, intercepted -> returning.");
}
if(i-1 >= 0)
last_rule = rules[i-1];
else
last_rule = rules[0];
if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained) {
int st = 0;
for(st=i;st>=0;st--) {
rule_starter = rules[st];
if(rule_starter != NULL && rule_starter->chain_starter != NULL) {
if((msr != NULL) && (msr->intercept_actionset != NULL) && (rule_starter->actionset != NULL))
msr->intercept_actionset->intercept_uri = rule_starter->actionset->intercept_uri;
break;
}
}
}
apr_table_clear(msr->matched_vars);
return 1;
}
if (rule->actionset->skip_after != NULL) {
skip_after = rule->actionset->skip_after;
mode = SKIP_RULES;
saw_starter = 1;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Skipping after rule %pp id=\"%s\" -> mode SKIP_RULES.", rule, skip_after);
}
continue;
}
if(skipped == 1) {
mode = SKIP_RULES;
continue;
}
/* We had a match but the transaction was not
* intercepted. In that case we proceed with the
* next rule...
*/
mode = NEXT_RULE;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Match -> mode NEXT_RULE.");
}
/* ...unless we need to skip, in which case we
* determine how many rules/chains we need to
* skip and configure the counter accordingly.
*/
if (rule->actionset->is_chained == 0) {
apr_table_clear(msr->matched_vars);
if (rule->chain_starter != NULL) {
if (rule->chain_starter->actionset->skip_count > 0) {
skip = rule->chain_starter->actionset->skip_count;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping %d rules/chains (from a chain).", skip);
}
}
}
else if (rule->actionset->skip_count > 0) {
skip = rule->actionset->skip_count;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping %d rules/chains.", skip);
}
}
}
}
else if (rc < 0) {
const char *id = "";
const char *msg = "";
if (rule->actionset) {
if (rule->actionset->id) {
id = rule->actionset->id;
}
if (rule->actionset->msg) {
msg = rule->actionset->msg;
}
}
msr_log(msr, 1, "Rule processing failed (id=%s, msg=%s).", id, msg);
if (msr->txcfg->reqintercept_oe == 1) {
apr_table_clear(msr->matched_vars);
return -1;
} else {
if (rule->actionset->is_chained) {
/* If the current rule is part of a chain then
* we need to skip over all the rules in the chain.
*/
mode = NEXT_CHAIN;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Ruled failed, chained -> mode NEXT_CHAIN.");
}
} else {
/* This rule is not part of a chain so we simply
* move to the next rule.
*/
mode = NEXT_RULE;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Rule failed, not chained -> mode NEXT_RULE.");
}
}
apr_table_clear(msr->matched_vars);
skipped = 0;
saw_starter = 0;
}
}
else {
const char *id = "";
const char *msg = "";
if (rule->actionset) {
if (rule->actionset->id) {
id = rule->actionset->id;
}
if (rule->actionset->msg) {
msg = rule->actionset->msg;
}
}
msr_log(msr, 1, "Rule processing failed with unknown return code: %d (id=%s, msg=%s).", rc, id, msg);
apr_table_clear(msr->matched_vars);
return -1;
}
}
/* ENH warn if chained rules are missing. */
apr_table_clear(msr->matched_vars);
return 0;
}