diff --git a/headers/modsecurity/actions/action.h b/headers/modsecurity/actions/action.h index 08186209..4409660c 100644 --- a/headers/modsecurity/actions/action.h +++ b/headers/modsecurity/actions/action.h @@ -23,6 +23,7 @@ #include "modsecurity/intervention.h" #include "modsecurity/rule.h" +#include "modsecurity/rule_with_actions.h" #ifndef HEADERS_MODSECURITY_ACTIONS_ACTION_H_ #define HEADERS_MODSECURITY_ACTIONS_ACTION_H_ @@ -32,7 +33,6 @@ namespace modsecurity { class Transaction; class RuleWithOperator; -class RuleWithActions; namespace actions { diff --git a/headers/modsecurity/rule.h b/headers/modsecurity/rule.h index f7595a21..c23b5bb5 100644 --- a/headers/modsecurity/rule.h +++ b/headers/modsecurity/rule.h @@ -102,127 +102,6 @@ class Rule { }; -class RuleWithActions : public Rule { - public: - RuleWithActions( - Actions *a, - Transformations *t, - std::unique_ptr fileName, - int lineNumber); - - ~RuleWithActions(); - - void executeAction(Transaction *trans, - bool containsBlock, - std::shared_ptr ruleMessage, - actions::Action *a, - bool context); - - void executeTransformations( - Transaction *trasn, const std::string &value, TransformationResults &ret); - inline void executeTransformation( - actions::transformations::Transformation *a, - std::shared_ptr *value, - Transaction *trans, - TransformationResults *ret, - std::string *path, - int *nth) const; - - void executeActionsIndependentOfChainedRuleResult(Transaction *trasn, - bool *b, std::shared_ptr ruleMessage); - void executeActionsAfterFullMatch(Transaction *trasn, - bool containsDisruptive, std::shared_ptr ruleMessage); - - std::vector getActionsByName(const std::string& name, - Transaction *t); - bool containsTag(const std::string& name, Transaction *t); - bool containsMsg(const std::string& name, Transaction *t); - - inline bool isChained() const { return m_isChained == true; } - inline bool hasCaptureAction() const { return m_containsCaptureAction == true; } - inline void setChained(bool b) { m_isChained = b; } - inline bool hasDisruptiveAction() const { return m_disruptiveAction != NULL; } - inline bool hasBlockAction() const { return m_containsStaticBlockAction == true; } - inline bool hasMultimatch() const { return m_containsMultiMatchAction == true; } - - inline bool hasLogData() const { return m_logData != NULL; } - std::string logData(Transaction *t); - inline bool hasMsg() const { return m_msg != NULL; } - std::string msg(Transaction *t); - inline bool hasSeverity() const { return m_severity != NULL; } - int severity() const; - - std::string m_rev; - std::string m_ver; - int m_accuracy; - int m_maturity; - - int64_t m_ruleId; - - private: - /* actions */ - actions::Action *m_disruptiveAction; - actions::LogData *m_logData; - actions::Msg *m_msg; - actions::Severity *m_severity; - MatchActions m_actionsRuntimePos; - SetVars m_actionsSetVar; - Tags m_actionsTag; - - /* actions > transformations */ - Transformations m_transformations; - - bool m_containsCaptureAction:1; - bool m_containsMultiMatchAction:1; - bool m_containsStaticBlockAction:1; - bool m_isChained:1; -}; - - -class RuleWithOperator : public RuleWithActions { - public: - RuleWithOperator(operators::Operator *op, - variables::Variables *variables, - std::vector *actions, - Transformations *transformations, - std::unique_ptr fileName, - int lineNumber); - - virtual ~RuleWithOperator(); - - bool evaluate(Transaction *transaction, - std::shared_ptr rm) override; - - void getVariablesExceptions(Transaction *t, - variables::Variables *exclusion, variables::Variables *addition); - inline void getFinalVars(variables::Variables *vars, - variables::Variables *eclusion, Transaction *trans); - - bool executeOperatorAt(Transaction *trasn, const std::string &key, - std::string value, std::shared_ptr rm); - - static void updateMatchedVars(Transaction *trasn, const std::string &key, - const std::string &value); - static void cleanMatchedVars(Transaction *trasn); - - inline bool isUnconditional() const { return m_operator == NULL; } - - std::string getOperatorName() const; - - virtual std::string getReference() override { - return std::to_string(m_ruleId); - } - - std::unique_ptr m_chainedRuleChild; - RuleWithOperator *m_chainedRuleParent; - - private: - modsecurity::variables::Variables *m_variables; - operators::Operator *m_operator; - - bool m_unconditional:1; -}; - } // namespace modsecurity #endif diff --git a/headers/modsecurity/rule_message.h b/headers/modsecurity/rule_message.h index 535c526e..c36d5431 100644 --- a/headers/modsecurity/rule_message.h +++ b/headers/modsecurity/rule_message.h @@ -26,6 +26,7 @@ #include "modsecurity/transaction.h" #include "modsecurity/rule.h" +#include "modsecurity/rule_with_operator.h" #ifdef __cplusplus diff --git a/headers/modsecurity/rule_with_actions.h b/headers/modsecurity/rule_with_actions.h new file mode 100644 index 00000000..0426769a --- /dev/null +++ b/headers/modsecurity/rule_with_actions.h @@ -0,0 +1,121 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#ifdef __cplusplus +#include +#include +#include +#include +#include +#include +#endif + +#ifndef HEADERS_MODSECURITY_RULE_WITH_ACTIONS_H_ +#define HEADERS_MODSECURITY_RULE_WITH_ACTIONS_H_ + +#include "modsecurity/transaction.h" +#include "modsecurity/modsecurity.h" +#include "modsecurity/variable_value.h" +#include "modsecurity/rule.h" + +#ifdef __cplusplus + +namespace modsecurity { + + +class RuleWithActions : public Rule { + public: + RuleWithActions( + Actions *a, + Transformations *t, + std::unique_ptr fileName, + int lineNumber); + + ~RuleWithActions(); + + void executeAction(Transaction *trans, + bool containsBlock, + std::shared_ptr ruleMessage, + actions::Action *a, + bool context); + + + void executeTransformations( + Transaction *trasn, const std::string &value, TransformationResults &ret); + + inline void executeTransformation( + actions::transformations::Transformation *a, + std::shared_ptr *value, + Transaction *trans, + TransformationResults *ret, + std::string *path, + int *nth) const; + + void executeActionsIndependentOfChainedRuleResult(Transaction *trasn, + bool *b, std::shared_ptr ruleMessage); + void executeActionsAfterFullMatch(Transaction *trasn, + bool containsDisruptive, std::shared_ptr ruleMessage); + + std::vector getActionsByName(const std::string& name, + Transaction *t); + bool containsTag(const std::string& name, Transaction *t); + bool containsMsg(const std::string& name, Transaction *t); + + inline bool isChained() const { return m_isChained == true; } + inline bool hasCaptureAction() const { return m_containsCaptureAction == true; } + inline void setChained(bool b) { m_isChained = b; } + inline bool hasDisruptiveAction() const { return m_disruptiveAction != NULL; } + inline bool hasBlockAction() const { return m_containsStaticBlockAction == true; } + inline bool hasMultimatch() const { return m_containsMultiMatchAction == true; } + + inline bool hasLogData() const { return m_logData != NULL; } + std::string logData(Transaction *t); + inline bool hasMsg() const { return m_msg != NULL; } + std::string msg(Transaction *t); + inline bool hasSeverity() const { return m_severity != NULL; } + int severity() const; + + std::string m_rev; + std::string m_ver; + int m_accuracy; + int m_maturity; + + + int64_t m_ruleId; + + private: + /* actions */ + actions::Action *m_disruptiveAction; + actions::LogData *m_logData; + actions::Msg *m_msg; + actions::Severity *m_severity; + MatchActions m_actionsRuntimePos; + SetVars m_actionsSetVar; + Tags m_actionsTag; + + /* actions > transformations */ + Transformations m_transformations; + + bool m_containsCaptureAction:1; + bool m_containsMultiMatchAction:1; + bool m_containsStaticBlockAction:1; + bool m_isChained:1; +}; + +} // namespace modsecurity +#endif + + +#endif // HEADERS_MODSECURITY_RULE_WITH_ACTIONS_H_ \ No newline at end of file diff --git a/headers/modsecurity/rule_with_operator.h b/headers/modsecurity/rule_with_operator.h new file mode 100644 index 00000000..69ae0bfa --- /dev/null +++ b/headers/modsecurity/rule_with_operator.h @@ -0,0 +1,89 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#ifdef __cplusplus +#include +#include +#include +#include +#include +#include +#endif + +#ifndef HEADERS_MODSECURITY_RULE_WITH_OPERATOR_H_ +#define HEADERS_MODSECURITY_RULE_WITH_OPERATOR_H_ + +#include "modsecurity/transaction.h" +#include "modsecurity/modsecurity.h" +#include "modsecurity/variable_value.h" +#include "modsecurity/rule.h" +#include "modsecurity/rule_with_actions.h" + +#ifdef __cplusplus + +namespace modsecurity { + + +class RuleWithOperator : public RuleWithActions { + public: + RuleWithOperator(operators::Operator *op, + variables::Variables *variables, + std::vector *actions, + Transformations *transformations, + std::unique_ptr fileName, + int lineNumber); + + virtual ~RuleWithOperator(); + + bool evaluate(Transaction *transaction, + std::shared_ptr rm) override; + + void getVariablesExceptions(Transaction *t, + variables::Variables *exclusion, variables::Variables *addition); + inline void getFinalVars(variables::Variables *vars, + variables::Variables *eclusion, Transaction *trans); + + bool executeOperatorAt(Transaction *trasn, const std::string &key, + std::string value, std::shared_ptr rm); + + static void updateMatchedVars(Transaction *trasn, const std::string &key, + const std::string &value); + static void cleanMatchedVars(Transaction *trasn); + + inline bool isUnconditional() const { return m_operator == NULL; } + + std::string getOperatorName() const; + + virtual std::string getReference() override { + return std::to_string(m_ruleId); + } + + std::unique_ptr m_chainedRuleChild; + RuleWithOperator *m_chainedRuleParent; + + private: + modsecurity::variables::Variables *m_variables; + operators::Operator *m_operator; + + + bool m_unconditional:1; +}; + + +} // namespace modsecurity +#endif + + +#endif // HEADERS_MODSECURITY_RULE_WITH_OPERATOR_H_ diff --git a/headers/modsecurity/rules.h b/headers/modsecurity/rules.h index 1e121d13..afdd030c 100644 --- a/headers/modsecurity/rules.h +++ b/headers/modsecurity/rules.h @@ -27,6 +27,8 @@ #endif #include "modsecurity/rule.h" +#include "modsecurity/rule_with_operator.h" +#include "modsecurity/rule_with_actions.h" #ifndef HEADERS_MODSECURITY_RULES_H_ #define HEADERS_MODSECURITY_RULES_H_ diff --git a/src/Makefile.am b/src/Makefile.am index 96eb09cb..df46cbff 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,6 +43,8 @@ pkginclude_HEADERS = \ ../headers/modsecurity/modsecurity.h \ ../headers/modsecurity/rule.h \ ../headers/modsecurity/rule_marker.h \ + ../headers/modsecurity/rule_with_actions.h \ + ../headers/modsecurity/rule_with_operator.h \ ../headers/modsecurity/rules.h \ ../headers/modsecurity/rule_message.h \ ../headers/modsecurity/rules_set.h \ @@ -283,6 +285,8 @@ libmodsecurity_la_SOURCES = \ debug_log/debug_log_writer.cc \ run_time_string.cc \ rule.cc \ + rule_with_actions.cc \ + rule_with_operator.cc \ rule_message.cc \ rule_script.cc \ unique_id.cc \ diff --git a/src/rule.cc b/src/rule.cc index 9e58835b..dc56bda7 100644 --- a/src/rule.cc +++ b/src/rule.cc @@ -41,778 +41,3 @@ #include "src/actions/set_var.h" #include "src/actions/block.h" #include "src/variables/variable.h" - - -namespace modsecurity { - -using operators::Operator; -using actions::Action; -using variables::Variable; -using actions::transformations::None; -using actions::transformations::Transformation; - - -RuleWithActions::RuleWithActions( - Actions *actions, - Transformations *transformations, - std::unique_ptr fileName, - int lineNumber) - : Rule(std::move(fileName), lineNumber), - m_rev(""), - m_ver(""), - m_accuracy(0), - m_maturity(0), - m_ruleId(0), - m_disruptiveAction(nullptr), - m_logData(nullptr), - m_msg(nullptr), - m_severity(nullptr), - m_actionsRuntimePos(), - m_actionsSetVar(), - m_actionsTag(), - m_transformations(transformations != NULL ? *transformations : Transformations()), - m_containsCaptureAction(false), - m_containsMultiMatchAction(false), - m_containsStaticBlockAction(false), - m_isChained(false) { - if (actions) { - for (Action *a : *actions) { - if (a->action_kind == Action::ConfigurationKind) { - a->evaluate(this, NULL); - delete a; - - } else if (a->action_kind == Action::RunTimeOnlyIfMatchKind) { - if (dynamic_cast(a)) { - m_containsCaptureAction = true; - delete a; - } else if (dynamic_cast(a)) { - m_containsMultiMatchAction = true; - delete a; - } else if (dynamic_cast(a)) { - m_severity = dynamic_cast(a); - } else if (dynamic_cast(a)) { - m_logData = dynamic_cast(a); - } else if (dynamic_cast(a)) { - m_msg = dynamic_cast(a); - } else if (dynamic_cast(a)) { - m_actionsSetVar.push_back( - dynamic_cast(a)); - } else if (dynamic_cast(a)) { - m_actionsTag.push_back(dynamic_cast(a)); - } else if (dynamic_cast(a)) { - m_actionsRuntimePos.push_back(a); - m_containsStaticBlockAction = true; - } else if (a->isDisruptive() == true) { - if (m_disruptiveAction != nullptr) { - delete m_disruptiveAction; - m_disruptiveAction = nullptr; - } - m_disruptiveAction = a; - } else { - m_actionsRuntimePos.push_back(a); - } - } else { - delete a; - std::cout << "General failure, action: " << a->m_name; - std::cout << " has an unknown type." << std::endl; - throw; - } - } - delete actions; - } -} - -RuleWithActions::~RuleWithActions() { - if (m_severity) { - delete m_severity; - m_severity = nullptr; - } - if (m_logData) { - delete m_logData; - m_logData = nullptr; - } - if (m_msg) { - delete m_msg; - m_msg = nullptr; - } - while (m_transformations.empty() == false) { - auto *a = m_transformations.back(); - m_transformations.pop_back(); - delete a; - } - while (m_actionsRuntimePos.empty() == false) { - auto *a = m_actionsRuntimePos.back(); - m_actionsRuntimePos.pop_back(); - delete a; - } - while (m_actionsSetVar.empty() == false) { - auto *a = m_actionsSetVar.back(); - m_actionsSetVar.pop_back(); - delete a; - } - while (m_actionsTag.empty() == false) { - auto *a = m_actionsTag.back(); - m_actionsTag.pop_back(); - delete a; - } - if (m_disruptiveAction != nullptr) { - delete m_disruptiveAction; - m_disruptiveAction = nullptr; - } -} - -void RuleWithActions::executeActionsIndependentOfChainedRuleResult(Transaction *trans, - bool *containsBlock, std::shared_ptr ruleMessage) { - - for (actions::SetVar *a : m_actionsSetVar) { - ms_dbg_a(trans, 4, "Running [independent] (non-disruptive) " \ - "action: " + *a->m_name.get()); - - a->evaluate(this, trans); - } - - for (auto &b : - trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { - if (m_ruleId != b.first) { - continue; - } - actions::Action *a = dynamic_cast(b.second.get()); - if (a->isDisruptive() == true && *a->m_name.get() == "block") { - ms_dbg_a(trans, 9, "Rule contains a `block' action"); - *containsBlock = true; - } else if (*a->m_name.get() == "setvar") { - ms_dbg_a(trans, 4, "Running [independent] (non-disruptive) " \ - "action: " + *a->m_name.get()); - a->evaluate(this, trans, ruleMessage); - } - } - - if (m_severity) { - m_severity->evaluate(this, trans, ruleMessage); - } - - if (m_logData) { - m_logData->evaluate(this, trans, ruleMessage); - } - - if (m_msg) { - m_msg->evaluate(this, trans, ruleMessage); - } -} - - -inline void RuleWithActions::executeTransformation( - actions::transformations::Transformation *a, - std::shared_ptr *value, - Transaction *trans, - TransformationResults *ret, - std::string *path, - int *nth) const { - - std::string *oldValue = (*value).get(); - std::string newValue = a->evaluate(*oldValue, trans); - - if (newValue != *oldValue) { - std::shared_ptr u(new std::string(newValue)); - if (m_containsMultiMatchAction) { - ret->push_back(std::make_pair(u, a->m_name)); - (*nth)++; - } - *value = u; - } - - if (path->empty()) { - path->append(*a->m_name.get()); - } else { - path->append("," + *a->m_name.get()); - } - - ms_dbg_a(trans, 9, " T (" + \ - std::to_string(*nth) + ") " + \ - *a->m_name.get() + ": \"" + \ - utils::string::limitTo(80, newValue) +"\""); -} - -void RuleWithActions::executeTransformations( - Transaction *trans, const std::string &in, TransformationResults &ret) { - int none = 0; - int transformations = 0; - std::string path(""); - std::shared_ptr value = - std::shared_ptr(new std::string(in)); - - if (m_containsMultiMatchAction == true) { - /* keep the original value */ - ret.push_back(std::make_pair( - std::shared_ptr(new std::string(*value)), - std::shared_ptr(new std::string(path)))); - } - - for (Action *a : m_transformations) { - if (a->m_isNone) { - none++; - } - } - - // Check for transformations on the SecDefaultAction - // Notice that first we make sure that won't be a t:none - // on the target rule. - if (none == 0) { - for (auto &a : trans->m_rules->m_defaultActions[getPhase()]) { - if (a->action_kind \ - != actions::Action::RunTimeBeforeMatchAttemptKind) { - continue; - } - - // FIXME: here the object needs to be a transformation already. - Transformation *t = dynamic_cast(a.get()); - executeTransformation(t, &value, trans, &ret, &path, - &transformations); - } - } - - for (Transformation *a : m_transformations) { - if (none == 0) { - Transformation *t = dynamic_cast(a); - executeTransformation(t, &value, trans, &ret, &path, - &transformations); - } - if (a->m_isNone) { - none--; - } - } - - // FIXME: It can't be something different from transformation. Sort this - // on rules compile time. - for (auto &b : - trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { - if (m_ruleId != b.first) { - continue; - } - Transformation *a = dynamic_cast(b.second.get()); - if (a->m_isNone) { - none++; - } - } - - for (auto &b : - trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { - if (m_ruleId != b.first) { - continue; - } - Transformation *a = dynamic_cast(b.second.get()); - if (none == 0) { - Transformation *t = dynamic_cast(a); - executeTransformation(t, &value, trans, &ret, &path, - &transformations); - } - if (a->m_isNone) { - none--; - } - } - - if (m_containsMultiMatchAction == true) { - ms_dbg_a(trans, 9, "multiMatch is enabled. " \ - + std::to_string(ret.size()) + \ - " values to be tested."); - } - - if (!m_containsMultiMatchAction) { - ret.push_back(std::make_pair( - std::shared_ptr(new std::string(*value)), - std::shared_ptr(new std::string(path)))); - } -} - -void RuleWithActions::executeActionsAfterFullMatch(Transaction *trans, - bool containsBlock, std::shared_ptr ruleMessage) { - bool disruptiveAlreadyExecuted = false; - - for (auto &a : trans->m_rules->m_defaultActions[getPhase()]) { - if (a.get()->action_kind != actions::Action::RunTimeOnlyIfMatchKind) { - continue; - } - if (!a.get()->isDisruptive()) { - executeAction(trans, containsBlock, ruleMessage, a.get(), true); - } - } - - for (actions::Tag *a : this->m_actionsTag) { - ms_dbg_a(trans, 4, "Running (non-disruptive) action: " \ - + *a->m_name.get()); - a->evaluate(this, trans, ruleMessage); - } - - for (auto &b : - trans->m_rules->m_exceptions.m_action_pos_update_target_by_id) { - if (m_ruleId != b.first) { - continue; - } - actions::Action *a = dynamic_cast(b.second.get()); - executeAction(trans, containsBlock, ruleMessage, a, false); - disruptiveAlreadyExecuted = true; - } - for (Action *a : this->m_actionsRuntimePos) { - if (!a->isDisruptive() - && !(disruptiveAlreadyExecuted - && dynamic_cast(a))) { - executeAction(trans, containsBlock, ruleMessage, a, false); - } - } - if (!disruptiveAlreadyExecuted && m_disruptiveAction != nullptr) { - executeAction(trans, containsBlock, ruleMessage, - m_disruptiveAction, false); - } -} - -bool RuleWithActions::containsTag(const std::string& name, Transaction *t) { - for (auto &tag : m_actionsTag) { - if (tag != NULL && tag->getName(t) == name) { - return true; - } - } - return false; -} - - -bool RuleWithActions::containsMsg(const std::string& name, Transaction *t) { - return m_msg && m_msg->data(t) == name; -} - -std::string RuleWithActions::logData(Transaction *t) { return m_logData->data(t); } -std::string RuleWithActions::msg(Transaction *t) { return m_msg->data(t); } -int RuleWithActions::severity() const { return m_severity->m_severity; } - - -RuleWithOperator::RuleWithOperator(Operator *op, - variables::Variables *_variables, - std::vector *actions, - Transformations *transformations, - std::unique_ptr fileName, - int lineNumber) - : RuleWithActions(actions, transformations, std::move(fileName), lineNumber), - m_chainedRuleChild(nullptr), - m_chainedRuleParent(NULL), - - m_operator(op), - m_variables(_variables), - m_unconditional(false) { /* */ } - - -RuleWithOperator::~RuleWithOperator() { - if (m_operator != NULL) { - delete m_operator; - } - - while (m_variables != NULL && m_variables->empty() == false) { - auto *a = m_variables->back(); - m_variables->pop_back(); - delete a; - } - - if (m_variables != NULL) { - delete m_variables; - } -} - - -void RuleWithOperator::updateMatchedVars(Transaction *trans, const std::string &key, - const std::string &value) { - ms_dbg_a(trans, 9, "Matched vars updated."); - trans->m_variableMatchedVar.set(value, trans->m_variableOffset); - trans->m_variableMatchedVarName.set(key, trans->m_variableOffset); - - trans->m_variableMatchedVars.set(key, value, trans->m_variableOffset); - trans->m_variableMatchedVarsNames.set(key, key, trans->m_variableOffset); -} - - -void RuleWithOperator::cleanMatchedVars(Transaction *trans) { - ms_dbg_a(trans, 9, "Matched vars cleaned."); - trans->m_variableMatchedVar.unset(); - trans->m_variableMatchedVars.unset(); - trans->m_variableMatchedVarName.unset(); - trans->m_variableMatchedVarsNames.unset(); -} - - - -bool RuleWithOperator::executeOperatorAt(Transaction *trans, const std::string &key, - std::string value, std::shared_ptr ruleMessage) { -#if MSC_EXEC_CLOCK_ENABLED - clock_t begin = clock(); - clock_t end; - double elapsed_s = 0; -#endif - bool ret; - - ms_dbg_a(trans, 9, "Target value: \"" + utils::string::limitTo(80, - utils::string::toHexIfNeeded(value)) \ - + "\" (Variable: " + key + ")"); - - ret = this->m_operator->evaluateInternal(trans, this, value, ruleMessage); - if (ret == false) { - return false; - } - -#if MSC_EXEC_CLOCK_ENABLED - end = clock(); - elapsed_s = static_cast(end - begin) / CLOCKS_PER_SEC; - - ms_dbg_a(trans, 5, "Operator completed in " + \ - std::to_string(elapsed_s) + " seconds"); -#endif - return ret; -} - - -void RuleWithOperator::getVariablesExceptions(Transaction *t, - variables::Variables *exclusion, variables::Variables *addition) { - for (auto &a : t->m_rules->m_exceptions.m_variable_update_target_by_tag) { - if (containsTag(*a.first.get(), t) == false) { - continue; - } - Variable *b = a.second.get(); - if (dynamic_cast(b)) { - exclusion->push_back( - dynamic_cast( - b)->m_base.get()); - } else { - addition->push_back(b); - } - } - - for (auto &a : t->m_rules->m_exceptions.m_variable_update_target_by_msg) { - if (containsMsg(*a.first.get(), t) == false) { - continue; - } - Variable *b = a.second.get(); - if (dynamic_cast(b)) { - exclusion->push_back( - dynamic_cast( - b)->m_base.get()); - } else { - addition->push_back(b); - } - } - - for (auto &a : t->m_rules->m_exceptions.m_variable_update_target_by_id) { - if (m_ruleId != a.first) { - continue; - } - Variable *b = a.second.get(); - if (dynamic_cast(b)) { - exclusion->push_back( - dynamic_cast( - b)->m_base.get()); - } else { - addition->push_back(b); - } - } -} - - -inline void RuleWithOperator::getFinalVars(variables::Variables *vars, - variables::Variables *exclusion, Transaction *trans) { - variables::Variables addition; - getVariablesExceptions(trans, exclusion, &addition); - - for (int i = 0; i < m_variables->size(); i++) { - Variable *variable = m_variables->at(i); - if (exclusion->contains(variable)) { - continue; - } - if (std::find_if(trans->m_ruleRemoveTargetById.begin(), - trans->m_ruleRemoveTargetById.end(), - [&, variable, this](std::pair &m) -> bool { - return m.first == m_ruleId - && m.second == *variable->m_fullName.get(); - }) != trans->m_ruleRemoveTargetById.end()) { - continue; - } - if (std::find_if(trans->m_ruleRemoveTargetByTag.begin(), - trans->m_ruleRemoveTargetByTag.end(), - [&, variable, trans, this]( - std::pair &m) -> bool { - return containsTag(m.first, trans) - && m.second == *variable->m_fullName.get(); - }) != trans->m_ruleRemoveTargetByTag.end()) { - continue; - } - vars->push_back(variable); - } - - for (int i = 0; i < addition.size(); i++) { - Variable *variable = addition.at(i); - vars->push_back(variable); - } -} - - - -void RuleWithActions::executeAction(Transaction *trans, - bool containsBlock, std::shared_ptr ruleMessage, - Action *a, bool defaultContext) { - if (a->isDisruptive() == false && *a->m_name.get() != "block") { - ms_dbg_a(trans, 9, "Running " \ - "action: " + *a->m_name.get()); - a->evaluate(this, trans, ruleMessage); - return; - } - - if (defaultContext && !containsBlock) { - ms_dbg_a(trans, 4, "Ignoring action: " + *a->m_name.get() + \ - " (rule does not cotains block)"); - return; - } - - if (trans->getRuleEngineState() == RulesSet::EnabledRuleEngine) { - ms_dbg_a(trans, 4, "Running (disruptive) action: " + *a->m_name.get() + \ - "."); - a->evaluate(this, trans, ruleMessage); - return; - } - - ms_dbg_a(trans, 4, "Not running any disruptive action (or block): " \ - + *a->m_name.get() + ". SecRuleEngine is not On."); -} - - -bool RuleWithOperator::evaluate(Transaction *trans, - std::shared_ptr ruleMessage) { - bool globalRet = false; - variables::Variables *variables = this->m_variables; - bool recursiveGlobalRet; - bool containsBlock = hasBlockAction(); - std::string eparam; - variables::Variables vars; - vars.reserve(4); - variables::Variables exclusion; - - if (ruleMessage == NULL) { - ruleMessage = std::shared_ptr( - new RuleMessage(this, trans)); - } - - trans->m_matched.clear(); - - if (isMarker() == true) { - return true; - } - - if (isUnconditional() == true) { - ms_dbg_a(trans, 4, "(Rule: " + std::to_string(m_ruleId) \ - + ") Executing unconditional rule..."); - executeActionsIndependentOfChainedRuleResult(trans, - &containsBlock, ruleMessage); - goto end_exec; - } - - for (auto &i : trans->m_ruleRemoveById) { - if (m_ruleId != i) { - continue; - } - ms_dbg_a(trans, 9, "Rule id: " + std::to_string(m_ruleId) + - " was skipped due to a ruleRemoveById action..."); - return true; - } - for (auto &i : trans->m_ruleRemoveByIdRange) { - if (!(i.first <= m_ruleId && i.second >= m_ruleId)) { - continue; - } - ms_dbg_a(trans, 9, "Rule id: " + std::to_string(m_ruleId) + - " was skipped due to a ruleRemoveById action..."); - return true; - } - - if (m_operator->m_string) { - eparam = m_operator->m_string->evaluate(trans); - - if (m_operator->m_string->containsMacro()) { - eparam = "\"" + eparam + "\" Was: \"" \ - + m_operator->m_string->evaluate(NULL) + "\""; - } else { - eparam = "\"" + eparam + "\""; - } - ms_dbg_a(trans, 4, "(Rule: " + std::to_string(m_ruleId) \ - + ") Executing operator \"" + getOperatorName() \ - + "\" with param " \ - + eparam \ - + " against " \ - + variables + "."); - } else { - ms_dbg_a(trans, 4, "(Rule: " + std::to_string(m_ruleId) \ - + ") Executing operator \"" + getOperatorName() \ - + " against " \ - + variables + "."); - } - - getFinalVars(&vars, &exclusion, trans); - - for (auto &var : vars) { - std::vector e; - if (!var) { - continue; - } - var->evaluate(trans, this, &e); - for (const VariableValue *v : e) { - const std::string &value = v->getValue(); - const std::string &key = v->getKeyWithCollection(); - - if (exclusion.contains(v) || - std::find_if(trans->m_ruleRemoveTargetById.begin(), - trans->m_ruleRemoveTargetById.end(), - [&, v, this](std::pair &m) -> bool { - return m.first == m_ruleId && m.second == v->getKeyWithCollection(); - }) != trans->m_ruleRemoveTargetById.end() - ) { - delete v; - v = NULL; - continue; - } - if (exclusion.contains(v) || - std::find_if(trans->m_ruleRemoveTargetByTag.begin(), - trans->m_ruleRemoveTargetByTag.end(), - [&, v, trans, this](std::pair &m) -> bool { - return containsTag(m.first, trans) && m.second == v->getKeyWithCollection(); - }) != trans->m_ruleRemoveTargetByTag.end() - ) { - delete v; - v = NULL; - continue; - } - - TransformationResults values; - - executeTransformations(trans, value, values); - - for (const auto &valueTemp : values) { - bool ret; - std::string valueAfterTrans = std::move(*valueTemp.first); - - ret = executeOperatorAt(trans, key, valueAfterTrans, ruleMessage); - - if (ret == true) { - ruleMessage->m_match = m_operator->resolveMatchMessage(trans, - key, value); - for (auto &i : v->getOrigin()) { - ruleMessage->m_reference.append(i->toText()); - } - - ruleMessage->m_reference.append(*valueTemp.second); - updateMatchedVars(trans, key, valueAfterTrans); - executeActionsIndependentOfChainedRuleResult(trans, - &containsBlock, ruleMessage); - - bool isItToBeLogged = ruleMessage->m_saveMessage; - if (hasMultimatch() && isItToBeLogged) { - /* warn */ - trans->m_rulesMessages.push_back(*ruleMessage); - - /* error */ - if (!ruleMessage->m_isDisruptive) { - trans->serverLog(ruleMessage); - } - - RuleMessage *rm = new RuleMessage(this, trans); - rm->m_saveMessage = ruleMessage->m_saveMessage; - ruleMessage.reset(rm); - } - - globalRet = true; - } - } - delete v; - v = NULL; - } - e.clear(); - e.reserve(4); - } - - if (globalRet == false) { - ms_dbg_a(trans, 4, "Rule returned 0."); - cleanMatchedVars(trans); - goto end_clean; - } - ms_dbg_a(trans, 4, "Rule returned 1."); - - if (this->isChained() == false) { - goto end_exec; - } - - /* FIXME: this check should happens on the parser. */ - if (this->m_chainedRuleChild == nullptr) { - ms_dbg_a(trans, 4, "Rule is marked as chained but there " \ - "isn't a subsequent rule."); - goto end_clean; - } - - ms_dbg_a(trans, 4, "Executing chained rule."); - recursiveGlobalRet = m_chainedRuleChild->evaluate(trans, ruleMessage); - - if (recursiveGlobalRet == true) { - goto end_exec; - } - -end_clean: - return false; - -end_exec: - executeActionsAfterFullMatch(trans, containsBlock, ruleMessage); - - /* last rule in the chain. */ - bool isItToBeLogged = (ruleMessage->m_saveMessage && (m_chainedRuleParent == nullptr)); - if (isItToBeLogged && !hasMultimatch()) { - /* warn */ - trans->m_rulesMessages.push_back(*ruleMessage); - - /* error */ - if (!ruleMessage->m_isDisruptive) { - trans->serverLog(ruleMessage); - } - } - return true; -} - - -std::vector RuleWithActions::getActionsByName(const std::string& name, - Transaction *trans) { - std::vector ret; - for (auto &z : m_actionsRuntimePos) { - if (*z->m_name.get() == name) { - ret.push_back(z); - } - } - for (auto &z : m_transformations) { - if (*z->m_name.get() == name) { - ret.push_back(z); - } - } - for (auto &b : - trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { - if (m_ruleId != b.first) { - continue; - } - actions::Action *z = dynamic_cast(b.second.get()); - if (*z->m_name.get() == name) { - ret.push_back(z); - } - } - for (auto &b : - trans->m_rules->m_exceptions.m_action_pos_update_target_by_id) { - if (m_ruleId != b.first) { - continue; - } - actions::Action *z = dynamic_cast(b.second.get()); - if (*z->m_name.get() == name) { - ret.push_back(z); - } - } - return ret; -} - - -std::string RuleWithOperator::getOperatorName() const { return m_operator->m_op; } - - -} // namespace modsecurity diff --git a/src/rule_with_actions.cc b/src/rule_with_actions.cc new file mode 100644 index 00000000..4b9cf924 --- /dev/null +++ b/src/rule_with_actions.cc @@ -0,0 +1,389 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#include "modsecurity/rule.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "modsecurity/rules_set.h" +#include "src/operators/operator.h" +#include "modsecurity/actions/action.h" +#include "modsecurity/modsecurity.h" +#include "src/actions/transformations/none.h" +#include "src/actions/tag.h" +#include "src/utils/string.h" +#include "modsecurity/rule_message.h" +#include "modsecurity/rule_with_actions.h" +#include "src/actions/msg.h" +#include "src/actions/log_data.h" +#include "src/actions/severity.h" +#include "src/actions/capture.h" +#include "src/actions/multi_match.h" +#include "src/actions/set_var.h" +#include "src/actions/block.h" +#include "src/variables/variable.h" + + +namespace modsecurity { + +using operators::Operator; +using actions::Action; +using variables::Variable; +using actions::transformations::None; +using actions::transformations::Transformation; + + + +RuleWithActions::RuleWithActions( + Actions *actions, + Transformations *transformations, + std::unique_ptr fileName, + int lineNumber) + : Rule(std::move(fileName), lineNumber), + m_rev(""), + m_ver(""), + m_accuracy(0), + m_maturity(0), + m_ruleId(0), + m_disruptiveAction(nullptr), + m_logData(nullptr), + m_msg(nullptr), + m_severity(nullptr), + m_actionsRuntimePos(), + m_actionsSetVar(), + m_actionsTag(), + m_transformations(transformations != NULL ? *transformations : Transformations()), + m_containsCaptureAction(false), + m_containsMultiMatchAction(false), + m_containsStaticBlockAction(false), + m_isChained(false) { + if (actions) { + for (Action *a : *actions) { + if (a->action_kind == Action::ConfigurationKind) { + a->evaluate(this, NULL); + delete a; + + } else if (a->action_kind == Action::RunTimeOnlyIfMatchKind) { + if (dynamic_cast(a)) { + m_containsCaptureAction = true; + delete a; + } else if (dynamic_cast(a)) { + m_containsMultiMatchAction = true; + delete a; + } else if (dynamic_cast(a)) { + m_severity = dynamic_cast(a); + } else if (dynamic_cast(a)) { + m_logData = dynamic_cast(a); + } else if (dynamic_cast(a)) { + m_msg = dynamic_cast(a); + } else if (dynamic_cast(a)) { + m_actionsSetVar.push_back( + dynamic_cast(a)); + } else if (dynamic_cast(a)) { + m_actionsTag.push_back(dynamic_cast(a)); + } else if (dynamic_cast(a)) { + m_actionsRuntimePos.push_back(a); + m_containsStaticBlockAction = true; + } else if (a->isDisruptive() == true) { + if (m_disruptiveAction != nullptr) { + delete m_disruptiveAction; + m_disruptiveAction = nullptr; + } + m_disruptiveAction = a; + } else { + m_actionsRuntimePos.push_back(a); + } + } else { + delete a; + std::cout << "General failure, action: " << a->m_name; + std::cout << " has an unknown type." << std::endl; + throw; + } + } + delete actions; + } +} + +RuleWithActions::~RuleWithActions() { + if (m_severity) { + delete m_severity; + m_severity = nullptr; + } + if (m_logData) { + delete m_logData; + m_logData = nullptr; + } + if (m_msg) { + delete m_msg; + m_msg = nullptr; + } + while (m_transformations.empty() == false) { + auto *a = m_transformations.back(); + m_transformations.pop_back(); + delete a; + } + while (m_actionsRuntimePos.empty() == false) { + auto *a = m_actionsRuntimePos.back(); + m_actionsRuntimePos.pop_back(); + delete a; + } + while (m_actionsSetVar.empty() == false) { + auto *a = m_actionsSetVar.back(); + m_actionsSetVar.pop_back(); + delete a; + } + while (m_actionsTag.empty() == false) { + auto *a = m_actionsTag.back(); + m_actionsTag.pop_back(); + delete a; + } + if (m_disruptiveAction != nullptr) { + delete m_disruptiveAction; + m_disruptiveAction = nullptr; + } +} + +void RuleWithActions::executeActionsIndependentOfChainedRuleResult(Transaction *trans, + bool *containsBlock, std::shared_ptr ruleMessage) { + + for (actions::SetVar *a : m_actionsSetVar) { + ms_dbg_a(trans, 4, "Running [independent] (non-disruptive) " \ + "action: " + *a->m_name.get()); + + a->evaluate(this, trans); + } + + for (auto &b : + trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { + if (m_ruleId != b.first) { + continue; + } + actions::Action *a = dynamic_cast(b.second.get()); + if (a->isDisruptive() == true && *a->m_name.get() == "block") { + ms_dbg_a(trans, 9, "Rule contains a `block' action"); + *containsBlock = true; + } else if (*a->m_name.get() == "setvar") { + ms_dbg_a(trans, 4, "Running [independent] (non-disruptive) " \ + "action: " + *a->m_name.get()); + a->evaluate(this, trans, ruleMessage); + } + } + + if (m_severity) { + m_severity->evaluate(this, trans, ruleMessage); + } + + if (m_logData) { + m_logData->evaluate(this, trans, ruleMessage); + } + + if (m_msg) { + m_msg->evaluate(this, trans, ruleMessage); + } +} + + +inline void RuleWithActions::executeTransformation( + actions::transformations::Transformation *a, + std::shared_ptr *value, + Transaction *trans, + TransformationResults *ret, + std::string *path, + int *nth) const { + + std::string *oldValue = (*value).get(); + std::string newValue = a->evaluate(*oldValue, trans); + + if (newValue != *oldValue) { + std::shared_ptr u(new std::string(newValue)); + if (m_containsMultiMatchAction) { + ret->push_back(std::make_pair(u, a->m_name)); + (*nth)++; + } + *value = u; + } + + if (path->empty()) { + path->append(*a->m_name.get()); + } else { + path->append("," + *a->m_name.get()); + } + + ms_dbg_a(trans, 9, " T (" + \ + std::to_string(*nth) + ") " + \ + *a->m_name.get() + ": \"" + \ + utils::string::limitTo(80, newValue) +"\""); +} + +void RuleWithActions::executeTransformations( + Transaction *trans, const std::string &in, TransformationResults &ret) { + int none = 0; + int transformations = 0; + std::string path(""); + std::shared_ptr value = + std::shared_ptr(new std::string(in)); + + if (m_containsMultiMatchAction == true) { + /* keep the original value */ + ret.push_back(std::make_pair( + std::shared_ptr(new std::string(*value)), + std::shared_ptr(new std::string(path)))); + } + + for (Action *a : m_transformations) { + if (a->m_isNone) { + none++; + } + } + + // Check for transformations on the SecDefaultAction + // Notice that first we make sure that won't be a t:none + // on the target rule. + if (none == 0) { + for (auto &a : trans->m_rules->m_defaultActions[getPhase()]) { + if (a->action_kind \ + != actions::Action::RunTimeBeforeMatchAttemptKind) { + continue; + } + + // FIXME: here the object needs to be a transformation already. + Transformation *t = dynamic_cast(a.get()); + executeTransformation(t, &value, trans, &ret, &path, + &transformations); + } + } + + for (Transformation *a : m_transformations) { + if (none == 0) { + Transformation *t = dynamic_cast(a); + executeTransformation(t, &value, trans, &ret, &path, + &transformations); + } + if (a->m_isNone) { + none--; + } + } + + // FIXME: It can't be something different from transformation. Sort this + // on rules compile time. + for (auto &b : + trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { + if (m_ruleId != b.first) { + continue; + } + Transformation *a = dynamic_cast(b.second.get()); + if (a->m_isNone) { + none++; + } + } + + for (auto &b : + trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { + if (m_ruleId != b.first) { + continue; + } + Transformation *a = dynamic_cast(b.second.get()); + if (none == 0) { + Transformation *t = dynamic_cast(a); + executeTransformation(t, &value, trans, &ret, &path, + &transformations); + } + if (a->m_isNone) { + none--; + } + } + + if (m_containsMultiMatchAction == true) { + ms_dbg_a(trans, 9, "multiMatch is enabled. " \ + + std::to_string(ret.size()) + \ + " values to be tested."); + } + + if (!m_containsMultiMatchAction) { + ret.push_back(std::make_pair( + std::shared_ptr(new std::string(*value)), + std::shared_ptr(new std::string(path)))); + } +} + +void RuleWithActions::executeActionsAfterFullMatch(Transaction *trans, + bool containsBlock, std::shared_ptr ruleMessage) { + bool disruptiveAlreadyExecuted = false; + + for (auto &a : trans->m_rules->m_defaultActions[getPhase()]) { + if (a.get()->action_kind != actions::Action::RunTimeOnlyIfMatchKind) { + continue; + } + if (!a.get()->isDisruptive()) { + executeAction(trans, containsBlock, ruleMessage, a.get(), true); + } + } + + for (actions::Tag *a : this->m_actionsTag) { + ms_dbg_a(trans, 4, "Running (non-disruptive) action: " \ + + *a->m_name.get()); + a->evaluate(this, trans, ruleMessage); + } + + for (auto &b : + trans->m_rules->m_exceptions.m_action_pos_update_target_by_id) { + if (m_ruleId != b.first) { + continue; + } + actions::Action *a = dynamic_cast(b.second.get()); + executeAction(trans, containsBlock, ruleMessage, a, false); + disruptiveAlreadyExecuted = true; + } + for (Action *a : this->m_actionsRuntimePos) { + if (!a->isDisruptive() + && !(disruptiveAlreadyExecuted + && dynamic_cast(a))) { + executeAction(trans, containsBlock, ruleMessage, a, false); + } + } + if (!disruptiveAlreadyExecuted && m_disruptiveAction != nullptr) { + executeAction(trans, containsBlock, ruleMessage, + m_disruptiveAction, false); + } +} + +bool RuleWithActions::containsTag(const std::string& name, Transaction *t) { + for (auto &tag : m_actionsTag) { + if (tag != NULL && tag->getName(t) == name) { + return true; + } + } + return false; +} + + +bool RuleWithActions::containsMsg(const std::string& name, Transaction *t) { + return m_msg && m_msg->data(t) == name; +} + +std::string RuleWithActions::logData(Transaction *t) { return m_logData->data(t); } +std::string RuleWithActions::msg(Transaction *t) { return m_msg->data(t); } +int RuleWithActions::severity() const { return m_severity->m_severity; } + + +} // namespace modsecurity diff --git a/src/rule_with_operator.cc b/src/rule_with_operator.cc new file mode 100644 index 00000000..e70d224c --- /dev/null +++ b/src/rule_with_operator.cc @@ -0,0 +1,485 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#include "modsecurity/rule_with_operator.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "modsecurity/rules_set.h" +#include "src/operators/operator.h" +#include "modsecurity/actions/action.h" +#include "modsecurity/modsecurity.h" +#include "src/actions/transformations/none.h" +#include "src/actions/tag.h" +#include "src/utils/string.h" +#include "modsecurity/rule_message.h" +#include "src/actions/msg.h" +#include "src/actions/log_data.h" +#include "src/actions/severity.h" +#include "src/actions/capture.h" +#include "src/actions/multi_match.h" +#include "src/actions/set_var.h" +#include "src/actions/block.h" +#include "src/variables/variable.h" + + +namespace modsecurity { + +using operators::Operator; +using actions::Action; +using variables::Variable; +using actions::transformations::None; + + +RuleWithOperator::RuleWithOperator(Operator *op, + variables::Variables *_variables, + std::vector *actions, + Transformations *transformations, + std::unique_ptr fileName, + int lineNumber) + : RuleWithActions(actions, transformations, std::move(fileName), lineNumber), + m_chainedRuleChild(nullptr), + m_chainedRuleParent(NULL), + + m_operator(op), + m_variables(_variables), + m_unconditional(false) { /* */ } + + +RuleWithOperator::~RuleWithOperator() { + if (m_operator != NULL) { + delete m_operator; + } + + while (m_variables != NULL && m_variables->empty() == false) { + auto *a = m_variables->back(); + m_variables->pop_back(); + delete a; + } + + if (m_variables != NULL) { + delete m_variables; + } +} + + +void RuleWithOperator::updateMatchedVars(Transaction *trans, const std::string &key, + const std::string &value) { + ms_dbg_a(trans, 9, "Matched vars updated."); + trans->m_variableMatchedVar.set(value, trans->m_variableOffset); + trans->m_variableMatchedVarName.set(key, trans->m_variableOffset); + + trans->m_variableMatchedVars.set(key, value, trans->m_variableOffset); + trans->m_variableMatchedVarsNames.set(key, key, trans->m_variableOffset); +} + + +void RuleWithOperator::cleanMatchedVars(Transaction *trans) { + ms_dbg_a(trans, 9, "Matched vars cleaned."); + trans->m_variableMatchedVar.unset(); + trans->m_variableMatchedVars.unset(); + trans->m_variableMatchedVarName.unset(); + trans->m_variableMatchedVarsNames.unset(); +} + + + +bool RuleWithOperator::executeOperatorAt(Transaction *trans, const std::string &key, + std::string value, std::shared_ptr ruleMessage) { +#if MSC_EXEC_CLOCK_ENABLED + clock_t begin = clock(); + clock_t end; + double elapsed_s = 0; +#endif + bool ret; + + ms_dbg_a(trans, 9, "Target value: \"" + utils::string::limitTo(80, + utils::string::toHexIfNeeded(value)) \ + + "\" (Variable: " + key + ")"); + + ret = this->m_operator->evaluateInternal(trans, this, value, ruleMessage); + if (ret == false) { + return false; + } + +#if MSC_EXEC_CLOCK_ENABLED + end = clock(); + elapsed_s = static_cast(end - begin) / CLOCKS_PER_SEC; + + ms_dbg_a(trans, 5, "Operator completed in " + \ + std::to_string(elapsed_s) + " seconds"); +#endif + return ret; +} + + +void RuleWithOperator::getVariablesExceptions(Transaction *t, + variables::Variables *exclusion, variables::Variables *addition) { + for (auto &a : t->m_rules->m_exceptions.m_variable_update_target_by_tag) { + if (containsTag(*a.first.get(), t) == false) { + continue; + } + Variable *b = a.second.get(); + if (dynamic_cast(b)) { + exclusion->push_back( + dynamic_cast( + b)->m_base.get()); + } else { + addition->push_back(b); + } + } + + for (auto &a : t->m_rules->m_exceptions.m_variable_update_target_by_msg) { + if (containsMsg(*a.first.get(), t) == false) { + continue; + } + Variable *b = a.second.get(); + if (dynamic_cast(b)) { + exclusion->push_back( + dynamic_cast( + b)->m_base.get()); + } else { + addition->push_back(b); + } + } + + for (auto &a : t->m_rules->m_exceptions.m_variable_update_target_by_id) { + if (m_ruleId != a.first) { + continue; + } + Variable *b = a.second.get(); + if (dynamic_cast(b)) { + exclusion->push_back( + dynamic_cast( + b)->m_base.get()); + } else { + addition->push_back(b); + } + } +} + + +inline void RuleWithOperator::getFinalVars(variables::Variables *vars, + variables::Variables *exclusion, Transaction *trans) { + variables::Variables addition; + getVariablesExceptions(trans, exclusion, &addition); + + for (int i = 0; i < m_variables->size(); i++) { + Variable *variable = m_variables->at(i); + if (exclusion->contains(variable)) { + continue; + } + if (std::find_if(trans->m_ruleRemoveTargetById.begin(), + trans->m_ruleRemoveTargetById.end(), + [&, variable, this](std::pair &m) -> bool { + return m.first == m_ruleId + && m.second == *variable->m_fullName.get(); + }) != trans->m_ruleRemoveTargetById.end()) { + continue; + } + if (std::find_if(trans->m_ruleRemoveTargetByTag.begin(), + trans->m_ruleRemoveTargetByTag.end(), + [&, variable, trans, this]( + std::pair &m) -> bool { + return containsTag(m.first, trans) + && m.second == *variable->m_fullName.get(); + }) != trans->m_ruleRemoveTargetByTag.end()) { + continue; + } + vars->push_back(variable); + } + + for (int i = 0; i < addition.size(); i++) { + Variable *variable = addition.at(i); + vars->push_back(variable); + } +} + + + +void RuleWithActions::executeAction(Transaction *trans, + bool containsBlock, std::shared_ptr ruleMessage, + Action *a, bool defaultContext) { + if (a->isDisruptive() == false && *a->m_name.get() != "block") { + ms_dbg_a(trans, 9, "Running " \ + "action: " + *a->m_name.get()); + a->evaluate(this, trans, ruleMessage); + return; + } + + if (defaultContext && !containsBlock) { + ms_dbg_a(trans, 4, "Ignoring action: " + *a->m_name.get() + \ + " (rule does not cotains block)"); + return; + } + + if (trans->getRuleEngineState() == RulesSet::EnabledRuleEngine) { + ms_dbg_a(trans, 4, "Running (disruptive) action: " + *a->m_name.get() + \ + "."); + a->evaluate(this, trans, ruleMessage); + return; + } + + ms_dbg_a(trans, 4, "Not running any disruptive action (or block): " \ + + *a->m_name.get() + ". SecRuleEngine is not On."); +} + + +bool RuleWithOperator::evaluate(Transaction *trans, + std::shared_ptr ruleMessage) { + bool globalRet = false; + variables::Variables *variables = this->m_variables; + bool recursiveGlobalRet; + bool containsBlock = hasBlockAction(); + std::string eparam; + variables::Variables vars; + vars.reserve(4); + variables::Variables exclusion; + + if (ruleMessage == NULL) { + ruleMessage = std::shared_ptr( + new RuleMessage(this, trans)); + } + + trans->m_matched.clear(); + + if (isMarker() == true) { + return true; + } + + if (isUnconditional() == true) { + ms_dbg_a(trans, 4, "(Rule: " + std::to_string(m_ruleId) \ + + ") Executing unconditional rule..."); + executeActionsIndependentOfChainedRuleResult(trans, + &containsBlock, ruleMessage); + goto end_exec; + } + + for (auto &i : trans->m_ruleRemoveById) { + if (m_ruleId != i) { + continue; + } + ms_dbg_a(trans, 9, "Rule id: " + std::to_string(m_ruleId) + + " was skipped due to a ruleRemoveById action..."); + return true; + } + for (auto &i : trans->m_ruleRemoveByIdRange) { + if (!(i.first <= m_ruleId && i.second >= m_ruleId)) { + continue; + } + ms_dbg_a(trans, 9, "Rule id: " + std::to_string(m_ruleId) + + " was skipped due to a ruleRemoveById action..."); + return true; + } + + if (m_operator->m_string) { + eparam = m_operator->m_string->evaluate(trans); + + if (m_operator->m_string->containsMacro()) { + eparam = "\"" + eparam + "\" Was: \"" \ + + m_operator->m_string->evaluate(NULL) + "\""; + } else { + eparam = "\"" + eparam + "\""; + } + ms_dbg_a(trans, 4, "(Rule: " + std::to_string(m_ruleId) \ + + ") Executing operator \"" + getOperatorName() \ + + "\" with param " \ + + eparam \ + + " against " \ + + variables + "."); + } else { + ms_dbg_a(trans, 4, "(Rule: " + std::to_string(m_ruleId) \ + + ") Executing operator \"" + getOperatorName() \ + + " against " \ + + variables + "."); + } + + getFinalVars(&vars, &exclusion, trans); + + for (auto &var : vars) { + std::vector e; + if (!var) { + continue; + } + var->evaluate(trans, this, &e); + for (const VariableValue *v : e) { + const std::string &value = v->getValue(); + const std::string &key = v->getKeyWithCollection(); + + if (exclusion.contains(v) || + std::find_if(trans->m_ruleRemoveTargetById.begin(), + trans->m_ruleRemoveTargetById.end(), + [&, v, this](std::pair &m) -> bool { + return m.first == m_ruleId && m.second == v->getKeyWithCollection(); + }) != trans->m_ruleRemoveTargetById.end() + ) { + delete v; + v = NULL; + continue; + } + if (exclusion.contains(v) || + std::find_if(trans->m_ruleRemoveTargetByTag.begin(), + trans->m_ruleRemoveTargetByTag.end(), + [&, v, trans, this](std::pair &m) -> bool { + return containsTag(m.first, trans) && m.second == v->getKeyWithCollection(); + }) != trans->m_ruleRemoveTargetByTag.end() + ) { + delete v; + v = NULL; + continue; + } + + TransformationResults values; + + executeTransformations(trans, value, values); + + for (const auto &valueTemp : values) { + bool ret; + std::string valueAfterTrans = std::move(*valueTemp.first); + + ret = executeOperatorAt(trans, key, valueAfterTrans, ruleMessage); + + if (ret == true) { + ruleMessage->m_match = m_operator->resolveMatchMessage(trans, + key, value); + for (auto &i : v->getOrigin()) { + ruleMessage->m_reference.append(i->toText()); + } + + ruleMessage->m_reference.append(*valueTemp.second); + updateMatchedVars(trans, key, valueAfterTrans); + executeActionsIndependentOfChainedRuleResult(trans, + &containsBlock, ruleMessage); + + bool isItToBeLogged = ruleMessage->m_saveMessage; + if (hasMultimatch() && isItToBeLogged) { + /* warn */ + trans->m_rulesMessages.push_back(*ruleMessage); + + /* error */ + if (!ruleMessage->m_isDisruptive) { + trans->serverLog(ruleMessage); + } + + RuleMessage *rm = new RuleMessage(this, trans); + rm->m_saveMessage = ruleMessage->m_saveMessage; + ruleMessage.reset(rm); + } + + globalRet = true; + } + } + delete v; + v = NULL; + } + e.clear(); + e.reserve(4); + } + + if (globalRet == false) { + ms_dbg_a(trans, 4, "Rule returned 0."); + cleanMatchedVars(trans); + goto end_clean; + } + ms_dbg_a(trans, 4, "Rule returned 1."); + + if (this->isChained() == false) { + goto end_exec; + } + + /* FIXME: this check should happens on the parser. */ + if (this->m_chainedRuleChild == nullptr) { + ms_dbg_a(trans, 4, "Rule is marked as chained but there " \ + "isn't a subsequent rule."); + goto end_clean; + } + + ms_dbg_a(trans, 4, "Executing chained rule."); + recursiveGlobalRet = m_chainedRuleChild->evaluate(trans, ruleMessage); + + if (recursiveGlobalRet == true) { + goto end_exec; + } + +end_clean: + return false; + +end_exec: + executeActionsAfterFullMatch(trans, containsBlock, ruleMessage); + + /* last rule in the chain. */ + bool isItToBeLogged = (ruleMessage->m_saveMessage && (m_chainedRuleParent == nullptr)); + if (isItToBeLogged && !hasMultimatch()) { + /* warn */ + trans->m_rulesMessages.push_back(*ruleMessage); + + /* error */ + if (!ruleMessage->m_isDisruptive) { + trans->serverLog(ruleMessage); + } + } + return true; +} + + +std::vector RuleWithActions::getActionsByName(const std::string& name, + Transaction *trans) { + std::vector ret; + for (auto &z : m_actionsRuntimePos) { + if (*z->m_name.get() == name) { + ret.push_back(z); + } + } + for (auto &z : m_transformations) { + if (*z->m_name.get() == name) { + ret.push_back(z); + } + } + for (auto &b : + trans->m_rules->m_exceptions.m_action_pre_update_target_by_id) { + if (m_ruleId != b.first) { + continue; + } + actions::Action *z = dynamic_cast(b.second.get()); + if (*z->m_name.get() == name) { + ret.push_back(z); + } + } + for (auto &b : + trans->m_rules->m_exceptions.m_action_pos_update_target_by_id) { + if (m_ruleId != b.first) { + continue; + } + actions::Action *z = dynamic_cast(b.second.get()); + if (*z->m_name.get() == name) { + ret.push_back(z); + } + } + return ret; +} + + +std::string RuleWithOperator::getOperatorName() const { return m_operator->m_op; } + + +} // namespace modsecurity