/* * 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