Adds checks for the NO_LOGS definition and improved the vars resolution time

This commit is contained in:
Felipe Zimmerle 2015-09-17 17:41:38 -03:00
parent 3e067e7409
commit ed86c24df6
46 changed files with 294 additions and 62 deletions

View File

@ -136,7 +136,7 @@ class ModSecurityStringVariables :
l.push_back(pair);
}
if (l.size() == 0) {
if (l.size() == 0 && key.find(":") == std::string::npos) {
for (auto& x : *this) {
if ((x.first.substr(0, key.size() + 1).compare(key + ":") != 0)
&& (x.first != key)) {
@ -144,10 +144,8 @@ class ModSecurityStringVariables :
}
std::list<std::pair<std::string, std::string>> t;
t = this->resolveVariable(x.first);
for (std::pair<std::string, std::string> z : t) {
pair = std::make_pair(std::string(z.first),
std::string(z.second));
l.push_back(pair);
if (t.empty() == false) {
l.insert(l.end(), t.begin(), t.end());
}
}
}
@ -239,8 +237,9 @@ class Assay {
ModSecurityStringVariables m_variables_strings;
std::unordered_map<std::string, ModSecurityStringVariables *> collections;
#ifndef NO_LOGS
void debug(int, std::string);
#endif
void serverLog(const std::string& msg);
std::vector<actions::Action *> actions;

View File

@ -33,7 +33,9 @@ Block::Block(std::string action)
bool Block::evaluate(Rule *rule, Assay *assay) {
#ifndef NO_LOGS
assay->debug(8, "Running action block");
#endif
for (Action *a : rule->actions_runtime_pos) {
if (a->isDisruptive() == true) {
assay->actions.push_back(a);

View File

@ -35,17 +35,16 @@ bool Capture::evaluate(Rule *rule, Assay *assay) {
std::list<std::string> match;
operators::Pm *pm = dynamic_cast<operators::Pm *>(op);
operators::Rx *rx = dynamic_cast<operators::Rx *>(op);
operators::Contains *contains = dynamic_cast<operators::Contains *>(op);
if (pm != NULL) {
match = pm->matched;
}
operators::Rx *rx = dynamic_cast<operators::Rx *>(op);
if (rx != NULL) {
match = rx->matched;
}
operators::Contains *contains = dynamic_cast<operators::Contains *>(op);
if (contains != NULL) {
match = contains->matched;
}

View File

@ -31,7 +31,9 @@ Deny::Deny(std::string action)
bool Deny::evaluate(Rule *rule, Assay *assay) {
#ifndef NO_LOGS
assay->debug(8, "Running action deny");
#endif
assay->actions.push_back(this);
return true;
}

View File

@ -36,7 +36,9 @@ LogData::LogData(std::string action)
bool LogData::evaluate(Rule *rule, Assay *assay) {
std::string msg = MacroExpansion::expand(m_data, assay);
#ifndef NO_LOGS
assay->debug(9, "Saving msg: " + msg);
#endif
assay->rulesMessages.push_back(msg);
assay->serverLog(msg);
return true;

View File

@ -36,7 +36,9 @@ Msg::Msg(std::string action)
bool Msg::evaluate(Rule *rule, Assay *assay) {
std::string msg = MacroExpansion::expand(m_msg, assay);
#ifndef NO_LOGS
assay->debug(9, "Saving msg: " + msg);
#endif
assay->rulesMessages.push_back(msg);
assay->serverLog(msg);
return true;

View File

@ -21,6 +21,7 @@
#include "modsecurity/assay.h"
#include "src/rule.h"
#include "src/macro_expansion.h"
#include "src/utils.h"
namespace ModSecurity {
namespace actions {
@ -57,6 +58,7 @@ bool SetVar::init(std::string *error) {
pos = action.find(".");
if (pos != std::string::npos) {
collectionName = std::string(action, 0, pos);
collectionName = toupper(collectionName);
} else {
error->assign("Missing the collection and/or variable name");
return false;
@ -140,8 +142,10 @@ bool SetVar::evaluate(Rule *rule, Assay *assay) {
break;
}
#ifndef NO_LOGS
assay->debug(8, "Saving variable: " + collectionName + ":" + \
variableName + " with value: " + targetValue);
#endif
assay->setCollection(collectionName, variableName, targetValue);
return true;

View File

@ -51,9 +51,11 @@ Severity::Severity(std::string action)
bool Severity::evaluate(Rule *rule, Assay *assay) {
#ifndef NO_LOGS
assay->debug(9, "This rule severity is: " + \
std::to_string(this->m_severity) + " current assay is: " + \
std::to_string(assay->highest_severity));
#endif
if (assay->highest_severity > this->m_severity) {
assay->highest_severity = this->m_severity;

View File

@ -32,7 +32,9 @@ SkipAfter::SkipAfter(std::string action)
bool SkipAfter::evaluate(Rule *rule, Assay *assay) {
#ifndef NO_LOGS
assay->debug(5, "Setting skipAfter for: " + m_marker);
#endif
assay->m_marker = m_marker;
return true;
}

View File

@ -36,7 +36,9 @@ Tag::Tag(std::string action)
bool Tag::evaluate(Rule *rule, Assay *assay) {
std::string tag = MacroExpansion::expand(m_tag, assay);
#ifndef NO_LOGS
assay->debug(9, "Rule tag: " + tag);
#endif
assay->ruleTags.push_back(tag);
return true;
}

View File

@ -41,7 +41,9 @@ std::string Base64Decode::evaluate(std::string value,
* @todo Implement the transformation base64decode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation 64 is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string Base64DecodeExt::evaluate(std::string value,
* @todo Implement the transformation Base64DecodeExt
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation Base64DecodeExt is" \
" not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string CmdLine::evaluate(std::string value,
* @todo Implement the transformation CmdLine
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation CmdLine is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string CompressWhitespace::evaluate(std::string value,
* @todo Implement the transformation CompressWhitespace
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation CompressWhitespace is " \
"not implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string EscapeSeqDecode::evaluate(std::string value,
* @todo Implement the transformation EscapeSeqDecode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation EscapeSeqDecode is " \
"not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string HexDecode::evaluate(std::string value,
* @todo Implement the transformation HexDecode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation HexDecode is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string HexEncode::evaluate(std::string value,
* @todo Implement the transformation HexEncode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation HexEncode is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string Length::evaluate(std::string value,
* @todo Implement the transformation Length
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation Length is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string Md5::evaluate(std::string value,
* @todo Implement the transformation Md5
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation Md5 is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string NormalisePath::evaluate(std::string value,
* @todo Implement the transformation NormalisePath
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation NormalisePath is not" \
" implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string ParityEven7bit::evaluate(std::string value,
* @todo Implement the transformation ParityEven7bit
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation ParityEven7bit is not" \
" implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string ParityOdd7bit::evaluate(std::string value,
* @todo Implement the transformation ParityOdd7bit
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation ParityOdd7bit is not " \
"implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string ParityZero7bit::evaluate(std::string value,
* @todo Implement the transformation ParityZero7bit
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation ParityZero7bit is not" \
"implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string RemoveComments::evaluate(std::string value,
* @todo Implement the transformation RemoveComments
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation RemoveComments is not " \
"implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string RemoveCommentsChar::evaluate(std::string value,
* @todo Implement the transformation RemoveCommentsChar
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation RemoveCommentsChar " \
"is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string RemoveWhitespace::evaluate(std::string value,
* @todo Implement the transformation RemoveWhitespace
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation RemoveWhitespace is " \
"not implemented yet.");
#endif
}
return value;
}

View File

@ -41,8 +41,10 @@ std::string ReplaceComments::evaluate(std::string value,
* @todo Implement the transformation ReplaceComments
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation ReplaceComments " \
"is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string ReplaceNulls::evaluate(std::string value,
* @todo Implement the transformation ReplaceNulls
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation ReplaceNulls is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string Sha1::evaluate(std::string value,
* @todo Implement the transformation Sha1
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation Sha1 is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string SqlHexDecode::evaluate(std::string value,
* @todo Implement the transformation SqlHexDecode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation SqlHexDecode is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string UrlDecode::evaluate(std::string value,
* @todo Implement the transformation UrlDecode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation UrlDecode is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string UrlEncode::evaluate(std::string value,
* @todo Implement the transformation UrlEncode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation UrlEncode is not implemented yet.");
#endif
}
return value;
}

View File

@ -41,7 +41,9 @@ std::string Utf8Unicode::evaluate(std::string value,
* @todo Implement the transformation Utf8Unicode
*/
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "Transformation Utf8Unicode is not implemented yet.");
#endif
}
return value;
}

View File

@ -127,7 +127,9 @@ Assay::Assay(ModSecurity *ms, Rules *rules, void *logCbData)
"RESPONSE_HEADERS_NAMES");
collections.emplace("TX", new ModSecurityStringVariables());
#ifndef NO_LOGS
this->debug(4, "Initialising transaction");
#endif
}
@ -157,6 +159,7 @@ Assay::~Assay() {
* @param message Message to be logged.
*
*/
#ifndef NO_LOGS
void Assay::debug(int level, std::string message) {
if (m_rules == NULL) {
return;
@ -164,7 +167,7 @@ void Assay::debug(int level, std::string message) {
m_rules->debug(level, message);
}
#endif
/**
* @name processConnection
@ -193,8 +196,10 @@ int Assay::processConnection(const char *client, int cPort, const char *server,
this->m_serverIpAddress = server;
this->m_clientPort = cPort;
this->m_serverPort = sPort;
#ifndef NO_LOGS
debug(4, "Transaction context created.");
debug(4, "Starting phase CONNECTION. (SecRules 0)");
#endif
this->store_variable("REMOTE_HOST", m_clientIpAddress);
this->store_variable("UNIQUE_ID", id);
@ -232,7 +237,10 @@ int Assay::processConnection(const char *client, int cPort, const char *server,
*/
int Assay::processURI(const char *uri, const char *protocol,
const char *http_version) {
#ifndef NO_LOGS
debug(4, "Starting phase URI. (SecRules 0 + 1/2)");
#endif
m_protocol = protocol;
m_httpVersion = http_version;
@ -326,9 +334,10 @@ int Assay::processURI(const char *uri, const char *protocol,
key.length() + value.length();
this->m_ARGScombinedSizeStr->assign(
std::to_string(this->m_ARGScombinedSize));
#ifndef NO_LOGS
debug(4, "Adding request argument (QUERY_STRING): name \"" + \
key + "\", value \"" + value + "\"");
#endif
}
}
return true;
@ -350,10 +359,14 @@ int Assay::processURI(const char *uri, const char *protocol,
*
*/
int Assay::processRequestHeaders() {
#ifndef NO_LOGS
debug(4, "Starting phase REQUEST_HEADERS. (SecRules 1)");
#endif
if (m_rules->secRuleEngine == Rules::DisabledRuleEngine) {
#ifndef NO_LOGS
debug(4, "Rule engine disabled, returning...");
#endif
return true;
}
@ -502,10 +515,14 @@ int Assay::addRequestHeader(const unsigned char *key, size_t key_n,
*
*/
int Assay::processRequestBody() {
#ifndef NO_LOGS
debug(4, "Starting phase REQUEST_BODY. (SecRules 2)");
#endif
if (m_rules->secRuleEngine == Rules::DisabledRuleEngine) {
#ifndef NO_LOGS
debug(4, "Rule engine disabled, returning...");
#endif
return true;
}
@ -538,41 +555,56 @@ int Assay::processRequestBody() {
store_variable("MULTIPART_CRLF_LF_LINES", "0");
}
if (m.boundaryStartsWithWhiteSpace) {
#ifndef NO_LOGS
debug(9, "Multipart: Boundary starts with white space, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
}
if (m.boundaryIsQuoted) {
#ifndef NO_LOGS
debug(9, "Multipart: Boundary is quoted, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
}
if (m.containsDataAfter) {
#ifndef NO_LOGS
debug(9, "Multipart: There is data after the boundary, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
store_variable("MULTIPART_UNMATCHED_BOUNDARY", "1");
} else {
store_variable("MULTIPART_UNMATCHED_BOUNDARY", "0");
}
if (m.containsDataBefore) {
#ifndef NO_LOGS
debug(9, "Multipart: There is data before the boundary, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
}
if (m.lf) {
#ifndef NO_LOGS
debug(9, "Multipart: Lines are LF-terminated, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
}
if (m.missingSemicolon) {
#ifndef NO_LOGS
debug(9, "Multipart: Boundary missing semicolon, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
}
if (m.invalidQuote) {
#ifndef NO_LOGS
debug(9, "Multipart: Invalid quote, " \
"setting MULTIPART_STRICT_ERROR to 1");
#endif
update_variable_first("MULTIPART_STRICT_ERROR", "1");
}
}
@ -682,7 +714,9 @@ int Assay::requestBodyFromFile(const char *path) {
std::string str;
if (request_body.is_open() == false) {
#ifndef NO_LOGS
debug(3, "Failed to open request body at: " + std::string(path));
#endif
return false;
}
@ -695,8 +729,10 @@ int Assay::requestBodyFromFile(const char *path) {
const char *buf = str.c_str();
int len = request_body.tellg();
#ifndef NO_LOGS
debug(9, "Adding request body: " + std::to_string(len) + " bytes. " \
"Limit set to: " + std::to_string(this->m_rules->requestBodyLimit));
#endif
return appendRequestBody(reinterpret_cast<const unsigned char*>(buf), len);
}
@ -704,25 +740,33 @@ int Assay::requestBodyFromFile(const char *path) {
int Assay::appendRequestBody(const unsigned char *buf, size_t len) {
int current_size = this->m_requestBody.tellp();
#ifndef NO_LOGS
debug(9, "Appending request body: " + std::to_string(len) + " bytes. " \
"Limit set to: " + std::to_string(this->m_rules->requestBodyLimit));
#endif
if (this->m_rules->requestBodyLimit > 0
&& this->m_rules->requestBodyLimit < len + current_size) {
store_variable("INBOUND_DATA_ERROR", "1");
#ifndef NO_LOGS
debug(5, "Request body is bigger than the maximum expected.");
#endif
if (this->m_rules->requestBodyLimitAction ==
Rules::BodyLimitAction::ProcessPartialBodyLimitAction) {
size_t spaceLeft = this->m_rules->requestBodyLimit - current_size;
this->m_requestBody.write(reinterpret_cast<const char*>(buf),
spaceLeft);
#ifndef NO_LOGS
debug(5, "Request body limit is marked to process partial");
#endif
return false;
} else {
if (this->m_rules->requestBodyLimitAction ==
Rules::BodyLimitAction::RejectBodyLimitAction) {
#ifndef NO_LOGS
debug(5, "Request body limit is marked to reject the " \
"request");
#endif
Action *a = new actions::Deny("deny");
a->temporaryAction = true;
actions.push_back(a);
@ -752,10 +796,14 @@ int Assay::appendRequestBody(const unsigned char *buf, size_t len) {
*
*/
int Assay::processResponseHeaders() {
#ifndef NO_LOGS
debug(4, "Starting phase RESPONSE_HEADERS. (SecRules 3)");
#endif
if (m_rules->secRuleEngine == Rules::DisabledRuleEngine) {
#ifndef NO_LOGS
debug(4, "Rule engine disabled, returning...");
#endif
return true;
}
@ -869,10 +917,14 @@ int Assay::addResponseHeader(const unsigned char *key, size_t key_n,
*
*/
int Assay::processResponseBody() {
#ifndef NO_LOGS
debug(4, "Starting phase RESPONSE_BODY. (SecRules 4)");
#endif
if (m_rules->secRuleEngine == Rules::DisabledRuleEngine) {
#ifndef NO_LOGS
debug(4, "Rule engine disabled, returning...");
#endif
return true;
}
@ -910,26 +962,34 @@ int Assay::processResponseBody() {
int Assay::appendResponseBody(const unsigned char *buf, size_t len) {
int current_size = this->m_responseBody.tellp();
#ifndef NO_LOGS
debug(9, "Appending response body: " + std::to_string(len + current_size)
+ " bytes. Limit set to: " +
std::to_string(this->m_rules->responseBodyLimit));
#endif
if (this->m_rules->responseBodyLimit > 0
&& this->m_rules->responseBodyLimit < len + current_size) {
store_variable("OUTBOUND_DATA_ERROR", "1");
#ifndef NO_LOGS
debug(5, "Response body is bigger than the maximum expected.");
#endif
if (this->m_rules->responseBodyLimitAction ==
Rules::BodyLimitAction::ProcessPartialBodyLimitAction) {
size_t spaceLeft = this->m_rules->responseBodyLimit - current_size;
this->m_responseBody.write(reinterpret_cast<const char*>(buf),
spaceLeft);
#ifndef NO_LOGS
debug(5, "Response body limit is marked to process partial");
#endif
return false;
} else {
if (this->m_rules->responseBodyLimitAction ==
Rules::BodyLimitAction::RejectBodyLimitAction) {
#ifndef NO_LOGS
debug(5, "Response body limit is marked to reject the " \
"request");
#endif
Action *a = new actions::Deny("deny");
a->temporaryAction = true;
actions.push_back(a);
@ -1005,10 +1065,14 @@ int Assay::getResponseBodyLenth() {
*
*/
int Assay::processLogging(int returned_code) {
#ifndef NO_LOGS
debug(4, "Starting phase LOGGING. (SecRules 5)");
#endif
if (m_rules->secRuleEngine == Rules::DisabledRuleEngine) {
#ifndef NO_LOGS
debug(4, "Rule engine disabled, returning...");
#endif
return true;
}
@ -1017,12 +1081,16 @@ int Assay::processLogging(int returned_code) {
/* If relevant, save this assay information at the audit_logs */
if (m_rules != NULL && m_rules->audit_log != NULL) {
#ifndef NO_LOGS
debug(8, "Checking if this request is suitable to be saved as an audit log.");
#endif
int parts = -1;
if (this->auditLogModifier.size() > 0)
{
#ifndef NO_LOGS
debug(4, "There was an audit log modifier for this transaction.");
#endif
std::list<std::pair<int, std::string>>::iterator it;
parts = this->m_rules->audit_log->m_parts;
for (it = auditLogModifier.begin(); it != auditLogModifier.end(); ++it) {
@ -1034,6 +1102,7 @@ int Assay::processLogging(int returned_code) {
}
}
}
#ifndef NO_LOGS
if (save_in_auditlog) {
debug(8, "This request was marked to be saved via auditlog action.");
}
@ -1042,6 +1111,7 @@ int Assay::processLogging(int returned_code) {
if (saved) {
debug(8, "Request was relevant to be saved.");
}
#endif
}
return true;
@ -1079,6 +1149,7 @@ void Assay::cleanup() {
bool Assay::intervention(ModSecurityIntervention *it) {
it->status = 200;
it->url = NULL;
it->disruptive = false;
if (actions.size() > 0) {
for (Action *a : actions) {
if (a->action_kind == Action::Kind::RunTimeOnlyIfMatchKind) {
@ -1405,50 +1476,15 @@ std::list<std::pair<std::string, std::string>>
std::list<std::pair<std::string, std::string>> l;
std::pair<std::string, std::string> pair;
auto range = m_variables_strings.equal_range(var);
for (auto it = range.first; it != range.second; ++it) {
pair = std::make_pair(std::string(var), std::string(it->second));
l.push_back(pair);
}
if (l.empty()) {
for (auto &x : m_variables_strings) {
if ((x.first.substr(0, var.size() + 1).compare(var + ":") != 0)
&& (x.first != var)) {
continue;
}
std::list<std::pair<std::string, std::string>> t;
t = resolve_variable(x.first);
for (std::pair<std::string, std::string> z : t) {
pair = std::make_pair(std::string(z.first),
std::string(z.second));
l.push_back(pair);
}
}
}
l = m_variables_strings.resolveVariable(var);
size_t ac = var.find(":");
if (ac != std::string::npos) {
/* It may be a collection */
for (auto &a : collections) {
auto range = a.second->equal_range(var);
for (auto it = range.first; it != range.second; ++it) {
pair = std::make_pair(std::string(var), std::string(it->second));
l.push_back(pair);
}
if (l.empty()) {
for (auto &x : *a.second) {
if ((x.first.substr(0, var.size() + 1).compare(var + ":") != 0)
&& (x.first != var)) {
continue;
}
std::list<std::pair<std::string, std::string>> t;
t = resolve_variable(x.first);
for (std::pair<std::string, std::string> z : t) {
pair = std::make_pair(std::string(z.first),
std::string(z.second));
l.push_back(pair);
}
std::list<std::pair<std::string, std::string>> l2 = a.second->resolveVariable(var);
if (l2.empty() == false) {
l.insert(l.end(), l2.begin(), l2.end());
}
}
}
@ -1500,12 +1536,14 @@ void Assay::setCollection(const std::string& collectionName,
try {
ModSecurityStringVariables *collection;
collection = collections.at(toupper(collectionName));
collection->storeOrUpdateVariable(toupper(collectionName) + ":"
collection = collections.at(collectionName);
collection->storeOrUpdateVariable(collectionName + ":"
+ variableName, targetValue);
} catch (...) {
#ifndef NO_LOGS
debug(9, "don't know any collection named: "
+ collectionName + ". it was created?");
#endif
return;
}
}

View File

@ -35,14 +35,18 @@ bool DetectSQLi::evaluate(Assay *assay, const std::string &input) {
if (issqli) {
// set_match_to_tx(msr, capture, fingerprint, 0);
if (assay) {
#ifndef NO_LOGS
assay->debug(4, "detected SQLi using libinjection with " \
"fingerprint '" + std::string(fingerprint) + "' at: '" +
input + "'");
#endif
}
} else {
if (assay) {
#ifndef NO_LOGS
assay->debug(9, "detected SQLi: not able to find an inject on '" +
input + "'");
#endif
}
}

View File

@ -32,12 +32,16 @@ bool DetectXSS::evaluate(Assay *assay, const std::string &input) {
if (is_xss) {
if (assay) {
#ifndef NO_LOGS
assay->debug(5, "detected XSS using libinjection.");
#endif
}
} else {
if (assay) {
#ifndef NO_LOGS
assay->debug(9, "libinjection was not able to " \
"find any XSS in: " + input);
#endif
}
}

View File

@ -67,18 +67,24 @@ namespace operators {
bool Operator::debug(Assay *assay, int x, std::string a) {
#ifndef NO_LOGS
assay->debug(x, a);
#endif
return true;
}
bool Operator::evaluate(Assay *assay) {
if (assay) {
#ifndef NO_LOGS
assay->debug(2, "Operator: " + this->op + \
" is not implemented or malfunctioning.");
#endif
} else {
#ifndef NO_LOGS
std::cerr << "Operator: " + this->op + \
" is not implemented or malfunctioning.";
#endif
}
return true;
}
@ -86,11 +92,15 @@ bool Operator::evaluate(Assay *assay) {
bool Operator::evaluate(Assay *assay, const std::string& a) {
if (assay) {
#ifndef NO_LOGS
assay->debug(2, "Operator: " + this->op + \
" is not implemented or malfunctioning.");
#endif
} else {
#ifndef NO_LOGS
std::cerr << "Operator: " + this->op + \
" is not implemented or malfunctioning.";
#endif
}
return true;

View File

@ -77,29 +77,37 @@ bool ValidateUrlEncoding::evaluate(Assay *assay, const std::string &input) {
case 1 :
/* Encoding is valid */
if (assay) {
#ifndef NO_LOGS
assay->debug(7, "Valid URL Encoding at '" +input + "'");
#endif
}
res = false;
break;
case -2 :
if (assay) {
#ifndef NO_LOGS
assay->debug(7, "Invalid URL Encoding: Non-hexadecimal "
"digits used at '" + input + "'");
#endif
}
res = true; /* Invalid match. */
break;
case -3 :
if (assay) {
#ifndef NO_LOGS
assay->debug(7, "Invalid URL Encoding: Not enough characters "
"at the end of input at '" + input + "'");
#endif
}
res = true; /* Invalid match. */
break;
case -1 :
default :
if (assay) {
#ifndef NO_LOGS
assay->debug(7, "Invalid URL Encoding: Internal Error (rc = " +
std::to_string(rc) + ") at '" + input + "'");
#endif
}
res = true;
break;

View File

@ -125,45 +125,55 @@ bool ValidateUtf8Encoding::evaluate(Assay *assay, const std::string &str) {
switch (rc) {
case UNICODE_ERROR_CHARACTERS_MISSING :
if (assay) {
#ifndef NO_LOGS
assay->debug(8, "Invalid UTF-8 encoding: "
"not enough bytes in character "
"at " + str + ". [offset \"" +
std::to_string(i) + "\"]");
#endif
}
return true;
break;
case UNICODE_ERROR_INVALID_ENCODING :
if (assay) {
#ifndef NO_LOGS
assay->debug(8, "Invalid UTF-8 encoding: "
"invalid byte value in character "
"at " + str + ". [offset \"" +
std::to_string(i) + "\"]");
#endif
}
return true;
break;
case UNICODE_ERROR_OVERLONG_CHARACTER :
if (assay) {
#ifndef NO_LOGS
assay->debug(8, "Invalid UTF-8 encoding: "
"overlong character detected "
"at " + str + ". [offset \"" +
std::to_string(i) + "\"]");
#endif
}
return true;
break;
case UNICODE_ERROR_RESTRICTED_CHARACTER :
if (assay) {
#ifndef NO_LOGS
assay->debug(8, "Invalid UTF-8 encoding: "
"use of restricted character "
"at " + str + ". [offset \"" +
std::to_string(i) + "\"]");
#endif
}
return true;
break;
case UNICODE_ERROR_DECODING_ERROR :
if (assay) {
#ifndef NO_LOGS
assay->debug(8, "Error validating UTF-8 decoding "
"at " + str + ". [offset \"" +
std::to_string(i) + "\"]");
#endif
}
return true;
break;
@ -171,9 +181,11 @@ bool ValidateUtf8Encoding::evaluate(Assay *assay, const std::string &str) {
if (rc <= 0) {
if (assay) {
#ifndef NO_LOGS
assay->debug(8, "Internal error during UTF-8 validation "
"at " + str + ". [offset \"" +
std::to_string(i) + "\"]");
#endif
}
return true;
}

View File

@ -79,9 +79,11 @@ bool VerifyCC::evaluate(Assay *assay, const std::string &i) {
is_cc = luhnVerify(match.c_str(), match.size());
if (is_cc) {
if (assay) {
#ifndef NO_LOGS
assay->debug(9, "CC# match \"" + param +
"\" at " + i + ". [offset " +
std::to_string(offset) + "]");
#endif
}
return true;
}

View File

@ -40,15 +40,19 @@ Multipart::Multipart(std:: string header, Assay *assay)
bool Multipart::init() {
if (m_header.length() > 1024) {
#ifndef NO_LOGS
debug(4, "Multipart: Invalid boundary in Content-Type (length).");
#endif
return false;
}
std::size_t boundary_pos = m_header.find("boundary");
if (boundary_pos != std::string::npos &&
m_header.find("boundary", boundary_pos + 1) != std::string::npos) {
#ifndef NO_LOGS
debug(4, "Multipart: Multiple boundary parameters in " \
"Content-Type.");
#endif
return false;
}
@ -56,35 +60,47 @@ bool Multipart::init() {
std::size_t semicolon_pos = boundary.find(";");
if (semicolon_pos != std::string::npos
&& boundary.find(";", semicolon_pos + 1) != std::string::npos) {
#ifndef NO_LOGS
debug(4, "Multipart: Invalid boundary in Content-Type. (malformed). " \
"Too many semicolons.");
#endif
return false;
}
if (semicolon_pos == std::string::npos) {
#ifndef NO_LOGS
debug(4, "Multipart: Missing semicolon.");
#endif
this->missingSemicolon = true;
}
if (boundary.at(8) != '=') {
#ifndef NO_LOGS
debug(4, "Multipart: Invalid boundary in Content-Type. (malformed). " \
"Missing equals.");
#endif
return false;
}
if (boundary.at(8 + 1) == ' ') {
boundaryStartsWithWhiteSpace = true;
#ifndef NO_LOGS
debug(4, "Multipart: Boundary starts with a white space");
#endif
}
if ((boundaryStartsWithWhiteSpace && boundary.at(8 + 2) == '"') ||
(!boundaryStartsWithWhiteSpace && boundary.at(8 + 1) == '"')) {
boundaryIsQuoted = true;
#ifndef NO_LOGS
debug(4, "Multipart: Boundary inside quotes");
#endif
}
if (boundaryIsQuoted && boundary.at(boundary.length()-1) != '"') {
#ifndef NO_LOGS
debug(4, "Multipart: Invalid boundary in Content-type (quote).");
#endif
return false;
}
@ -116,8 +132,10 @@ bool Multipart::init() {
}
if (boundaryContainsOnlyValidCharacters() == false) {
#ifndef NO_LOGS
debug(4, "Multipart: Invalid boundary in Content-type " \
"(invalid characters).");
#endif
return false;
}
@ -190,7 +208,9 @@ bool Multipart::process(std::string data) {
double files_size = 0;
if (start != 0) {
#ifndef NO_LOGS
debug(4, "Multipart: Boundary was not the first thing.");
#endif
this->containsDataBefore = true;
}
while (start != std::string::npos) {
@ -230,7 +250,9 @@ bool Multipart::process(std::string data) {
int i = 0;
for (std::string x : blobs) {
i++;
#ifndef NO_LOGS
debug(5, "Multipart: Inspecting blob: " + std::to_string(i));
#endif
MultipartBlob m(x, this);
if (m.name.empty() == false) {
@ -249,11 +271,15 @@ bool Multipart::process(std::string data) {
variables.emplace("FILES_NAMES:" + name, name);
variables.emplace("FILES_SIZES:" + name,
std::to_string(m.content.size()));
#ifndef NO_LOGS
debug(5, "Multipart: Saving FILES_TMP_CONTENT:" + name + " variable.");
#endif
variables.emplace("FILES_TMP_CONTENT:" + name, m.content);
files_size = files_size + m.content.size();
if (m.invalidQuote) {
#ifndef NO_LOGS
debug(4, "Multipart: Found invalid quoting.");
#endif
this->invalidQuote = true;
}
}

View File

@ -45,10 +45,11 @@ class Multipart {
bool boundaryIsQuoted;
bool missingSemicolon;
bool invalidQuote;
#ifndef NO_LOGS
void debug(int a, std::string str) {
m_assay->debug(a, str);
}
#endif
private:
std::string m_boundary;
std::string m_header;

View File

@ -37,14 +37,18 @@ bool MultipartBlob::processContent() {
end = m_blob.find("\n", offset);
if (end == std::string::npos) {
#ifndef NO_LOGS
debug(4, "Missing end of line");
#endif
return false;
}
std::string firstLine = std::string(m_blob, offset, end);
offset = end + 1;
end = m_blob.find("\n", offset);
if (end == std::string::npos) {
#ifndef NO_LOGS
debug(4, "Missing end of line");
#endif
return false;
}
std::string secondLine = std::string(m_blob, offset, end - offset);
@ -63,7 +67,9 @@ bool MultipartBlob::processContent() {
if (contentType.empty() == false) {
end = m_blob.find_first_of("\n", offset);
if (end == std::string::npos) {
#ifndef NO_LOGS
debug(4, "Missing end of line");
#endif
return false;
}
offset = end + 1;
@ -94,7 +100,9 @@ bool MultipartBlob::processContentDispositionLine(
if (dispositionLine.size() < 30 ||
dispositionLine.compare(21, 9, "form-data") != 0) {
#ifndef NO_LOGS
debug(4, "Multipart: Content-Disposition is unknown");
#endif
return false;
}

View File

@ -37,9 +37,12 @@ class MultipartBlob {
std::string filename;
std::string contentType;
std::string content;
#ifndef NO_LOGS
void debug(int a, std::string str) {
m_parent->debug(a, str);
}
#endif
private:
const std::string m_blob;
Multipart *m_parent;

View File

@ -138,7 +138,9 @@ bool Rule::evaluateActions(Assay *assay) {
}
}
#ifndef NO_LOGS
assay->debug(4, "Running unconditional rule.");
#endif
if (none == 0) {
/*
@ -172,7 +174,9 @@ bool Rule::evaluateActions(Assay *assay) {
for (Action *a : this->actions_runtime_pos) {
if (a->isDisruptive() == false) {
#ifndef NO_LOGS
assay->debug(4, "Running (_non_ disruptive) action: " + a->action);
#endif
a->evaluate(this, assay);
} else {
containsDisruptive = true;
@ -183,28 +187,36 @@ bool Rule::evaluateActions(Assay *assay) {
if (a->action_kind == actions::Action::RunTimeOnlyIfMatchKind) {
if (a->isDisruptive()) {
if (containsDisruptive) {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) " \
"_ignoring_ action: " + a->action + \
" (rule contains a disruptive action)");
#endif
} else {
if (assay->m_rules->secRuleEngine
== Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) " \
"Running action: " + a->action + \
" (rule _does not_ contains a " \
"disruptive action)");
#endif
a->evaluate(this, assay);
} else {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) " \
"_Not_ running action: " + a->action + \
". Rule _does not_ contains a " \
"disruptive action, but SecRuleEngine is not On.");
#endif
}
}
} else {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) Running action: " + \
a->action);
a->evaluate(this, assay);
#endif
}
}
}
@ -212,11 +224,15 @@ bool Rule::evaluateActions(Assay *assay) {
for (Action *a : this->actions_runtime_pos) {
if (a->isDisruptive()
&& assay->m_rules->secRuleEngine == Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
assay->debug(4, "Running (disruptive) action: " + a->action);
#endif
a->evaluate(this, assay);
} else if (a->isDisruptive()) {
#ifndef NO_LOGS
assay->debug(4, "Not running disruptive action: " + \
a->action + ". SecRuleEngine is not On");
#endif
}
}
@ -235,6 +251,7 @@ bool Rule::evaluate(Assay *assay) {
return evaluateActions(assay);
}
#ifndef NO_LOGS
std::string eparam = MacroExpansion::expand(this->op->param, assay);
if (this->op->param != eparam) {
@ -251,6 +268,7 @@ bool Rule::evaluate(Assay *assay) {
+ Variable::to_s(variables) + ".");
clock_t begin = clock();
#endif
std::list<std::string> exclusions;
for (int i = 0; i < variables->size(); i++) {
@ -281,8 +299,10 @@ bool Rule::evaluate(Assay *assay) {
for (auto &v : e) {
if (std::find(exclusions.begin(), exclusions.end(),
v.first) != exclusions.end()) {
#ifndef NO_LOGS
assay->debug(9, "Variable: " + v.first + " is part of the" +
" exclusion list, skipping...");
#endif
continue;
}
std::string value = v.second;
@ -301,9 +321,11 @@ bool Rule::evaluate(Assay *assay) {
for (Action *a : assay->m_rules->defaultActions[this->phase]) {
if (a->action_kind == actions::Action::RunTimeBeforeMatchAttemptKind) {
value = a->evaluate(value, assay);
#ifndef NO_LOGS
assay->debug(9, "(SecDefaultAction) T (" + \
std::to_string(transformations) + ") " + \
a->name + ": \"" + value +"\"");
#endif
transformations++;
}
}
@ -313,9 +335,11 @@ bool Rule::evaluate(Assay *assay) {
None *z = dynamic_cast<None *>(a);
if (none == 0) {
value = a->evaluate(value, assay);
#ifndef NO_LOGS
assay->debug(9, " T (" + \
std::to_string(transformations) + ") " + \
a->name + ": \"" + value +"\"");
#endif
transformations++;
}
if (z != NULL) {
@ -323,27 +347,35 @@ bool Rule::evaluate(Assay *assay) {
}
}
#ifndef NO_LOGS
assay->debug(9, "Target value: \"" + limitTo(80, toHexIfNeeded(value)) + \
"\" (Variable: " + v.first + ")");
#endif
ret = this->op->evaluate(assay, value);
#ifndef NO_LOGS
clock_t end = clock();
double elapsed_secs = static_cast<double>(end - begin) \
/ CLOCKS_PER_SEC;
assay->debug(4, "Operator completed in " + \
std::to_string(elapsed_secs) + " seconds");
#endif
if (ret) {
bool containsDisruptive = false;
bool chainResult = false;
#ifndef NO_LOGS
assay->debug(4, "Rule returned 1.");
#endif
for (Action *a :
this->actions_runtime_pos) {
if (a->isDisruptive() == false) {
#ifndef NO_LOGS
assay->debug(4, "Running (_non_ disruptive) action: " + a->action);
#endif
a->evaluate(this, assay);
} else {
containsDisruptive = true;
@ -351,11 +383,15 @@ bool Rule::evaluate(Assay *assay) {
}
if (this->chained && this->chainedRule == NULL) {
#ifndef NO_LOGS
assay->debug(4, "Rule is marked as chained but there " \
"isn't a subsequent rule.");
#endif
}
if (this->chained && this->chainedRule != NULL) {
#ifndef NO_LOGS
assay->debug(4, "Executing chained rule.");
#endif
if (assay->update_variable_first("MATCHED_VAR",
value) == false) {
assay->store_variable("MATCHED_VAR", value);
@ -378,27 +414,35 @@ bool Rule::evaluate(Assay *assay) {
if (a->action_kind == actions::Action::RunTimeOnlyIfMatchKind) {
if (a->isDisruptive()) {
if (containsDisruptive) {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) " \
"_ignoring_ action: " + a->action + \
" (rule contains a disruptive action)");
#endif
} else {
if (assay->m_rules->secRuleEngine
== Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) " \
"Running action: " + a->action + \
" (rule _does not_ contains a " \
"disruptive action)");
#endif
a->evaluate(this, assay);
} else {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) " \
"_Not_ running action: " + a->action + \
". Rule _does not_ contains a " \
"disruptive action, but SecRuleEngine is not On.");
#endif
}
}
} else {
#ifndef NO_LOGS
assay->debug(4, "(SecDefaultAction) Running " \
"action: " + a->action + "!!" + std::to_string(a->isDisruptive()));
#endif
a->evaluate(this, assay);
}
}
@ -408,19 +452,25 @@ bool Rule::evaluate(Assay *assay) {
if (a->isDisruptive()
&& assay->m_rules->secRuleEngine
== Rules::EnabledRuleEngine) {
#ifndef NO_LOGS
assay->debug(4, "Running (disruptive) action: " + \
a->action);
#endif
a->evaluate(this, assay);
} else if (a->isDisruptive()) {
#ifndef NO_LOGS
assay->debug(4,
"Not running disruptive action: " + \
a->action + ". SecRuleEngine is not On");
#endif
}
}
}
} else {
#ifndef NO_LOGS
assay->debug(4, "Rule returned 0.");
#endif
}
}
}

View File

@ -66,7 +66,7 @@ char ip[] = "200.249.12.31";
char rules_file[] = "basic_rules.conf";
#define NUM_REQUESTS 1000
#define NUM_REQUESTS 10000
int main(int argc, char *argv[]) {
int i = 0;
@ -79,10 +79,14 @@ int main(int argc, char *argv[]) {
" (ModSecurity benchmark utility)");
rules = new ModSecurity::Rules();
rules->loadFromUri(rules_file);
if (rules->loadFromUri(rules_file) < 0) {
std::cout << "Problems loading the rules..." << std::endl;
std::cout << rules->parserError.str() << std::endl;
return -1;
}
for (i = 0; i < NUM_REQUESTS; i++) {
std::cout << "Proceding with request " << i << std::endl;
std::cout << "Proceeding with request " << i << std::endl;
Assay *modsecAssay = new Assay(modsec, rules, NULL);
modsecAssay->processConnection(ip, 12345, "127.0.0.1", 80);