Adds support for basic Multipart process

Adjustments will be needed, for instance: the logging support is still missing
This commit is contained in:
Felipe Zimmerle
2015-07-17 15:12:15 -03:00
parent d0b7a9966d
commit 5d5e10bfde
8 changed files with 518 additions and 6 deletions

View File

@@ -0,0 +1,221 @@
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 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.
*
*/
#include "request_body_processor/multipart.h"
#include <list>
#include <iostream>
#include <string>
#include "request_body_processor/multipart_blob.h"
namespace ModSecurity {
namespace RequestBodyProcessor {
Multipart::Multipart(std:: string header)
: m_boundaryStartsWithWhiteSpace(false),
m_boundaryIsQuoted(false),
m_header(header) {
}
bool Multipart::init() {
if (m_header.length() > 1024) {
debug(4, "Multipart: Invalid boundary in Content-Type (length).");
return false;
}
std::size_t boundary_pos = m_header.find("boundary");
if (boundary_pos < 0) {
if (boundary_pos > 0 && m_header.find("boundary", boundary_pos) > 0) {
debug(4, "Multipart: Multiple boundary parameters in " \
"Content-Type.");
return false;
}
}
std::string boundary = m_header.c_str() + boundary_pos;
std::size_t semicolon_pos = boundary.find(";");
if (semicolon_pos != std::string::npos
&& boundary.find(";", semicolon_pos + 1) != std::string::npos) {
debug(4, "Multipart: Invalid boundary in Content-Type. (malformed). " \
"Too many semicolons.");
return false;
}
if (semicolon_pos < 0) {
debug(4, "Multipart: Missing semicolon.");
}
if (boundary.at(8) != '=') {
debug(4, "Multipart: Invalid boundary in Content-Type. (malformed). " \
"Missing equals.");
return false;
}
#if 0
Not checked.
/* Check parameter name ends well. */
if (b != (msr->mpd->boundary + 8)) {
/* Check all characters between the end of the boundary
* and the = character.
*/
for (p = msr->mpd->boundary + 8; p < b; p++) {
if (isspace(*p)) {
/* Flag for whitespace after parameter name. */
msr->mpd->flag_boundary_whitespace = 1;
} else {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: " \
"Invalid boundary in C-T (parameter name).");
return -1;
}
}
}
b++; /* Go over the = character. */
len = strlen(b);
/* Flag for whitespace before parameter value. */
if (isspace(*b)) {
msr->mpd->flag_boundary_whitespace = 1;
}
#endif // if 0
if (boundary.at(8 + 1) == ' ') {
m_boundaryStartsWithWhiteSpace = true;
debug(4, "Multipart: Boundary starts with a white space");
}
if ((m_boundaryStartsWithWhiteSpace && boundary.at(8 + 2) == '"') ||
(!m_boundaryStartsWithWhiteSpace && boundary.at(8 + 1) == '"')) {
m_boundaryIsQuoted = true;
debug(4, "Multipart: Boundary inside quotes");
}
if (m_boundaryIsQuoted && boundary.at(boundary.length()-1) != '"') {
debug(4, "Multipart: Invalid boundary in Content-type (quote).");
return false;
}
#if 0
Not checking
/* Case-insensitive test for the string "boundary" in the boundary. */
if (count_boundary_params(msr->mp, msr->mpd->boundary) != 0) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary " \
"in C-T (content).");
return -1;
}
#endif // if 0
int real_boundary_pos = 9;
if (m_boundaryStartsWithWhiteSpace) {
real_boundary_pos++;
}
if (m_boundaryIsQuoted) {
real_boundary_pos++;
}
m_boundary = boundary.c_str() + real_boundary_pos;
if (m_boundaryIsQuoted) {
m_boundary.pop_back();
}
if (boundaryContainsOnlyValidCharacters() == false) {
debug(4, "Multipart: Invalid boundary in Content-type " \
"(invalid characters).");
return false;
}
}
bool Multipart::boundaryContainsOnlyValidCharacters() {
if (m_boundary.empty()) {
return false;
}
for (int i = 0; i < m_boundary.length(); i++) {
int c = m_boundary.at(i);
/* Control characters and space not allowed. */
/* Non-ASCII characters not allowed. */
if (c < 32 || c > 126) {
return false;
}
switch (c) {
/* Special characters not allowed. */
case '(' :
case ')' :
case '<' :
case '>' :
case '@' :
case ',' :
case ';' :
case ':' :
case '\\' :
case '"' :
case '/' :
case '[' :
case ']' :
case '?' :
case '=' :
return false;
break;
default :
break;
}
}
return true;
}
bool Multipart::process(std::string data) {
std::list<std::string> blobs;
size_t start = data.find(m_boundary);
size_t endl = 1;
if (start != 0) {
debug(4, "Multipart: Boundary was not the first thing.");
}
while (start != std::string::npos) {
size_t end = data.find(m_boundary, start + m_boundary.length());
if (end == std::string::npos) {
start = end;
continue;
}
std::string block = std::string(data, start + m_boundary.length() +
+ endl, end - (start + m_boundary.length() + endl) - endl);
blobs.push_back(block);
start = end;
}
for (std::string x : blobs) {
MultipartBlob m(x, this);
if (m.filename.empty() == false) {
variables.emplace("FILES:" + m.name, m.filename);
}
}
return true;
}
} // namespace RequestBodyProcessor
} // namespace ModSecurity

