/* * 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/accuracy.h" #include "src/actions/maturity.h" #include "src/actions/msg.h" #include "src/actions/log.h" #include "src/actions/no_log.h" #include "src/actions/log_data.h" #include "src/actions/rev.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_ruleId(0), m_chainedRuleChild(nullptr), m_chainedRuleParent(nullptr), m_disruptiveAction(nullptr), m_logData(nullptr), m_msg(nullptr), m_actionsRuntimePos(), m_actionsSetVar(), m_actionsTag(), m_defaultActionDisruptiveAction(nullptr), m_defaultActionLogData(nullptr), m_defaultActionMsg(nullptr), m_defaultActionActionsRuntimePos(), m_defaultActionActionsSetVar(), m_defaultActionActionsTag(), m_transformations(transformations != nullptr ? *transformations : Transformations()), m_defaultTransformations(), m_severity(SEVERITY_NOT_SET), m_revision(""), m_version(""), m_accuracy(ACCURACY_NOT_SET), m_maturity(MATURITY_NOT_SET), m_containsCaptureAction(false), m_containsLogAction(false), m_containsNoLogAction(false), m_containsMultiMatchAction(false), m_containsStaticBlockAction(false), m_defaultSeverity(SEVERITY_NOT_SET), m_defaultRevision(""), m_defaultVersion(""), m_defaultAccuracy(ACCURACY_NOT_SET), m_defaultMaturity(MATURITY_NOT_SET), m_defaultContainsCaptureAction(false), m_defaultContainsLogAction(false), m_defaultContainsNoLogAction(false), m_defaultContainsMultiMatchAction(false), m_defaultContainsStaticBlockAction(false), m_isChained(false) { if (actions) { for (actions::Action *a : *actions) { addAction(a); } } } void RuleWithActions::addDefaultAction(std::shared_ptr a) { if (a->action_kind == Action::ConfigurationKind) { a->evaluate(this, NULL); return; } if (a->action_kind == Action::ConfigurationKind) { if (dynamic_cast(a.get())) { actions::Accuracy *accuracy = dynamic_cast(a.get()); m_defaultAccuracy = accuracy->getAccuracy(); } else if (dynamic_cast(a.get())) { actions::Rev *rev = dynamic_cast(a.get()); m_defaultRevision = rev->getRevision(); } else { a->evaluate(this, NULL); } return; } if (a->action_kind == Action::RunTimeOnlyIfMatchKind) { if (dynamic_cast(a.get())) { m_defaultContainsCaptureAction = true; } else if (dynamic_cast(a.get())) { m_defaultContainsMultiMatchAction = true; } else if (dynamic_cast(a.get())) { actions::Severity *severity = dynamic_cast(a.get()); setDefaultActionSeverity(severity->m_severity); } else if (dynamic_cast(a.get())) { actions::Maturity *maturity = dynamic_cast(a.get()); setDefaultActionMaturity(maturity->getMaturity()); } else if (dynamic_cast(a.get())) { m_defaultActionLogData = std::static_pointer_cast(a); } else if (dynamic_cast(a.get())) { m_defaultActionMsg = std::static_pointer_cast(a); } else if (dynamic_cast(a.get())) { m_defaultActionActionsSetVar.push_back(std::static_pointer_cast(a)); } else if (dynamic_cast(a.get())) { m_defaultActionActionsTag.push_back(std::static_pointer_cast(a)); } else if (dynamic_cast(a.get())) { m_defaultContainsLogAction = true; } else if (dynamic_cast(a.get())) { m_defaultContainsNoLogAction = true; } else if (dynamic_cast(a.get())) { m_defaultActionActionsRuntimePos.push_back(a); m_defaultContainsStaticBlockAction = true; } else if (a->isDisruptive() == true) { m_defaultActionDisruptiveAction = a; } else { m_defaultActionActionsRuntimePos.push_back(a); } return; } std::cout << "General failure, action: " << *a->m_name; std::cout << " has an unknown type." << std::endl; throw; } void RuleWithActions::addAction(actions::Action *a) { if (a->action_kind == Action::ConfigurationKind) { if (dynamic_cast(a)) { actions::Accuracy *accuracy = dynamic_cast(a); m_accuracy = accuracy->getAccuracy(); } else if (dynamic_cast(a)) { actions::Rev *rev = dynamic_cast(a); m_revision = rev->getRevision(); } else { a->evaluate(this, NULL); } delete a; return; } 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)) { actions::Severity *severity = dynamic_cast(a); setSeverity(severity->m_severity); delete a; } else if (dynamic_cast(a)) { m_logData = std::unique_ptr(dynamic_cast(a)); } else if (dynamic_cast(a)) { m_msg = std::unique_ptr(dynamic_cast(a)); } else if (dynamic_cast(a)) { m_actionsSetVar.push_back(std::unique_ptr(dynamic_cast(a))); } else if (dynamic_cast(a)) { actions::Maturity *maturity = dynamic_cast(a); m_maturity = maturity->getMaturity(); delete a; } else if (dynamic_cast(a)) { m_containsLogAction = true; delete a; } else if (dynamic_cast(a)) { m_containsNoLogAction = true; delete a; } else if (dynamic_cast(a)) { m_actionsTag.push_back(std::unique_ptr(dynamic_cast(a))); } else if (dynamic_cast(a)) { m_actionsRuntimePos.push_back(std::unique_ptr(dynamic_cast(a))); m_containsStaticBlockAction = true; } else if (a->isDisruptive() == true) { m_disruptiveAction = std::unique_ptr(a); } else { m_actionsRuntimePos.push_back(std::unique_ptr(a)); } return; } std::cout << "General failure, action: " << *a->m_name; std::cout << " has an unknown type." << std::endl; delete a; throw; } RuleWithActions::~RuleWithActions() { } bool RuleWithActions::evaluate(Transaction *transaction) { /* Rule evaluate is pure virtual. * * Rule::evaluate(transaction); */ /* Matched vars needs to be clear at every new rule execution */ transaction->m_matched.clear(); return true; } void RuleWithActions::executeActionsIndependentOfChainedRuleResult(Transaction *trans, bool *containsBlock) { for (actions::SetVar *a : getSetVarsActionsPtr()) { 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, *trans->messageGetLast()); } } } void RuleWithActions::executeActionsAfterFullMatch(Transaction *trans, bool containsBlock) { bool disruptiveAlreadyExecuted = false; #if 0 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, a.get(), true); } } #endif for (actions::Tag *a : getTagsActionPtr()) { ms_dbg_a(trans, 4, "Running (non-disruptive) action: " \ + *a->m_name.get()); a->evaluate(this, trans, *trans->messageGetLast()); } 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, a, false); disruptiveAlreadyExecuted = true; } if (m_logData) { m_logData->evaluate(this, trans, *trans->messageGetLast()); } else if (m_defaultActionLogData) { m_defaultActionLogData->evaluate(this, trans, *trans->messageGetLast()); } if (m_msg) { m_msg->evaluate(this, trans, *trans->messageGetLast()); } else if (m_defaultActionMsg) { m_defaultActionMsg->evaluate(this, trans, *trans->messageGetLast()); } for (auto &a : getMatchActionsPtr()) { if (!a->isDisruptive() && !(disruptiveAlreadyExecuted && dynamic_cast(a))) { executeAction(trans, containsBlock, a, false); } } if (!disruptiveAlreadyExecuted && m_disruptiveAction != nullptr) { executeAction(trans, containsBlock, m_disruptiveAction.get(), false); } else if (!disruptiveAlreadyExecuted && hasBlockAction() && m_defaultActionDisruptiveAction != nullptr) { executeAction(trans, containsBlock, m_defaultActionDisruptiveAction.get(), false); } } void RuleWithActions::executeAction(Transaction *trans, bool containsBlock, 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, *trans->messageGetLast()); 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, *trans->messageGetLast()); return; } ms_dbg_a(trans, 4, "Not running any disruptive action (or block): " \ + *a->m_name.get() + ". SecRuleEngine is not On."); } 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 (hasMultimatchAction()) { /* 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 : getTransformationPtr()) { if (a->m_isNone) { none++; } } for (Transformation *a : getTransformationPtr()) { 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)))); } } bool RuleWithActions::containsTag(const std::string& name, Transaction *t) const { for (auto &tag : getTagsAction()) { 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::vector RuleWithActions::getActionsByName(const std::string& name, Transaction *trans) { std::vector ret; for (auto &z : getMatchActionsPtr()) { if (*z->m_name.get() == name) { ret.push_back(z); } } for (auto &z : getTransformationPtr()) { 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 RuleWithActions::getLogData(Transaction *t) { return m_logData->data(t); } std::string RuleWithActions::getMessage(Transaction *t) { return m_msg->data(t); } } // namespace modsecurity