Skip to content

Commit

Permalink
Adds XML variable, xml body request processor and @validateSchema
Browse files Browse the repository at this point in the history
  • Loading branch information
zimmerle committed May 12, 2016
1 parent 3563667 commit 6a40752
Show file tree
Hide file tree
Showing 19 changed files with 1,296 additions and 33 deletions.
5 changes: 5 additions & 0 deletions headers/modsecurity/transaction.h
Expand Up @@ -74,6 +74,9 @@ class RuleMessage;
namespace actions {
class Action;
}
namespace RequestBodyProcessor {
class XML;
}
namespace operators {
class Operator;
}
Expand Down Expand Up @@ -324,6 +327,8 @@ class Transaction {
*/
std::list<std::string> m_matched;

RequestBodyProcessor::XML *m_xml;

private:
std::string *m_ARGScombinedSizeStr;
std::string *m_namesArgs;
Expand Down
30 changes: 19 additions & 11 deletions src/Makefile.am
Expand Up @@ -56,7 +56,8 @@ VARIABLES = \
variables/tx.cc \
variables/variable.cc \
variables/variations/count.cc \
variables/variations/exclusion.cc
variables/variations/exclusion.cc \
variables/xml.cc


ACTIONS = \
Expand Down Expand Up @@ -179,6 +180,12 @@ COLLECTION = \
collection/backend/in_memory-per_process.cc


BODY_PROCESSORS = \
request_body_processor/multipart.cc \
request_body_processor/multipart_blob.cc \
request_body_processor/xml.cc


libmodsecurity_la_SOURCES = \
parser/seclang-parser.yy \
parser/seclang-scanner.ll \
Expand All @@ -196,10 +203,9 @@ libmodsecurity_la_SOURCES = \
debug_log_writer.cc \
debug_log_writer_agent.cc \
macro_expansion.cc \
request_body_processor/multipart.cc \
request_body_processor/multipart_blob.cc \
rule.cc \
unique_id.cc \
${BODY_PROCESSORS} \
${ACTIONS} \
${COLLECTION} \
${OPERATORS} \
Expand All @@ -221,16 +227,18 @@ libmodsecurity_la_CPPFLAGS = \
$(GLOBAL_CPPFLAGS) \
$(MODSEC_NO_LOGS) \
$(YAJL_CFLAGS) \
$(PCRE_CFLAGS)
$(PCRE_CFLAGS) \
$(LIBXML2_CFLAGS)

libmodsecurity_la_LIBADD = \
$(GLOBAL_LDADD) \
$(CURL_LDADD) \
$(GEOIP_LDFLAGS) $(GEOIP_LDADD) \
@LEXLIB@ \
$(PCRE_LDADD) \
$(YAJL_LDADD) \
../others/libinjection.la
$(GLOBAL_LDADD) \
$(CURL_LDADD) \
$(GEOIP_LDFLAGS) $(GEOIP_LDADD) \
@LEXLIB@ \
$(PCRE_LDADD) \
$(YAJL_LDADD) \
$(LIBXML2_LDADD) \
../others/libinjection.la


libmodsecurity_la_LDFLAGS = \
Expand Down
112 changes: 101 additions & 11 deletions src/operators/validate_schema.cc
Expand Up @@ -18,26 +18,116 @@
#include <string>

#include "operators/operator.h"
#include "request_body_processor/xml.h"
#include "src/utils.h"


namespace modsecurity {
namespace operators {

bool ValidateSchema::evaluate(Transaction *transaction,
const std::string &str) {
/**
* @todo Implement the operator ValidateSchema.
* Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#validateSchema
*/
bool ValidateSchema::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;
}

m_parserCtx = xmlSchemaNewParserCtxt(m_resource.c_str());
if (m_parserCtx == NULL) {
std::stringstream err;
err << "XML: Failed to load Schema from file: ";
err << m_resource;
err << ". ";
if (m_err.empty() == false) {
err << m_err;
}
*error = strdup(err.str().c_str());
return false;
}

xmlSchemaSetParserErrors(m_parserCtx,
(xmlSchemaValidityErrorFunc)error_load,
(xmlSchemaValidityWarningFunc)warn_load, &m_err);

xmlThrDefSetGenericErrorFunc(m_parserCtx,
null_error);

xmlSetGenericErrorFunc(m_parserCtx,
null_error);

m_schema = xmlSchemaParse(m_parserCtx);
if (m_schema == NULL) {
std::stringstream err;
err << "XML: Failed to load Schema: ";
err << m_resource;
err << ".";
if (m_err.empty() == false) {
err << " " << m_err;
}
*error = strdup(err.str().c_str());
xmlSchemaFreeParserCtxt(m_parserCtx);
return false;
}

m_validCtx = xmlSchemaNewValidCtxt(m_schema);
if (m_validCtx == NULL) {
std::stringstream err("XML: Failed to create validation context.");
if (m_err.empty() == false) {
err << " " << m_err;
}
*error = strdup(err.str().c_str());
return false;
}

return true;
}


ValidateSchema::ValidateSchema(std::string op, std::string param,
bool negation)
: Operator() {
this->op = op;
this->param = param;
bool ValidateSchema::evaluate(Transaction *t,
const std::string &str) {
int rc;

/* Send validator errors/warnings to msr_log */
xmlSchemaSetValidErrors(m_validCtx,
(xmlSchemaValidityErrorFunc)error_runtime,
(xmlSchemaValidityWarningFunc)warn_runtime, t);

if (t->m_xml->m_data.doc == NULL) {
t->debug(4, "XML document tree could not be found for " \
"schema validation.");
return true;
}

if (t->m_xml->m_data.well_formed != 1) {
t->debug(4, "XML: Schema validation failed because " \
"content is not well formed.");
return true;
}

/* Make sure there were no other generic processing errors */
/*
if (msr->msc_reqbody_error) {
t->debug(4, "XML: Schema validation could not proceed due to previous"
" processing errors.");
return true;
}
*/

rc = xmlSchemaValidateDoc(m_validCtx, t->m_xml->m_data.doc);
if (rc != 0) {
t->debug(4, "XML: Schema validation failed.");
xmlSchemaFree(m_schema);
xmlSchemaFreeParserCtxt(m_parserCtx);
return true; /* No match. */
}

t->debug(4, "XML: Successfully validated payload against " \
"Schema: " + m_resource);

return false;
}


} // namespace operators
} // namespace modsecurity
100 changes: 99 additions & 1 deletion src/operators/validate_schema.h
Expand Up @@ -16,6 +16,12 @@
#ifndef SRC_OPERATORS_VALIDATE_SCHEMA_H_
#define SRC_OPERATORS_VALIDATE_SCHEMA_H_

#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <libxml/xmlschemas.h>
#include <libxml/xpath.h>

#include <string>

#include "operators/operator.h"
Expand All @@ -27,8 +33,100 @@ namespace operators {
class ValidateSchema : public Operator {
public:
/** @ingroup ModSecurity_Operator */
ValidateSchema(std::string o, std::string p, bool i);
ValidateSchema(std::string o, std::string p, bool i)
: Operator(o, p, i),
m_schema(NULL),
m_validCtx(NULL),
m_parserCtx(NULL) { }
~ValidateSchema() {
/*
if (m_schema != NULL) {
xmlSchemaFree(m_schema);
m_schema = NULL;
}
*/
if (m_validCtx != NULL) {
xmlSchemaFreeValidCtxt(m_validCtx);
m_validCtx = NULL;
}
}

bool evaluate(Transaction *transaction, const std::string &str) override;
bool init(const std::string &file, const char **error) override;


static void error_load(void *ctx, const char *msg, ...) {
std::string *t = reinterpret_cast<std::string *>(ctx);
char buf[1024];
va_list args;

va_start(args, msg);
int len = vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);

if (len > 0) {
t->append("XML Error: " + std::string(buf));
}
}


static void warn_load(void *ctx, const char *msg, ...) {
std::string *t = reinterpret_cast<std::string *>(ctx);
char buf[1024];
va_list args;

va_start(args, msg);
int len = vsnprintf(buf, sizeof(buf), msg, args);
va_end(args);

if (len > 0) {
t->append("XML Warning: " + std::string(buf));
}
}


static void error_runtime(void *ctx, const char *msg, ...) {
Transaction *t = reinterpret_cast<Transaction *>(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<Transaction *>(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:
xmlSchemaParserCtxtPtr m_parserCtx;
xmlSchemaValidCtxtPtr m_validCtx;
xmlSchemaPtr m_schema;
std::string m_resource;
std::string m_err;
};

} // namespace operators
Expand Down
12 changes: 12 additions & 0 deletions src/parser/seclang-parser.yy
Expand Up @@ -61,6 +61,7 @@ class Driver;
#include "variables/time_wday.h"
#include "variables/time_year.h"
#include "variables/tx.h"
#include "variables/xml.h"

using modsecurity::ModSecurity;

Expand Down Expand Up @@ -103,6 +104,7 @@ using modsecurity::Variables::TimeWDay;
using modsecurity::Variables::TimeYear;
using modsecurity::Variables::Variable;
using modsecurity::Variables::Tx;
using modsecurity::Variables::XML;


#define CHECK_VARIATION_DECL \
Expand Down Expand Up @@ -229,6 +231,7 @@ using modsecurity::Variables::Tx;
%token <std::string> RUN_TIME_VAR_TIME_SEC
%token <std::string> RUN_TIME_VAR_TIME_WDAY
%token <std::string> RUN_TIME_VAR_TIME_YEAR
%token <std::string> RUN_TIME_VAR_XML

%token <std::string> CONFIG_SEC_REMOTE_RULES_FAIL_ACTION

Expand Down Expand Up @@ -816,6 +819,15 @@ var:
if (!var) { var = new TimeYear(name); }
$$ = var;
}
| RUN_TIME_VAR_XML
{
std::string name($1);
CHECK_VARIATION_DECL
CHECK_VARIATION(&) { var = new Count(new XML(name)); }
CHECK_VARIATION(!) { var = new Exclusion(new XML(name)); }
if (!var) { var = new XML(name); }
$$ = var;
}
;

act:
Expand Down
7 changes: 6 additions & 1 deletion src/parser/seclang-scanner.ll
Expand Up @@ -117,7 +117,7 @@ TRANSFORMATION t:(?i:(cmdLine|sha1|hexEncode|lowercase|urlDecodeUni|urlDecode|n


VARIABLE (?i:(RESOURCE|ARGS_COMBINED_SIZE|ARGS_GET_NAMES|ARGS_POST_NAMES|FILES_COMBINED_SIZE|FULL_REQUEST_LENGTH|REQUEST_BODY_LENGTH|REQUEST_URI_RAW|UNIQUE_ID|SERVER_PORT|SERVER_ADDR|REMOTE_PORT|REMOTE_HOST|MULTIPART_STRICT_ERROR|PATH_INFO|MULTIPART_CRLF_LF_LINES|MATCHED_VAR_NAME|MATCHED_VAR|INBOUND_DATA_ERROR|OUTBOUND_DATA_ERROR|FULL_REQUEST|AUTH_TYPE|ARGS_NAMES|REMOTE_ADDR|REQUEST_BASENAME|REQUEST_BODY|REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_METHOD|REQUEST_PROTOCOL|REQUEST_URI|RESPONSE_BODY|RESPONSE_CONTENT_LENGTH|RESPONSE_CONTENT_TYPE|RESPONSE_HEADERS_NAMES|RESPONSE_PROTOCOL|RESPONSE_STATUS|REQBODY_PROCESSOR|USERID|SESSIONID))
VARIABLE_COL (?i:(SESSION|GLOBAL|ARGS_POST|ARGS_GET|ARGS|FILES_SIZES|FILES_NAMES|FILES_TMP_CONTENT|MULTIPART_FILENAME|MULTIPART_NAME|MATCHED_VARS_NAMES|MATCHED_VARS|FILES|QUERY_STRING|REQUEST_COOKIES|REQUEST_HEADERS|RESPONSE_HEADERS|GEO|IP|XML|REQUEST_COOKIES_NAMES))
VARIABLE_COL (?i:(SESSION|GLOBAL|ARGS_POST|ARGS_GET|ARGS|FILES_SIZES|FILES_NAMES|FILES_TMP_CONTENT|MULTIPART_FILENAME|MULTIPART_NAME|MATCHED_VARS_NAMES|MATCHED_VARS|FILES|QUERY_STRING|REQUEST_COOKIES|REQUEST_HEADERS|RESPONSE_HEADERS|GEO|IP|REQUEST_COOKIES_NAMES))

VARIABLE_TX (?i:TX)
VARIABLE_WEBSERVER_ERROR_LOG (?:WEBSERVER_ERROR_LOG)
Expand All @@ -136,6 +136,7 @@ RUN_TIME_VAR_TIME_MON (?i:TIME_MON)
RUN_TIME_VAR_TIME_SEC (?i:TIME_SEC)
RUN_TIME_VAR_TIME_WDAY (?i:TIME_WDAY)
RUN_TIME_VAR_TIME_YEAR (?i:TIME_YEAR)
RUN_TIME_VAR_XML (?i:XML)

VARIABLENOCOLON (?i:REQBODY_ERROR|MULTIPART_STRICT_ERROR|MULTIPART_UNMATCHED_BOUNDARY|REMOTE_ADDR|REQUEST_LINE)

Expand Down Expand Up @@ -227,6 +228,8 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile)
[!&]?{VARIABLE}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE(yytext, *driver.loc.back()); }
[!&]?{VARIABLE_COL}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
[!&]?{VARIABLE_COL}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
[!&]?{RUN_TIME_VAR_XML}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
[!&]?{RUN_TIME_VAR_XML}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
[!&]?{VARIABLE_TX}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); }
[!&]?{VARIABLE_TX}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); }
[!&]?{RUN_TIME_VAR_DUR} { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, *driver.loc.back()); }
Expand All @@ -243,6 +246,8 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile)
["][!&]?{VARIABLE_TX}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); }
["][!&]?{VARIABLE_COL}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
["][!&]?{VARIABLE_COL}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
["][!&]?{RUN_TIME_VAR_XML}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
["][!&]?{RUN_TIME_VAR_XML}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
["][!&]?{RUN_TIME_VAR_DUR}["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, *driver.loc.back()); }
["][!&]?{RUN_TIME_VAR_ENV}(\:{DICT_ELEMENT})?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_ENV(yytext, *driver.loc.back()); }
Expand Down

0 comments on commit 6a40752

Please sign in to comment.