From 3e8defb853313ee6401b5a388367be5dba140bd2 Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Thu, 12 May 2016 11:09:59 -0300 Subject: [PATCH] Adds support to the operator @validateDTD Further info #1003 --- src/operators/validate_dtd.cc | 83 +++++++- src/operators/validate_dtd.h | 60 +++++- .../request-body-parser-xml-validade-dtd.json | 181 ++++++++++++++++++ 3 files changed, 314 insertions(+), 10 deletions(-) create mode 100644 test/test-cases/regression/request-body-parser-xml-validade-dtd.json diff --git a/src/operators/validate_dtd.cc b/src/operators/validate_dtd.cc index 7dc040c6..5b2f892d 100644 --- a/src/operators/validate_dtd.cc +++ b/src/operators/validate_dtd.cc @@ -17,25 +17,90 @@ #include +#include "request_body_processor/xml.h" +#include "src/utils.h" #include "operators/operator.h" namespace modsecurity { namespace operators { -bool ValidateDTD::evaluate(Transaction *transaction, const std::string &str) { - /** - * @todo Implement the operator ValidateDTD. - * Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#validateDTD - */ + +bool ValidateDTD::init(const std::string &file, const char **error) { + m_resource = find_resource(param, file); + if (m_resource == "") { + std::string f("XML: File not found: " + param + "."); + *error = strdup(f.c_str()); + return false; + } + + xmlThrDefSetGenericErrorFunc(NULL, + null_error); + + xmlSetGenericErrorFunc(NULL, + null_error); + + m_dtd = xmlParseDTD(NULL, (const xmlChar *)m_resource.c_str()); + if (m_dtd == NULL) { + std::string err = std::string("XML: Failed to load DTD: ") \ + + m_resource; + *error = strdup(err.c_str()); + return false; + } + return true; } -ValidateDTD::ValidateDTD(std::string op, std::string param, bool negation) - : Operator() { - this->op = op; - this->param = param; +bool ValidateDTD::evaluate(Transaction *t, const std::string &str) { + xmlValidCtxtPtr cvp; + + if (t->m_xml->m_data.doc == NULL) { + t->debug(4, "XML document tree could not "\ + "be found for DTD validation."); + return true; + } + + if (t->m_xml->m_data.well_formed != 1) { + t->debug(4, "XML: DTD validation failed because " \ + "content is not well formed."); + return true; + } + +#if 0 + /* Make sure there were no other generic processing errors */ + if (msr->msc_reqbody_error) { + *error_msg = apr_psprintf(msr->mp, + "XML: DTD validation could not proceed due to previous" + " processing errors."); + return 1; + } +#endif + + cvp = xmlNewValidCtxt(); + if (cvp == NULL) { + t->debug(4, "XML: Failed to create a validation context."); + return true; + } + + /* Send validator errors/warnings to msr_log */ + cvp->error = (xmlSchemaValidityErrorFunc)error_runtime; + cvp->warning = (xmlSchemaValidityErrorFunc)warn_runtime; + cvp->userData = t; + + if (!xmlValidateDtd(cvp, t->m_xml->m_data.doc, m_dtd)) { + t->debug(4, "XML: DTD validation failed."); + xmlFreeValidCtxt(cvp); + return true; + } + + t->debug(4, std::string("XML: Successfully validated " \ + "payload against DTD: ") + m_resource); + + xmlFreeValidCtxt(cvp); + + return false; } + } // namespace operators } // namespace modsecurity diff --git a/src/operators/validate_dtd.h b/src/operators/validate_dtd.h index c5339d56..3a47a71f 100644 --- a/src/operators/validate_dtd.h +++ b/src/operators/validate_dtd.h @@ -16,6 +16,12 @@ #ifndef SRC_OPERATORS_VALIDATE_DTD_H_ #define SRC_OPERATORS_VALIDATE_DTD_H_ +#include +#include +#include +#include +#include + #include #include "operators/operator.h" @@ -27,8 +33,60 @@ namespace operators { class ValidateDTD : public Operator { public: /** @ingroup ModSecurity_Operator */ - ValidateDTD(std::string o, std::string p, bool i); + ValidateDTD(std::string o, std::string p, bool i) + : Operator(o, p, i), + m_dtd(NULL) { } + ~ValidateDTD() { + if (m_dtd != NULL) { + xmlFreeDtd(m_dtd); + m_dtd = NULL; + } + } + bool evaluate(Transaction *transaction, const std::string &str) override; + bool init(const std::string &file, const char **error) override; + + + static void error_runtime(void *ctx, const char *msg, ...) { + Transaction *t = reinterpret_cast(ctx); + char buf[1024]; + std::string s; + va_list args; + + va_start(args, msg); + int len = vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + + if (len > 0) { + s = "XML Error: " + std::string(buf); + } + t->debug(4, s); + } + + + static void warn_runtime(void *ctx, const char *msg, ...) { + Transaction *t = reinterpret_cast(ctx); + char buf[1024]; + std::string s; + va_list args; + + va_start(args, msg); + int len = vsnprintf(buf, sizeof(buf), msg, args); + va_end(args); + + if (len > 0) { + s = "XML Warning: " + std::string(buf); + } + t->debug(4, s); + } + + + static void null_error(void *ctx, const char *msg, ...) { + } + + private: + std::string m_resource; + xmlDtdPtr m_dtd; }; } // namespace operators diff --git a/test/test-cases/regression/request-body-parser-xml-validade-dtd.json b/test/test-cases/regression/request-body-parser-xml-validade-dtd.json new file mode 100644 index 00000000..028987d4 --- /dev/null +++ b/test/test-cases/regression/request-body-parser-xml-validade-dtd.json @@ -0,0 +1,181 @@ +[ + { + "enabled":1, + "version_min":300000, + "title":"Testing XML request body parser - validateDTD (validate ok)", + "expected":{ + "debug_log": "XML: Successfully validated payload against DTD: test-cases/data/SoapEnvelope.dtd" + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/?key=value&key=other_value", + "method":"POST", + "body": [ + "", + " ", + " ", + " ", + " ", + " 12123", + " ", + " ", + " " + ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500008,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"", + "SecRule XML \"@validateDTD test-cases/data/SoapEnvelope.dtd\" \"id:500007,phase:3,deny\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing XML request body parser - validateDTD (validation failed)", + "expected":{ + "debug_log": "XML Error: No declaration for element xBody", + "http_code": 403 + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/?key=value&key=other_value", + "method":"POST", + "body": [ + "", + " ", + " ", + " ", + " ", + " 12123", + " ", + " ", + " " + ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500008,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"", + "SecRule XML \"@validateDTD test-cases/data/SoapEnvelope.dtd\" \"id:500007,phase:3,deny\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing XML request body parser - validateDTD (bad XML)", + "expected":{ + "debug_log": "XML: DTD validation failed because content is not well formed", + "http_code": 403 + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/?key=value&key=other_value", + "method":"POST", + "body": [ + "", + "", + " ", + " ", + " ", + " 12123", + " ", + " ", + " " + ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500008,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"", + "SecRule XML \"@validateDTD test-cases/data/SoapEnvelope.dtd\" \"id:500007,phase:3,deny\"" + ] + }, + { + "enabled":1, + "version_min":300000, + "title":"Testing XML request body parser - validateDTD (bad DTD)", + "expected":{ + "parser_error": "Line: 4. Column: 12. XML: Failed to load DTD: test-cases/data/SoapEnvelope-bad.dtd" + }, + "client":{ + "ip":"200.249.12.31", + "port":123 + }, + "request":{ + "headers":{ + "Host":"localhost", + "User-Agent":"curl/7.38.0", + "Accept":"*/*", + "Cookie": "PHPSESSID=rAAAAAAA2t5uvjq435r4q7ib3vtdjq120", + "Content-Type": "text/xml" + }, + "uri":"/?key=value&key=other_value", + "method":"POST", + "body": [ + "", + " ", + " ", + " ", + " ", + " 12123", + " ", + " ", + " " + ] + }, + "server":{ + "ip":"200.249.12.31", + "port":80 + }, + "rules":[ + "SecRuleEngine On", + "SecRequestBodyAccess On", + "SecRule REQUEST_HEADERS:Content-Type \"^text/xml$\" \"id:500008,phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML\"", + "SecRule XML \"@validateDTD test-cases/data/SoapEnvelope-bad.dtd\" \"id:500007,phase:3,deny\"" + ] + } +] +