apr_status_t msre_ruleset_process_phase()

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;
}