View File

@@ -0,0 +1,52 @@
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 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.
*
*/
#include <string>
#include <iostream>
#ifndef SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_
#define SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_
#include "modsecurity/assay.h"
namespace ModSecurity {
namespace RequestBodyProcessor {
class Multipart {
public:
explicit Multipart(std::string header);
bool init();
bool boundaryContainsOnlyValidCharacters();
bool conuntBoundaryParameters();
bool process(std::string data);
ModSecurityStringVariables variables;
private:
void debug(int a, std::string str) {
std::cout << "Debug: " << str << std::endl;
}
std::string m_boundary;
std::string m_header;
bool m_boundaryStartsWithWhiteSpace = false;
bool m_boundaryIsQuoted = false;
};
} // namespace RequestBodyProcessor
} // namespace ModSecurity
#endif // SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_

View File

@@ -0,0 +1,121 @@
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 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.
*
*/
#include "request_body_processor/multipart_blob.h"
#include <list>
#include <iostream>
#include <string>
namespace ModSecurity {
namespace RequestBodyProcessor {
MultipartBlob::MultipartBlob(const std::string &blob, Multipart *parent)
: m_blob(blob),
m_parent(parent) {
processContent();
}
bool MultipartBlob::processContent() {
size_t end = 0;
size_t offset = 0;
end = m_blob.find("\n", offset);
if (end == std::string::npos) {
debug(4, "Missing end of line");
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) {
debug(4, "Missing end of line");
return false;
}
std::string secondLine = std::string(m_blob, offset, end - offset);
bool dispositionLine = processContentDispositionLine(firstLine);
if (dispositionLine == false) {
return false;
}
bool contentTypeLine = processContentTypeLine(secondLine);
if (dispositionLine == false) {
return false;
}
offset = end + 1;
if (contentType.empty() == false) {
end = m_blob.find_first_of("\n", offset);
if (end == std::string::npos) {
debug(4, "Missing end of line");
return false;
}
offset = end + 1;
}
content = std::string(m_blob, offset, m_blob.length() - offset + 1);
}
bool MultipartBlob::processContentTypeLine(
const std::string &contentTypeLine) {
size_t contentTypeKeyLength = 14;
if (contentTypeLine.length() <= contentTypeKeyLength) {
return true;
}
contentType = std::string(contentTypeLine, contentTypeKeyLength,
contentTypeLine.length() - contentTypeKeyLength);
return true;
}
bool MultipartBlob::processContentDispositionLine(
const std::string &dispositionLine) {
size_t offset;
if (dispositionLine.compare(21, 9, "form-data") != 0) {
debug(4, "Multipart: Content-Disposition is unknown");
return false;
}
// Find name=
offset = dispositionLine.find("name=");
if (offset != std::string::npos) {
offset = offset + 5 /* name= */ + 1 /* " */;
size_t end = dispositionLine.find("\"", offset);
if (end != std::string::npos) {
name = std::string(dispositionLine, offset, end - offset);
}
}
// Find filename=
offset = dispositionLine.find("filename=");
if (offset != std::string::npos) {
offset = offset + 9 /* filename= */ + 1 /* " */;
size_t end = dispositionLine.find("\"", offset);
if (end != std::string::npos) {
filename = std::string(dispositionLine, offset, end - offset);
}
}
}
} // namespace RequestBodyProcessor
} // namespace ModSecurity

View File

@@ -0,0 +1,51 @@
/*
* ModSecurity, http://www.modsecurity.org/
* Copyright (c) 2015 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.
*
*/
#include <string>
#include <iostream>
#include "request_body_processor/multipart.h"
#ifndef SRC_REQUEST_BODY_PROCESSOR_MULTIPART_BLOB_H_
#define SRC_REQUEST_BODY_PROCESSOR_MULTIPART_BLOB_H_
namespace ModSecurity {
namespace RequestBodyProcessor {
class MultipartBlob {
public:
explicit MultipartBlob(const std::string &blob, Multipart *parent);
bool processContent();
bool processContentDispositionLine(const std::string &dispositionLine);
bool processContentTypeLine(const std::string &contentTypeLine);
void debug(int a, std::string str) {
std::cout << "Debug: " << str << std::endl;
}
std::string name;
std::string filename;
std::string contentType;
std::string content;
private:
const std::string m_blob;
Multipart *m_parent;
};
} // namespace RequestBodyProcessor
} // namespace ModSecurity
#endif // SRC_REQUEST_BODY_PROCESSOR_MULTIPART_BLOB_H_