mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-13 13:26:01 +03:00
Adds support to REQBODY_ERROR_MSG and REQBODY_ERROR
Support to REQBODY_PROCESSOR_ERROR and REQBODY_PROCESSOR_ERROR_MSG were also added.
This commit is contained in:
parent
7bd6e9a2bd
commit
ebe8424758
@ -215,3 +215,4 @@ TESTS+=test/test-cases/secrules-language-tests/operators/strmatch.json
|
||||
TESTS+=test/test-cases/secrules-language-tests/operators/detectXSS.json
|
||||
TESTS+=test/test-cases/secrules-language-tests/operators/eq.json
|
||||
TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR.json
|
||||
TESTS+=test/test-cases/regression/variable-REQBODY_PROCESSOR_ERROR.json
|
||||
|
@ -429,7 +429,7 @@ int Multipart::tmp_file_name(std::string *filename) {
|
||||
}
|
||||
|
||||
|
||||
int Multipart::process_part_data() {
|
||||
int Multipart::process_part_data(std::string *error) {
|
||||
char *p = m_buf + (MULTIPART_BUF_SIZE - m_bufleft);
|
||||
char localreserve[2] = { '\0', '\0' }; /* initialized to quiet warning */
|
||||
int bytes_reserved = 0;
|
||||
@ -470,6 +470,9 @@ int Multipart::process_part_data() {
|
||||
debug(1, "Multipart: Upload file limit exceeded " \
|
||||
+ std::to_string(m_transaction->m_rules->uploadFileLimit) \
|
||||
+ ". Use SecUploadFileLimit to change the limit.");
|
||||
error->assign("Multipart: Upload file limit exceeded " \
|
||||
+ std::to_string(m_transaction->m_rules->uploadFileLimit) \
|
||||
+ ". Use SecUploadFileLimit to change the limit.");
|
||||
m_flag_file_limit_exceeded = 1;
|
||||
}
|
||||
extract = 0;
|
||||
@ -490,6 +493,8 @@ int Multipart::process_part_data() {
|
||||
if (m_mpp->m_tmp_file_fd < 0) {
|
||||
debug(1, "Multipart: Failed to create file: " \
|
||||
+ m_mpp->m_tmp_file_name);
|
||||
error->assign("Multipart: Failed to create file: " \
|
||||
+ m_mpp->m_tmp_file_name);
|
||||
return -1;
|
||||
}
|
||||
/* keep track of the files count */
|
||||
@ -507,6 +512,8 @@ int Multipart::process_part_data() {
|
||||
!= m_reserve[0]) {
|
||||
debug(1, "Multipart: writing to \"" \
|
||||
+ m_mpp->m_tmp_file_name + "\" failed");
|
||||
error->assign("Multipart: writing to \"" \
|
||||
+ m_mpp->m_tmp_file_name + "\" failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -521,6 +528,8 @@ int Multipart::process_part_data() {
|
||||
!= (MULTIPART_BUF_SIZE - m_bufleft)) {
|
||||
debug(1, "Multipart: writing to \"" \
|
||||
+ m_mpp->m_tmp_file_name + "\" failed");
|
||||
error->assign("Multipart: writing to \"" \
|
||||
+ m_mpp->m_tmp_file_name + "\" failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -562,6 +571,8 @@ int Multipart::process_part_data() {
|
||||
} else {
|
||||
debug(1, "Multipart: unknown part type: " \
|
||||
+ std::to_string(m_mpp->m_type));
|
||||
error->assign("Multipart: unknown part type: " \
|
||||
+ std::to_string(m_mpp->m_type));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -582,7 +593,7 @@ int Multipart::process_part_data() {
|
||||
}
|
||||
|
||||
|
||||
int Multipart::process_part_header() {
|
||||
int Multipart::process_part_header(std::string *error) {
|
||||
int i, len, rc;
|
||||
|
||||
/* Check for nul bytes. */
|
||||
@ -590,6 +601,7 @@ int Multipart::process_part_header() {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (m_buf[i] == '\0') {
|
||||
debug(1, "Multipart: Nul byte in part headers.");
|
||||
error->assign("Multipart: Nul byte in part headers.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -614,7 +626,7 @@ int Multipart::process_part_header() {
|
||||
|
||||
if (m_mpp->m_headers.count("Content-Disposition") == 0) {
|
||||
debug(1, "Multipart: Part missing Content-Disposition header.");
|
||||
|
||||
error->assign("Multipart: Part missing Content-Disposition header.");
|
||||
return false;
|
||||
}
|
||||
header_value = m_mpp->m_headers.at("Content-Disposition");
|
||||
@ -623,13 +635,16 @@ int Multipart::process_part_header() {
|
||||
if (rc < 0) {
|
||||
debug(1, "Multipart: Invalid Content-Disposition header ("
|
||||
+ std::to_string(rc) + "): " + header_value);
|
||||
|
||||
error->assign("Multipart: Invalid Content-Disposition header ("
|
||||
+ std::to_string(rc) + "): " + header_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_mpp->m_name.empty()) {
|
||||
debug(1, "Multipart: Content-Disposition header missing " \
|
||||
"name field.");
|
||||
error->assign("Multipart: Content-Disposition header missing " \
|
||||
"name field.");
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -642,6 +657,8 @@ int Multipart::process_part_header() {
|
||||
if (strstr(header_value.c_str(), "filename=") == NULL) {
|
||||
debug(1, "Multipart: Invalid Content-Disposition " \
|
||||
"header (filename).");
|
||||
error->assign("Multipart: Invalid Content-Disposition " \
|
||||
"header (filename).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -672,6 +689,8 @@ int Multipart::process_part_header() {
|
||||
if (m_mpp->m_last_header_name.empty()) {
|
||||
/* we are not building a header at this moment */
|
||||
debug(1, "Multipart: Invalid part header (folding error).");
|
||||
error->assign("Multipart: Invalid part header " \
|
||||
"(folding error).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -701,6 +720,7 @@ int Multipart::process_part_header() {
|
||||
|
||||
if (new_value.size() > MULTIPART_BUF_SIZE) {
|
||||
debug(1, "Multipart: Part header too long.");
|
||||
error->assign("Multipart: Part header too long.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
@ -716,6 +736,8 @@ int Multipart::process_part_header() {
|
||||
if (*data == '\0') {
|
||||
debug(1, "Multipart: Invalid part header (colon missing): " \
|
||||
+ std::string(m_buf));
|
||||
error->assign("Multipart: Invalid part header (colon missing): " \
|
||||
+ std::string(m_buf));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -724,6 +746,8 @@ int Multipart::process_part_header() {
|
||||
if (data == m_buf) {
|
||||
debug(1, "Multipart: Invalid part header " \
|
||||
"(header name missing).");
|
||||
error->assign("Multipart: Invalid part header " \
|
||||
"(header name missing).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -822,7 +846,7 @@ int Multipart::process_boundary(int last_part) {
|
||||
* Finalize multipart processing. This method is invoked at the end, when it
|
||||
* is clear that there is no more data to be processed.
|
||||
*/
|
||||
int Multipart::multipart_complete() {
|
||||
int Multipart::multipart_complete(std::string *error) {
|
||||
m_transaction->m_collections.store("MULTIPART_UNMATCHED_BOUNDARY",
|
||||
std::to_string(m_flag_unmatched_boundary));
|
||||
|
||||
@ -929,10 +953,12 @@ int Multipart::multipart_complete() {
|
||||
|
||||
if (m_is_complete == 0) {
|
||||
debug(1, "Multipart: Final boundary missing.");
|
||||
error->assign("Multipart: Final boundary missing.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
debug(1, "Multipart: No boundaries found in payload.");
|
||||
error->assign("Multipart: No boundaries found in payload.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1030,7 +1056,7 @@ int Multipart::count_boundary_params(const std::string& str_header_value) {
|
||||
}
|
||||
|
||||
|
||||
bool Multipart::init() {
|
||||
bool Multipart::init(std::string *error) {
|
||||
m_bufleft = MULTIPART_BUF_SIZE;
|
||||
m_bufptr = m_buf;
|
||||
m_buf_contains_line = true;
|
||||
@ -1040,18 +1066,21 @@ bool Multipart::init() {
|
||||
if (m_header.empty()) {
|
||||
m_flag_error = true;
|
||||
debug(4, "Multipart: Content-Type header not available.");
|
||||
error->assign("Multipart: Content-Type header not available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_header.size() > 1024) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T (length).");
|
||||
error->assign("Multipart: Invalid boundary in C-T (length).");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncasecmp(m_header.c_str(), "multipart/form-data", 19) != 0) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid MIME type.");
|
||||
error->assign("Multipart: Invalid MIME type.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1059,6 +1088,7 @@ bool Multipart::init() {
|
||||
if (count_boundary_params(m_header) > 1) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Multiple boundary parameters in C-T.");
|
||||
error->assign("Multipart: Multiple boundary parameters in C-T.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1080,6 +1110,8 @@ bool Multipart::init() {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T " \
|
||||
"(malformed).");
|
||||
error->assign("Multipart: Invalid boundary in C-T " \
|
||||
"(malformed).");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1094,6 +1126,7 @@ bool Multipart::init() {
|
||||
if (b == NULL) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T (malformed).");
|
||||
error->assign("Multipart: Invalid boundary in C-T (malformed).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1110,6 +1143,8 @@ bool Multipart::init() {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T " \
|
||||
"(parameter name).");
|
||||
error->assign("Multipart: Invalid boundary in C-T " \
|
||||
"(parameter name).");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1139,6 +1174,7 @@ bool Multipart::init() {
|
||||
|| ((len >= 2) && (*(b + len - 1) == '"'))) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T (quote).");
|
||||
error->assign("Multipart: Invalid boundary in C-T (quote).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1154,6 +1190,7 @@ bool Multipart::init() {
|
||||
if (count_boundary_params(m_boundary.c_str()) != 0) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T (content).");
|
||||
error->assign("Multipart: Invalid boundary in C-T (content).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1161,6 +1198,7 @@ bool Multipart::init() {
|
||||
if (boundary_characters_valid(m_boundary.c_str()) != 1) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T (characters).");
|
||||
error->assign("Multipart: Invalid boundary in C-T (characters).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1172,6 +1210,7 @@ bool Multipart::init() {
|
||||
if (m_boundary.size() == 0) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary in C-T (empty).");
|
||||
error->assign("Multipart: Invalid boundary in C-T (empty).");
|
||||
return false;
|
||||
}
|
||||
} else { /* Could not find boundary in the C-T header. */
|
||||
@ -1181,10 +1220,13 @@ bool Multipart::init() {
|
||||
* highly unusual. */
|
||||
if (count_boundary_params(m_header) > 0) {
|
||||
debug(4, "Multipart: Invalid boundary in C-T (case sensitivity).");
|
||||
error->assign("Multipart: Invalid boundary in C-T " \
|
||||
"(case sensitivity).");
|
||||
return false;
|
||||
}
|
||||
|
||||
debug(4, "Multipart: Boundary not found in C-T.");
|
||||
error->assign("Multipart: Boundary not found in C-T.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1196,7 +1238,7 @@ bool Multipart::init() {
|
||||
* Assuming that all data is on data. We are not processing chunks.
|
||||
*
|
||||
*/
|
||||
bool Multipart::process(const std::string& data) {
|
||||
bool Multipart::process(const std::string& data, std::string *error) {
|
||||
const char *inptr = data.c_str();
|
||||
unsigned int inleft = data.size();
|
||||
|
||||
@ -1265,6 +1307,8 @@ bool Multipart::process(const std::string& data) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary " \
|
||||
"(final duplicate).");
|
||||
error->assign("Multipart: Invalid boundary " \
|
||||
"(final duplicate).");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1297,6 +1341,8 @@ bool Multipart::process(const std::string& data) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary: " \
|
||||
+ std::string(m_buf));
|
||||
error->assign("Multipart: Invalid boundary: " \
|
||||
+ std::string(m_buf));
|
||||
return false;
|
||||
}
|
||||
} else { /* It looks like a boundary but */
|
||||
@ -1312,6 +1358,7 @@ bool Multipart::process(const std::string& data) {
|
||||
m_boundary.size()) == 0)) {
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary (quotes).");
|
||||
error->assign("Multipart: Invalid boundary (quotes).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1327,6 +1374,8 @@ bool Multipart::process(const std::string& data) {
|
||||
/* Found whitespace in front of a boundary. */
|
||||
m_flag_error = 1;
|
||||
debug(4, "Multipart: Invalid boundary (whitespace).");
|
||||
error->assign("Multipart: Invalid boundary " \
|
||||
"(whitespace).");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1370,16 +1419,19 @@ bool Multipart::process(const std::string& data) {
|
||||
debug(4, "Multipart: Part header line over " \
|
||||
+ std::to_string(MULTIPART_BUF_SIZE) \
|
||||
+ " bytes long");
|
||||
error->assign("Multipart: Part header line over " \
|
||||
+ std::to_string(MULTIPART_BUF_SIZE) \
|
||||
+ " bytes long");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (process_part_header() < 0) {
|
||||
if (process_part_header(error) < 0) {
|
||||
m_flag_error = 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (process_part_data() < 0) {
|
||||
if (process_part_data(error) < 0) {
|
||||
m_flag_error = 1;
|
||||
return false;
|
||||
}
|
||||
|
@ -100,18 +100,18 @@ class Multipart {
|
||||
Multipart(std::string header, Transaction *transaction);
|
||||
~Multipart();
|
||||
|
||||
bool init();
|
||||
bool init(std::string *err);
|
||||
|
||||
int boundary_characters_valid(const char *boundary);
|
||||
int count_boundary_params(const std::string& str_header_value);
|
||||
int is_token_char(unsigned char c);
|
||||
int multipart_complete();
|
||||
int multipart_complete(std::string *err);
|
||||
|
||||
int parse_content_disposition(const char *c_d_value);
|
||||
bool process(const std::string& data);
|
||||
bool process(const std::string& data, std::string *err);
|
||||
int process_boundary(int last_part);
|
||||
int process_part_header();
|
||||
int process_part_data();
|
||||
int process_part_header(std::string *error);
|
||||
int process_part_data(std::string *error);
|
||||
|
||||
int tmp_file_name(std::string *filename);
|
||||
|
||||
|
@ -60,7 +60,8 @@ xmlParserInputBufferPtr XML::unloadExternalEntity(const char *URI,
|
||||
}
|
||||
|
||||
|
||||
bool XML::processChunk(const char *buf, unsigned int size) {
|
||||
bool XML::processChunk(const char *buf, unsigned int size,
|
||||
std::string *error) {
|
||||
/* We want to initialise our parsing context here, to
|
||||
* enable us to pass it the first chunk of data so that
|
||||
* it can attempt to auto-detect the encoding.
|
||||
@ -88,6 +89,7 @@ bool XML::processChunk(const char *buf, unsigned int size) {
|
||||
|
||||
if (m_data.parsing_ctx == NULL) {
|
||||
debug(4, "XML: Failed to create parsing context.");
|
||||
error->assign("XML: Failed to create parsing context.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -96,6 +98,7 @@ bool XML::processChunk(const char *buf, unsigned int size) {
|
||||
/* Not a first invocation. */
|
||||
xmlParseChunk(m_data.parsing_ctx, buf, size, 0);
|
||||
if (m_data.parsing_ctx->wellFormed != 1) {
|
||||
error->assign("XML: Failed to create parsing context.");
|
||||
debug(4, "XML: Failed parsing document.");
|
||||
return false;
|
||||
}
|
||||
@ -104,7 +107,7 @@ bool XML::processChunk(const char *buf, unsigned int size) {
|
||||
}
|
||||
|
||||
|
||||
bool XML::complete() {
|
||||
bool XML::complete(std::string *error) {
|
||||
/* Only if we have a context, meaning we've done some work. */
|
||||
if (m_data.parsing_ctx != NULL) {
|
||||
/* This is how we signalise the end of parsing to libxml. */
|
||||
@ -121,6 +124,7 @@ bool XML::complete() {
|
||||
+ std::to_string(m_data.well_formed) + ").");
|
||||
|
||||
if (m_data.well_formed != 1) {
|
||||
error->assign("XML: Failed parsing document.");
|
||||
debug(4, "XML: Failed parsing document.");
|
||||
return false;
|
||||
}
|
||||
|
@ -46,8 +46,8 @@ class XML {
|
||||
explicit XML(Transaction *transaction);
|
||||
~XML();
|
||||
bool init();
|
||||
bool processChunk(const char *buf, unsigned int size);
|
||||
bool complete();
|
||||
bool processChunk(const char *buf, unsigned int size, std::string *err);
|
||||
bool complete(std::string *err);
|
||||
static xmlParserInputBufferPtr unloadExternalEntity(const char *URI,
|
||||
xmlCharEncoding enc);
|
||||
|
||||
|
@ -589,27 +589,48 @@ int Transaction::processRequestBody() {
|
||||
*/
|
||||
|
||||
if (m_requestBodyProcessor == XMLRequestBody) {
|
||||
std::string error;
|
||||
if (m_xml->init() == true) {
|
||||
m_xml->processChunk(m_requestBody.str().c_str(),
|
||||
m_requestBody.str().size());
|
||||
m_xml->complete();
|
||||
m_requestBody.str().size(),
|
||||
&error);
|
||||
m_xml->complete(&error);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_requestBodyType == MultiPartRequestBody) {
|
||||
if (error.empty() == false) {
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR", "1");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR", "1");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR_MSG",
|
||||
"XML parsing error: " + error);
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR_MSG",
|
||||
"XML parsing error: " + error);
|
||||
} else {
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR", "0");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR", "0");
|
||||
}
|
||||
} else if (m_requestBodyType == MultiPartRequestBody) {
|
||||
std::string error;
|
||||
std::string *a = m_collections.resolveFirst(
|
||||
"REQUEST_HEADERS:Content-Type");
|
||||
if (a != NULL) {
|
||||
Multipart m(*a, this);
|
||||
|
||||
if (m.init() == true) {
|
||||
m.process(m_requestBody.str());
|
||||
if (m.init(&error) == true) {
|
||||
m.process(m_requestBody.str(), &error);
|
||||
}
|
||||
m.multipart_complete();
|
||||
m.multipart_complete(&error);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_requestBodyType == WWWFormUrlEncoded) {
|
||||
if (error.empty() == false) {
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR", "1");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR", "1");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR_MSG",
|
||||
"Multipart parsing error: " + error);
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR_MSG",
|
||||
"Multipart parsing error: " + error);
|
||||
} else {
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR", "0");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR", "0");
|
||||
}
|
||||
} else if (m_requestBodyType == WWWFormUrlEncoded) {
|
||||
std::string content = uri_decode(m_requestBody.str());
|
||||
if (content.empty() == false) {
|
||||
content.pop_back();
|
||||
@ -674,6 +695,19 @@ int Transaction::processRequestBody() {
|
||||
this->m_ARGScombinedSizeStr->assign(
|
||||
std::to_string(this->m_ARGScombinedSize));
|
||||
}
|
||||
} else {
|
||||
std::string *a = m_collections.resolveFirst(
|
||||
"REQUEST_HEADERS:Content-Type");
|
||||
std::string error;
|
||||
if (a != NULL && a->empty() == false) {
|
||||
error.assign(*a);
|
||||
}
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR", "1");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR", "1");
|
||||
m_collections.storeOrUpdateFirst("REQBODY_ERROR_MSG",
|
||||
"Unknown request body processor: " + error);
|
||||
m_collections.storeOrUpdateFirst("REQBODY_PROCESSOR_ERROR_MSG",
|
||||
"Unknown request body processor: " + error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user