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:
Felipe Zimmerle 2016-06-16 23:14:15 -03:00
parent 7bd6e9a2bd
commit ebe8424758
No known key found for this signature in database
GPG Key ID: E6DFB08CE8B11277
6 changed files with 120 additions and 29 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}
/**