Adds support to SecDefaultAction configuration directive

This commit is contained in:
Felipe Zimmerle
2015-09-04 10:55:20 -03:00
parent f2ed890ea6
commit 010c18f63f
11 changed files with 428 additions and 2 deletions

View File

@@ -88,6 +88,7 @@ class Action {
Assay *assay);
virtual bool evaluate(Rule *rule, Assay *assay);
virtual bool init(std::string *error) { return true; }
virtual bool isDisruptive() { return false; }
static Action *instantiate(const std::string& name);

View File

@@ -27,7 +27,9 @@ namespace ModSecurity {
namespace actions {
Phase::Phase(std::string action)
: Action(action) {
: Action(action),
m_secRulesPhase(0),
phase(0) {
this->action_kind = ConfigurationKind;
std::string a = action;
a.erase(0, 6);
@@ -42,20 +44,25 @@ Phase::Phase(std::string action)
this->phase = 0;
if (tolower(a) == "request") {
this->phase = this->phase + ModSecurity::Phases::RequestHeadersPhase;
m_secRulesPhase = 2;
}
if (tolower(a) == "response") {
this->phase = this->phase + ModSecurity::Phases::ResponseBodyPhase;
m_secRulesPhase = 4;
}
if (tolower(a) == "logging") {
this->phase = this->phase + ModSecurity::Phases::LoggingPhase;
m_secRulesPhase = 5;
}
}
if (this->phase == 0) {
/* Phase 0 is something new, we want to use as ConnectionPhase */
this->phase = ModSecurity::Phases::ConnectionPhase;
m_secRulesPhase = 2;
} else {
/* Otherwise we want to shift the rule to the correct phase */
m_secRulesPhase = phase;
this->phase = phase + ModSecurity::Phases::RequestHeadersPhase - 1;
}
}

View File

@@ -36,6 +36,7 @@ class Phase : public Action {
bool evaluate(Rule *rule, Assay *assay) override;
int phase;
int m_secRulesPhase;
};
} // namespace actions

View File

@@ -16,20 +16,25 @@ class Driver;
}
}
#include "modsecurity/modsecurity.h"
#include "actions/action.h"
#include "actions/audit_log.h"
#include "actions/ctl_audit_log_parts.h"
#include "actions/set_var.h"
#include "actions/severity.h"
#include "actions/msg.h"
#include "actions/phase.h"
#include "actions/log_data.h"
#include "actions/rev.h"
#include "actions/tag.h"
#include "actions/transformations/transformation.h"
#include "actions/transformations/none.h"
#include "operators/operator.h"
#include "rule.h"
#include "utils/geo_lookup.h"
#include "audit_log.h"
#include "utils.h"
#include "variables/variations/count.h"
#include "variables/variations/exclusion.h"
@@ -47,6 +52,8 @@ class Driver;
#include "variables/time_wday.h"
#include "variables/time_year.h"
using ModSecurity::ModSecurity;
using ModSecurity::actions::Action;
using ModSecurity::actions::CtlAuditLogParts;
using ModSecurity::actions::SetVar;
@@ -54,6 +61,8 @@ using ModSecurity::actions::Severity;
using ModSecurity::actions::Tag;
using ModSecurity::actions::Rev;
using ModSecurity::actions::Msg;
using ModSecurity::actions::Phase;
using ModSecurity::actions::transformations::None;
using ModSecurity::actions::LogData;
using ModSecurity::actions::transformations::Transformation;
using ModSecurity::operators::Operator;
@@ -181,6 +190,8 @@ using ModSecurity::Variables::Variable;
%token <std::string> CONFIG_DIR_DEBUG_LOG
%token <std::string> CONFIG_DIR_DEBUG_LVL
%token <std::string> CONFIG_DIR_SEC_DEFAULT_ACTION
%token <std::string> VARIABLE
%token <std::string> RUN_TIME_VAR_DUR
%token <std::string> RUN_TIME_VAR_ENV
@@ -345,6 +356,43 @@ expression:
);
driver.addSecRule(rule);
}
| CONFIG_DIR_SEC_DEFAULT_ACTION SPACE QUOTATION_MARK actions QUOTATION_MARK
{
std::vector<Action *> *actions = $4;
std::vector<Action *> checkedActions;
int definedPhase = -1;
int secRuleDefinedPhase = -1;
for (Action *a : *actions) {
Phase *phase = dynamic_cast<Phase *>(a);
if (phase != NULL) {
definedPhase = phase->phase;
secRuleDefinedPhase = phase->m_secRulesPhase;
} else if (a->action_kind == Action::RunTimeOnlyIfMatchKind ||
a->action_kind == Action::RunTimeBeforeMatchAttemptKind) {
None *none = dynamic_cast<None *>(a);
if (none != NULL) {
driver.parserError << "The transformation none is not suitable to be part of the SecDefaultActions";
YYERROR;
}
checkedActions.push_back(a);
} else {
driver.parserError << "The action '" << a->action << "' is not suitable to be part of the SecDefaultActions";
YYERROR;
}
}
if (definedPhase == -1) {
definedPhase = ModSecurity::ModSecurity::Phases::RequestHeadersPhase;
}
if (!driver.defaultActions[definedPhase].empty()) {
driver.parserError << "SecDefaultActions can only be placed once per phase and configuration context. Phase " << secRuleDefinedPhase << " was informed already.";
YYERROR;
}
for (Action *a : checkedActions) {
driver.defaultActions[definedPhase].push_back(a);
}
}
| CONFIG_DIR_RULE_ENG SPACE CONFIG_VALUE_OFF
{
driver.secRuleEngine = ModSecurity::Rules::DisabledRuleEngine;

View File

@@ -38,6 +38,8 @@ ACTION_CTL_AUDIT_LOG_PARTS (?i:ctl:auditLogParts)
DIRECTIVE (?i:SecRule)
LOG_DATA (?i:logdata)
CONFIG_DIR_SEC_DEFAULT_ACTION (?i:SecDefaultAction)
CONFIG_DIR_PCRE_MATCH_LIMIT_RECURSION (?i:SecPcreMatchLimitRecursion)
CONFIG_DIR_PCRE_MATCH_LIMIT (?i:SecPcreMatchLimit)
CONGIG_DIR_RESPONSE_BODY_MP (?i:SecResponseBodyMimeType)
@@ -241,6 +243,7 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile)
{CONFIG_VALUE_PROCESS_PARTIAL} { return yy::seclang_parser::make_CONFIG_VALUE_PROCESS_PARTIAL(yytext, *driver.loc.back()); }
{CONFIG_VALUE_REJECT} { return yy::seclang_parser::make_CONFIG_VALUE_REJECT(yytext, *driver.loc.back()); }
{CONFIG_DIR_SEC_DEFAULT_ACTION} { return yy::seclang_parser::make_CONFIG_DIR_SEC_DEFAULT_ACTION(yytext, *driver.loc.back()); }
<EXPECTING_OPERATOR>{
["][^@]{FREE_TEXT}["] { BEGIN(INITIAL); return yy::seclang_parser::make_FREE_TEXT(yytext, *driver.loc.back()); }

View File

@@ -152,6 +152,22 @@ bool Rule::evaluate(Assay *assay) {
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 (Action *a : assay->m_rules->defaultActions[this->phase]) {
if (a->action_kind == actions::Action::RunTimeBeforeMatchAttemptKind) {
value = a->evaluate(value, assay);
assay->debug(9, "(SecDefaultAction) T (" + \
std::to_string(transformations) + ") " + \
a->name + ": \"" + value +"\"");
transformations++;
}
}
}
for (Action *a : this->actions_runtime_pre) {
None *z = dynamic_cast<None *>(a);
if (none == 0) {
@@ -206,6 +222,12 @@ bool Rule::evaluate(Assay *assay) {
assay->delete_variable("MATCHED_VARS_NAMES:" + v.first);
}
if (this->chained && chainResult == true || !this->chained) {
for (Action *a : assay->m_rules->defaultActions[this->phase]) {
if (a->action_kind == actions::Action::RunTimeOnlyIfMatchKind) {
assay->debug(4, "(SecDefaultAction) Running action: " + a->action);
a->evaluate(this, assay);
}
}
for (Action *a :
this->actions_runtime_pos) {
assay->debug(4, "Running action: " + a->action);

View File

@@ -208,6 +208,20 @@ int Rules::merge(Driver *from) {
this->requestBodyLimitAction = from->requestBodyLimitAction;
this->responseBodyLimitAction = from->responseBodyLimitAction;
/*
*
* default Actions is something per configuration context, there is
* need to merge anything.
*
*/
for (int i = 0; i < ModSecurity::Phases::NUMBER_OF_PHASES; i++) {
std::vector<Action *> actions = from->defaultActions[i];
this->defaultActions[i].clear();
for (int j = 0; j < actions.size(); j++) {
Action *action = actions[j];
this->defaultActions[i].push_back(action);
}
}
if (from->audit_log != NULL && this->audit_log != NULL) {
this->audit_log->refCountDecreaseAndCheck();

View File

@@ -49,6 +49,33 @@
namespace ModSecurity {
std::string phase_name(int x) {
switch(x) {
case ModSecurity::Phases::ConnectionPhase:
return "Connection Phase";
break;
case ModSecurity::Phases::UriPhase:
return "URI Phase";
break;
case ModSecurity::Phases::RequestHeadersPhase:
return "Request Headers";
break;
case ModSecurity::Phases::RequestBodyPhase:
return "Request Headers";
break;
case ModSecurity::Phases::ResponseHeadersPhase:
return "Response Headers";
break;
case ModSecurity::Phases::ResponseBodyPhase:
return "Reponse Body";
break;
case ModSecurity::Phases::LoggingPhase:
return "Logging";
break;
}
return "Phase '" + std::to_string(x) + "' is not known.";
}
std::vector<std::string> split(std::string str, char delimiter) {
std::vector<std::string> internal;

View File

@@ -44,6 +44,7 @@ namespace ModSecurity {
std::string string_to_hex(const std::string& input);
int urldecode_uni_nonstrict_inplace_ex(Assay *assay, unsigned char *input,
int64_t input_len, int *changed);
std::string phase_name(int x);
} // namespace ModSecurity
#define SRC_UTILS_H_