ModSecurity/headers/modsecurity/rules_set_properties.h

660 lines
20 KiB
C++

/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 - 2021 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 WIN32
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#endif
#ifdef __cplusplus
#include <ctime>
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <set>
#include <cstring>
#include <limits>
#endif
#ifndef HEADERS_MODSECURITY_RULES_SET_PROPERTIES_H_
#define HEADERS_MODSECURITY_RULES_SET_PROPERTIES_H_
#include "modsecurity/modsecurity.h"
#include "modsecurity/rule.h"
#include "modsecurity/rules_exceptions.h"
#include "modsecurity/actions/action.h"
#include "modsecurity/audit_log.h"
#define CODEPAGE_SEPARATORS " \t\n\r"
#define merge_boolean_value(to, from, default) \
if (to == PropertyNotSetConfigBoolean) { \
to = (from == PropertyNotSetConfigBoolean) ? default : from; \
}
#define merge_ruleengine_value(to, from, default) \
if (to == PropertyNotSetRuleEngine) { \
to = (from == PropertyNotSetRuleEngine) ? default : from; \
}
#define merge_bodylimitaction_value(to, from, default) \
if (to == PropertyNotSetBodyLimitAction) { \
to = (from == PropertyNotSetBodyLimitAction) ? default : from; \
}
#define merge_xmlargparse_value(to, from, default) \
if (to == PropertyNotSetConfigXMLParseXmlIntoArgs) { \
to = (from == PropertyNotSetConfigXMLParseXmlIntoArgs) ? default : from; \
}
#ifdef __cplusplus
namespace modsecurity {
class RulesExceptions;
namespace Parser {
class Driver;
}
using modsecurity::debug_log::DebugLog;
using modsecurity::audit_log::AuditLog;
// template for different numeric int types
template <typename T>
class ConfigValue {
public:
bool m_set = false;
T m_value = 0;
ConfigValue() = default;
void merge(const ConfigValue<T>* from) {
if (m_set || !from->m_set) return;
m_set = true;
m_value = from->m_value;
}
// default parser
bool parse(const std::string& a, std::string* errmsg = nullptr) {
// use an alias type because the template can convert both signed and unsigned int
using LimitSigned = std::conditional_t<std::is_signed_v<T>, long long, unsigned long long>;
LimitSigned val;
// clear errno variable, wee need that later
errno = 0;
try {
if constexpr (std::is_signed_v<T>) {
val = std::stoll(a);
} else {
val = std::stoull(a);
}
}
catch (const std::invalid_argument&) {
// probably can't occur, but we handle it anyway
if (errmsg) *errmsg = "Invalid number format (not numeric)";
return false;
}
catch (const std::out_of_range&) {
if (errmsg) *errmsg = "Number out of range";
return false;
}
catch (...) {
// we don't need to handle all exceptions, the engine's BISON parser
// does not allow other symbols than numbers
if (errmsg) *errmsg = "An unknown error occurred while parsed the value.";
return false;
}
if (
// first condition will be true if the value is bigger than ull max value
// the second condition checks if the value is fit as ull, but not fit for
// designed type, eg. unsigned int; in that case the errno will be 0, but
// we must check the value is not bigger than the given maxValue at the type class
(errno == ERANGE && val == static_cast<unsigned long long>(maxValue()))
||
(val > static_cast<unsigned long long>(maxValue()))
) {
if (errmsg) *errmsg = "Value is too big.";
return false;
}
if constexpr (std::is_unsigned_v<T>) {
// we should split the check of minValue
// because if the type is signed, then the casted value
// will be a problem
if (
// same as above
(errno == ERANGE && val == static_cast<unsigned long long>(minValue()))
||
(val < static_cast<unsigned long long>(minValue()))
) {
if (errmsg) *errmsg = "Value is too small.";
return false;
}
}
else {
if (
// same as above
// but cast to not unsigned
(errno == ERANGE && val == static_cast<long long>(minValue()))
||
(val < static_cast<long long>(minValue()))
) {
if (errmsg) *errmsg = "Value is too small.";
return false;
}
}
m_value = static_cast<T>(val);
m_set = true;
return true;
}
protected:
// derived classes must implement the maxValue
virtual T maxValue() const = 0;
// minValue is optional
virtual T minValue() const { return 0; }
};
/** @ingroup ModSecurity_CPP_API */
class ConfigInt : public ConfigValue<signed int> {
protected:
signed int minValue() const override {
return std::numeric_limits<signed int>::min();
}
signed int maxValue() const override {
return std::numeric_limits<signed int>::max();
}
};
class ConfigUnsignedInt : public ConfigValue<unsigned int> {
protected:
unsigned int maxValue() const override {
return std::numeric_limits<unsigned int>::max();
}
};
class ConfigUnsignedLong : public ConfigValue<unsigned long> {
protected:
unsigned long maxValue() const override {
return std::numeric_limits<unsigned long>::max();
}
};
class ConfigDouble {
public:
bool m_set = false;
double m_value = 0.0;
ConfigDouble() = default;
void merge(const ConfigDouble *from) {
if (m_set == true || from->m_set == false) {
return;
}
m_set = true;
m_value = from->m_value;
return;
}
};
class ConfigString {
public:
bool m_set = false;
std::string m_value = "";
ConfigString() = default;
void merge(const ConfigString *from) {
if (m_set == true || from->m_set == false) {
return;
}
m_set = true;
m_value = from->m_value;
return;
}
};
class ConfigSet {
public:
bool m_set = false;
bool m_clear = false;
std::set<std::string> m_value;
ConfigSet() = default;
};
class UnicodeMapHolder {
public:
UnicodeMapHolder() {
memset(m_data, -1, (sizeof(int)*65536));
};
int& operator[](int index) { return m_data[index]; }
int operator[](int index) const { return m_data[index]; }
int at(int index) const { return m_data[index]; }
void change(int i, int a) { m_data[i] = a; }
int m_data[65536];
};
class RulesSetProperties;
class ConfigUnicodeMap {
public:
ConfigUnicodeMap() : m_set(false),
m_unicodeCodePage(0),
m_unicodeMapTable(NULL) { }
static void loadConfig(std::string f, double codePage,
RulesSetProperties *driver, std::string *errg);
void merge(const ConfigUnicodeMap *from) {
if (from->m_set == false) {
return;
}
m_set = true;
m_unicodeCodePage = from->m_unicodeCodePage;
m_unicodeMapTable = from->m_unicodeMapTable;
return;
}
bool m_set;
double m_unicodeCodePage;
std::shared_ptr<modsecurity::UnicodeMapHolder> m_unicodeMapTable;
};
class RulesSetProperties {
public:
RulesSetProperties() :
m_auditLog(new AuditLog()),
m_requestBodyLimitAction(PropertyNotSetBodyLimitAction),
m_responseBodyLimitAction(PropertyNotSetBodyLimitAction),
m_secRequestBodyAccess(PropertyNotSetConfigBoolean),
m_secResponseBodyAccess(PropertyNotSetConfigBoolean),
m_secXMLExternalEntity(PropertyNotSetConfigBoolean),
m_secXMLParseXmlIntoArgs(PropertyNotSetConfigXMLParseXmlIntoArgs),
m_tmpSaveUploadedFiles(PropertyNotSetConfigBoolean),
m_uploadKeepFiles(PropertyNotSetConfigBoolean),
m_debugLog(new DebugLog()),
m_remoteRulesActionOnFailed(PropertyNotSetRemoteRulesAction),
m_secRuleEngine(PropertyNotSetRuleEngine) { }
explicit RulesSetProperties(DebugLog *debugLog) :
m_auditLog(new AuditLog()),
m_requestBodyLimitAction(PropertyNotSetBodyLimitAction),
m_responseBodyLimitAction(PropertyNotSetBodyLimitAction),
m_secRequestBodyAccess(PropertyNotSetConfigBoolean),
m_secResponseBodyAccess(PropertyNotSetConfigBoolean),
m_secXMLExternalEntity(PropertyNotSetConfigBoolean),
m_secXMLParseXmlIntoArgs(PropertyNotSetConfigXMLParseXmlIntoArgs),
m_tmpSaveUploadedFiles(PropertyNotSetConfigBoolean),
m_uploadKeepFiles(PropertyNotSetConfigBoolean),
m_debugLog(debugLog),
m_remoteRulesActionOnFailed(PropertyNotSetRemoteRulesAction),
m_secRuleEngine(PropertyNotSetRuleEngine) { }
RulesSetProperties(const RulesSetProperties &r) = delete;
RulesSetProperties &operator =(const RulesSetProperties &r) = delete;
~RulesSetProperties() {
int i = 0;
for (i = 0; i < modsecurity::Phases::NUMBER_OF_PHASES; i++) {
std::vector<std::shared_ptr<actions::Action> > *tmp = \
&m_defaultActions[i];
while (tmp->empty() == false) {
tmp->pop_back();
}
}
delete m_debugLog;
delete m_auditLog;
}
/**
*
* The ConfigBoolean enumerator defines the states for configuration boolean values.
* The default value is PropertyNotSetConfigBoolean.
*/
enum ConfigBoolean {
TrueConfigBoolean,
FalseConfigBoolean,
PropertyNotSetConfigBoolean
};
/**
*
* The ConfigXMLParseXmlIntoArgs enumerator defines the states for the configuration
* XMLParseXmlIntoArgs values.
* The default value is PropertyNotSetConfigXMLParseXmlIntoArgs.
*/
enum ConfigXMLParseXmlIntoArgs {
TrueConfigXMLParseXmlIntoArgs,
FalseConfigXMLParseXmlIntoArgs,
OnlyArgsConfigXMLParseXmlIntoArgs,
PropertyNotSetConfigXMLParseXmlIntoArgs
};
/**
*
* The RuleEngine enumerator consists in mapping the different states
* of the rule engine.
*
*/
enum RuleEngine {
/**
*
* Rules won't be evaluated if Rule Engine is set to DisabledRuleEngine
*
*/
DisabledRuleEngine,
/**
*
* Rules will be evaluated and disturb actions will take place if needed.
*
*/
EnabledRuleEngine,
/**
* Rules will be evaluated but it won't generate any disruptive action.
*
*/
DetectionOnlyRuleEngine,
/**
*
*/
PropertyNotSetRuleEngine
};
/**
*
* Defines what actions should be taken in case the body (response or
* request) is bigger than the expected size.
*
*/
enum BodyLimitAction {
/**
*
* Process partial
*
*/
ProcessPartialBodyLimitAction,
/**
*
* Reject the request
*
*/
RejectBodyLimitAction,
/**
*
*/
PropertyNotSetBodyLimitAction
};
/**
*
* Defines what actions should be taken in case the remote rules failed to
* be downloaded (independent of the circumstances)
*
*
*/
enum OnFailedRemoteRulesAction {
/**
*
* Abort
*
*/
AbortOnFailedRemoteRulesAction,
/**
*
* Warn on logging
*
*/
WarnOnFailedRemoteRulesAction,
/**
*
*/
PropertyNotSetRemoteRulesAction
};
static std::string ruleEngineStateString(RuleEngine i) {
switch (i) {
case DisabledRuleEngine:
return "Disabled";
case EnabledRuleEngine:
return "Enabled";
case DetectionOnlyRuleEngine:
return "DetectionOnly";
case PropertyNotSetRuleEngine:
return "PropertyNotSet/DetectionOnly";
}
return std::string{};
}
static std::string configBooleanString(ConfigBoolean i) {
switch (i) {
case TrueConfigBoolean:
return "True";
case FalseConfigBoolean:
return "False";
case PropertyNotSetConfigBoolean:
default:
return "Not set";
}
}
static std::string configXMLParseXmlIntoArgsString(ConfigXMLParseXmlIntoArgs i) {
switch (i) {
case TrueConfigXMLParseXmlIntoArgs:
return "True";
case FalseConfigXMLParseXmlIntoArgs:
return "False";
case OnlyArgsConfigXMLParseXmlIntoArgs:
return "OnlyArgs";
case PropertyNotSetConfigXMLParseXmlIntoArgs:
default:
return "Not set";
}
}
static int mergeProperties(RulesSetProperties *from,
RulesSetProperties *to, std::ostringstream *err) {
merge_ruleengine_value(to->m_secRuleEngine, from->m_secRuleEngine,
PropertyNotSetRuleEngine);
merge_boolean_value(to->m_secRequestBodyAccess,
from->m_secRequestBodyAccess,
PropertyNotSetConfigBoolean);
merge_boolean_value(to->m_secResponseBodyAccess,
from->m_secResponseBodyAccess,
PropertyNotSetConfigBoolean);
merge_boolean_value(to->m_secXMLExternalEntity,
from->m_secXMLExternalEntity,
PropertyNotSetConfigBoolean);
merge_xmlargparse_value(to->m_secXMLParseXmlIntoArgs,
from->m_secXMLParseXmlIntoArgs,
PropertyNotSetConfigXMLParseXmlIntoArgs);
merge_boolean_value(to->m_uploadKeepFiles,
from->m_uploadKeepFiles,
PropertyNotSetConfigBoolean);
merge_boolean_value(to->m_tmpSaveUploadedFiles,
from->m_tmpSaveUploadedFiles,
PropertyNotSetConfigBoolean);
to->m_argumentsLimit.merge(&from->m_argumentsLimit);
to->m_requestBodyJsonDepthLimit.merge(&from->m_requestBodyJsonDepthLimit);
to->m_requestBodyLimit.merge(&from->m_requestBodyLimit);
to->m_requestBodyNoFilesLimit.merge(&from->m_requestBodyNoFilesLimit);
to->m_responseBodyLimit.merge(&from->m_responseBodyLimit);
merge_bodylimitaction_value(to->m_requestBodyLimitAction,
from->m_requestBodyLimitAction,
PropertyNotSetBodyLimitAction);
merge_bodylimitaction_value(to->m_responseBodyLimitAction,
from->m_responseBodyLimitAction,
PropertyNotSetBodyLimitAction);
to->m_pcreMatchLimit.merge(&from->m_pcreMatchLimit);
to->m_uploadFileLimit.merge(&from->m_uploadFileLimit);
to->m_uploadFileMode.merge(&from->m_uploadFileMode);
to->m_uploadDirectory.merge(&from->m_uploadDirectory);
to->m_uploadTmpDirectory.merge(&from->m_uploadTmpDirectory);
to->m_secArgumentSeparator.merge(&from->m_secArgumentSeparator);
to->m_secWebAppId.merge(&from->m_secWebAppId);
to->m_unicodeMapTable.merge(&from->m_unicodeMapTable);
to->m_httpblKey.merge(&from->m_httpblKey);
to->m_exceptions.merge(&from->m_exceptions);
to->m_components.insert(to->m_components.end(),
from->m_components.begin(), from->m_components.end());
if (from->m_responseBodyTypeToBeInspected.m_set == true) {
if (from->m_responseBodyTypeToBeInspected.m_clear == true) {
to->m_responseBodyTypeToBeInspected.m_value.clear();
from->m_responseBodyTypeToBeInspected.m_value.clear();
} else {
for (std::set<std::string>::iterator
it = from->m_responseBodyTypeToBeInspected.m_value.begin();
it != from->m_responseBodyTypeToBeInspected.m_value.end();
++it) {
to->m_responseBodyTypeToBeInspected.m_value.insert(*it);
}
}
to->m_responseBodyTypeToBeInspected.m_set = true;
}
for (int i = 0; i < modsecurity::Phases::NUMBER_OF_PHASES; i++) {
std::vector<std::shared_ptr<actions::Action> > *actions_from = \
&from->m_defaultActions[i];
std::vector<std::shared_ptr<actions::Action> > *actions_to = \
&to->m_defaultActions[i];
for (size_t j = 0; j < actions_from->size(); j++) {
actions_to->push_back(actions_from->at(j));
}
}
if (to->m_auditLog) {
std::string error;
to->m_auditLog->merge(from->m_auditLog, &error);
if (error.size() > 0) {
*err << error;
return -1;
}
}
if (from->m_debugLog && to->m_debugLog &&
from->m_debugLog->isLogFileSet()) {
if (to->m_debugLog->isLogFileSet() == false) {
std::string error;
to->m_debugLog->setDebugLogFile(
from->m_debugLog->getDebugLogFile(),
&error);
if (error.size() > 0) {
*err << error;
return -1;
}
}
}
if (from->m_debugLog && to->m_debugLog &&
from->m_debugLog->isLogLevelSet()) {
if (to->m_debugLog->isLogLevelSet() == false) {
to->m_debugLog->setDebugLogLevel(
from->m_debugLog->getDebugLogLevel());
}
}
return 1;
}
audit_log::AuditLog *m_auditLog;
BodyLimitAction m_requestBodyLimitAction;
BodyLimitAction m_responseBodyLimitAction;
ConfigBoolean m_secRequestBodyAccess;
ConfigBoolean m_secResponseBodyAccess;
ConfigBoolean m_secXMLExternalEntity;
ConfigXMLParseXmlIntoArgs m_secXMLParseXmlIntoArgs;
ConfigBoolean m_tmpSaveUploadedFiles;
ConfigBoolean m_uploadKeepFiles;
ConfigUnsignedInt m_argumentsLimit;
ConfigUnsignedInt m_requestBodyJsonDepthLimit;
ConfigUnsignedLong m_requestBodyLimit;
ConfigUnsignedLong m_requestBodyNoFilesLimit;
ConfigUnsignedLong m_responseBodyLimit;
ConfigUnsignedInt m_pcreMatchLimit;
ConfigUnsignedInt m_uploadFileLimit;
ConfigUnsignedInt m_uploadFileMode;
DebugLog *m_debugLog;
OnFailedRemoteRulesAction m_remoteRulesActionOnFailed;
RuleEngine m_secRuleEngine;
RulesExceptions m_exceptions;
std::list<std::string> m_components;
std::ostringstream m_parserError;
ConfigSet m_responseBodyTypeToBeInspected;
ConfigString m_httpblKey;
ConfigString m_uploadDirectory;
ConfigString m_uploadTmpDirectory;
ConfigString m_secArgumentSeparator;
ConfigString m_secWebAppId;
std::vector<std::shared_ptr<actions::Action> > \
m_defaultActions[modsecurity::Phases::NUMBER_OF_PHASES];
ConfigUnicodeMap m_unicodeMapTable;
};
#endif
#ifdef __cplusplus
} // namespace modsecurity
#endif
#endif // HEADERS_MODSECURITY_RULES_SET_PROPERTIES_H_