Skip to content

Commit 6a40752

Browse files
committed
Adds XML variable, xml body request processor and @validateSchema
1 parent 3563667 commit 6a40752

File tree

19 files changed

+1296
-33
lines changed

19 files changed

+1296
-33
lines changed

headers/modsecurity/transaction.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ class RuleMessage;
7474
namespace actions {
7575
class Action;
7676
}
77+
namespace RequestBodyProcessor {
78+
class XML;
79+
}
7780
namespace operators {
7881
class Operator;
7982
}
@@ -324,6 +327,8 @@ class Transaction {
324327
*/
325328
std::list<std::string> m_matched;
326329

330+
RequestBodyProcessor::XML *m_xml;
331+
327332
private:
328333
std::string *m_ARGScombinedSizeStr;
329334
std::string *m_namesArgs;

src/Makefile.am

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ VARIABLES = \
5656
variables/tx.cc \
5757
variables/variable.cc \
5858
variables/variations/count.cc \
59-
variables/variations/exclusion.cc
59+
variables/variations/exclusion.cc \
60+
variables/xml.cc
6061

6162

6263
ACTIONS = \
@@ -179,6 +180,12 @@ COLLECTION = \
179180
collection/backend/in_memory-per_process.cc
180181

181182

183+
BODY_PROCESSORS = \
184+
request_body_processor/multipart.cc \
185+
request_body_processor/multipart_blob.cc \
186+
request_body_processor/xml.cc
187+
188+
182189
libmodsecurity_la_SOURCES = \
183190
parser/seclang-parser.yy \
184191
parser/seclang-scanner.ll \
@@ -196,10 +203,9 @@ libmodsecurity_la_SOURCES = \
196203
debug_log_writer.cc \
197204
debug_log_writer_agent.cc \
198205
macro_expansion.cc \
199-
request_body_processor/multipart.cc \
200-
request_body_processor/multipart_blob.cc \
201206
rule.cc \
202207
unique_id.cc \
208+
${BODY_PROCESSORS} \
203209
${ACTIONS} \
204210
${COLLECTION} \
205211
${OPERATORS} \
@@ -221,16 +227,18 @@ libmodsecurity_la_CPPFLAGS = \
221227
$(GLOBAL_CPPFLAGS) \
222228
$(MODSEC_NO_LOGS) \
223229
$(YAJL_CFLAGS) \
224-
$(PCRE_CFLAGS)
230+
$(PCRE_CFLAGS) \
231+
$(LIBXML2_CFLAGS)
225232

226233
libmodsecurity_la_LIBADD = \
227-
$(GLOBAL_LDADD) \
228-
$(CURL_LDADD) \
229-
$(GEOIP_LDFLAGS) $(GEOIP_LDADD) \
230-
@LEXLIB@ \
231-
$(PCRE_LDADD) \
232-
$(YAJL_LDADD) \
233-
../others/libinjection.la
234+
$(GLOBAL_LDADD) \
235+
$(CURL_LDADD) \
236+
$(GEOIP_LDFLAGS) $(GEOIP_LDADD) \
237+
@LEXLIB@ \
238+
$(PCRE_LDADD) \
239+
$(YAJL_LDADD) \
240+
$(LIBXML2_LDADD) \
241+
../others/libinjection.la
234242

235243

236244
libmodsecurity_la_LDFLAGS = \

src/operators/validate_schema.cc

Lines changed: 101 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,116 @@
1818
#include <string>
1919

2020
#include "operators/operator.h"
21+
#include "request_body_processor/xml.h"
22+
#include "src/utils.h"
23+
2124

2225
namespace modsecurity {
2326
namespace operators {
2427

25-
bool ValidateSchema::evaluate(Transaction *transaction,
26-
const std::string &str) {
27-
/**
28-
* @todo Implement the operator ValidateSchema.
29-
* Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#validateSchema
30-
*/
28+
bool ValidateSchema::init(const std::string &file, const char **error) {
29+
m_resource = find_resource(param, file);
30+
if (m_resource == "") {
31+
std::string f("XML: File not found: " + param + ".");
32+
*error = strdup(f.c_str());
33+
return false;
34+
}
35+
36+
m_parserCtx = xmlSchemaNewParserCtxt(m_resource.c_str());
37+
if (m_parserCtx == NULL) {
38+
std::stringstream err;
39+
err << "XML: Failed to load Schema from file: ";
40+
err << m_resource;
41+
err << ". ";
42+
if (m_err.empty() == false) {
43+
err << m_err;
44+
}
45+
*error = strdup(err.str().c_str());
46+
return false;
47+
}
48+
49+
xmlSchemaSetParserErrors(m_parserCtx,
50+
(xmlSchemaValidityErrorFunc)error_load,
51+
(xmlSchemaValidityWarningFunc)warn_load, &m_err);
52+
53+
xmlThrDefSetGenericErrorFunc(m_parserCtx,
54+
null_error);
55+
56+
xmlSetGenericErrorFunc(m_parserCtx,
57+
null_error);
58+
59+
m_schema = xmlSchemaParse(m_parserCtx);
60+
if (m_schema == NULL) {
61+
std::stringstream err;
62+
err << "XML: Failed to load Schema: ";
63+
err << m_resource;
64+
err << ".";
65+
if (m_err.empty() == false) {
66+
err << " " << m_err;
67+
}
68+
*error = strdup(err.str().c_str());
69+
xmlSchemaFreeParserCtxt(m_parserCtx);
70+
return false;
71+
}
72+
73+
m_validCtx = xmlSchemaNewValidCtxt(m_schema);
74+
if (m_validCtx == NULL) {
75+
std::stringstream err("XML: Failed to create validation context.");
76+
if (m_err.empty() == false) {
77+
err << " " << m_err;
78+
}
79+
*error = strdup(err.str().c_str());
80+
return false;
81+
}
82+
3183
return true;
3284
}
3385

3486

35-
ValidateSchema::ValidateSchema(std::string op, std::string param,
36-
bool negation)
37-
: Operator() {
38-
this->op = op;
39-
this->param = param;
87+
bool ValidateSchema::evaluate(Transaction *t,
88+
const std::string &str) {
89+
int rc;
90+
91+
/* Send validator errors/warnings to msr_log */
92+
xmlSchemaSetValidErrors(m_validCtx,
93+
(xmlSchemaValidityErrorFunc)error_runtime,
94+
(xmlSchemaValidityWarningFunc)warn_runtime, t);
95+
96+
if (t->m_xml->m_data.doc == NULL) {
97+
t->debug(4, "XML document tree could not be found for " \
98+
"schema validation.");
99+
return true;
100+
}
101+
102+
if (t->m_xml->m_data.well_formed != 1) {
103+
t->debug(4, "XML: Schema validation failed because " \
104+
"content is not well formed.");
105+
return true;
106+
}
107+
108+
/* Make sure there were no other generic processing errors */
109+
/*
110+
if (msr->msc_reqbody_error) {
111+
t->debug(4, "XML: Schema validation could not proceed due to previous"
112+
" processing errors.");
113+
return true;
114+
}
115+
*/
116+
117+
rc = xmlSchemaValidateDoc(m_validCtx, t->m_xml->m_data.doc);
118+
if (rc != 0) {
119+
t->debug(4, "XML: Schema validation failed.");
120+
xmlSchemaFree(m_schema);
121+
xmlSchemaFreeParserCtxt(m_parserCtx);
122+
return true; /* No match. */
123+
}
124+
125+
t->debug(4, "XML: Successfully validated payload against " \
126+
"Schema: " + m_resource);
127+
128+
return false;
40129
}
41130

131+
42132
} // namespace operators
43133
} // namespace modsecurity

src/operators/validate_schema.h

Lines changed: 99 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
#ifndef SRC_OPERATORS_VALIDATE_SCHEMA_H_
1717
#define SRC_OPERATORS_VALIDATE_SCHEMA_H_
1818

19+
#include <stdio.h>
20+
#include <stdarg.h>
21+
#include <string.h>
22+
#include <libxml/xmlschemas.h>
23+
#include <libxml/xpath.h>
24+
1925
#include <string>
2026

2127
#include "operators/operator.h"
@@ -27,8 +33,100 @@ namespace operators {
2733
class ValidateSchema : public Operator {
2834
public:
2935
/** @ingroup ModSecurity_Operator */
30-
ValidateSchema(std::string o, std::string p, bool i);
36+
ValidateSchema(std::string o, std::string p, bool i)
37+
: Operator(o, p, i),
38+
m_schema(NULL),
39+
m_validCtx(NULL),
40+
m_parserCtx(NULL) { }
41+
~ValidateSchema() {
42+
/*
43+
if (m_schema != NULL) {
44+
xmlSchemaFree(m_schema);
45+
m_schema = NULL;
46+
}
47+
*/
48+
if (m_validCtx != NULL) {
49+
xmlSchemaFreeValidCtxt(m_validCtx);
50+
m_validCtx = NULL;
51+
}
52+
}
53+
3154
bool evaluate(Transaction *transaction, const std::string &str) override;
55+
bool init(const std::string &file, const char **error) override;
56+
57+
58+
static void error_load(void *ctx, const char *msg, ...) {
59+
std::string *t = reinterpret_cast<std::string *>(ctx);
60+
char buf[1024];
61+
va_list args;
62+
63+
va_start(args, msg);
64+
int len = vsnprintf(buf, sizeof(buf), msg, args);
65+
va_end(args);
66+
67+
if (len > 0) {
68+
t->append("XML Error: " + std::string(buf));
69+
}
70+
}
71+
72+
73+
static void warn_load(void *ctx, const char *msg, ...) {
74+
std::string *t = reinterpret_cast<std::string *>(ctx);
75+
char buf[1024];
76+
va_list args;
77+
78+
va_start(args, msg);
79+
int len = vsnprintf(buf, sizeof(buf), msg, args);
80+
va_end(args);
81+
82+
if (len > 0) {
83+
t->append("XML Warning: " + std::string(buf));
84+
}
85+
}
86+
87+
88+
static void error_runtime(void *ctx, const char *msg, ...) {
89+
Transaction *t = reinterpret_cast<Transaction *>(ctx);
90+
char buf[1024];
91+
std::string s;
92+
va_list args;
93+
94+
va_start(args, msg);
95+
int len = vsnprintf(buf, sizeof(buf), msg, args);
96+
va_end(args);
97+
98+
if (len > 0) {
99+
s = "XML Error: " + std::string(buf);
100+
}
101+
t->debug(4, s);
102+
}
103+
104+
105+
static void warn_runtime(void *ctx, const char *msg, ...) {
106+
Transaction *t = reinterpret_cast<Transaction *>(ctx);
107+
char buf[1024];
108+
std::string s;
109+
va_list args;
110+
111+
va_start(args, msg);
112+
int len = vsnprintf(buf, sizeof(buf), msg, args);
113+
va_end(args);
114+
115+
if (len > 0) {
116+
s = "XML Warning: " + std::string(buf);
117+
}
118+
t->debug(4, s);
119+
}
120+
121+
static void null_error(void *ctx, const char *msg, ...) {
122+
}
123+
124+
private:
125+
xmlSchemaParserCtxtPtr m_parserCtx;
126+
xmlSchemaValidCtxtPtr m_validCtx;
127+
xmlSchemaPtr m_schema;
128+
std::string m_resource;
129+
std::string m_err;
32130
};
33131

34132
} // namespace operators

src/parser/seclang-parser.yy

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class Driver;
6161
#include "variables/time_wday.h"
6262
#include "variables/time_year.h"
6363
#include "variables/tx.h"
64+
#include "variables/xml.h"
6465

6566
using modsecurity::ModSecurity;
6667

@@ -103,6 +104,7 @@ using modsecurity::Variables::TimeWDay;
103104
using modsecurity::Variables::TimeYear;
104105
using modsecurity::Variables::Variable;
105106
using modsecurity::Variables::Tx;
107+
using modsecurity::Variables::XML;
106108

107109

108110
#define CHECK_VARIATION_DECL \
@@ -229,6 +231,7 @@ using modsecurity::Variables::Tx;
229231
%token <std::string> RUN_TIME_VAR_TIME_SEC
230232
%token <std::string> RUN_TIME_VAR_TIME_WDAY
231233
%token <std::string> RUN_TIME_VAR_TIME_YEAR
234+
%token <std::string> RUN_TIME_VAR_XML
232235

233236
%token <std::string> CONFIG_SEC_REMOTE_RULES_FAIL_ACTION
234237

@@ -816,6 +819,15 @@ var:
816819
if (!var) { var = new TimeYear(name); }
817820
$$ = var;
818821
}
822+
| RUN_TIME_VAR_XML
823+
{
824+
std::string name($1);
825+
CHECK_VARIATION_DECL
826+
CHECK_VARIATION(&) { var = new Count(new XML(name)); }
827+
CHECK_VARIATION(!) { var = new Exclusion(new XML(name)); }
828+
if (!var) { var = new XML(name); }
829+
$$ = var;
830+
}
819831
;
820832

821833
act:

src/parser/seclang-scanner.ll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ TRANSFORMATION t:(?i:(cmdLine|sha1|hexEncode|lowercase|urlDecodeUni|urlDecode|n
117117

118118

119119
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))
120-
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))
120+
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))
121121

122122
VARIABLE_TX (?i:TX)
123123
VARIABLE_WEBSERVER_ERROR_LOG (?:WEBSERVER_ERROR_LOG)
@@ -136,6 +136,7 @@ RUN_TIME_VAR_TIME_MON (?i:TIME_MON)
136136
RUN_TIME_VAR_TIME_SEC (?i:TIME_SEC)
137137
RUN_TIME_VAR_TIME_WDAY (?i:TIME_WDAY)
138138
RUN_TIME_VAR_TIME_YEAR (?i:TIME_YEAR)
139+
RUN_TIME_VAR_XML (?i:XML)
139140

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

@@ -227,6 +228,8 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile)
227228
[!&]?{VARIABLE}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE(yytext, *driver.loc.back()); }
228229
[!&]?{VARIABLE_COL}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
229230
[!&]?{VARIABLE_COL}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
231+
[!&]?{RUN_TIME_VAR_XML}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
232+
[!&]?{RUN_TIME_VAR_XML}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
230233
[!&]?{VARIABLE_TX}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); }
231234
[!&]?{VARIABLE_TX}(\:[\']{FREE_TEXT_QUOTE}[\'])? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); }
232235
[!&]?{RUN_TIME_VAR_DUR} { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, *driver.loc.back()); }
@@ -243,6 +246,8 @@ CONFIG_DIR_UNICODE_MAP_FILE (?i:SecUnicodeMapFile)
243246
["][!&]?{VARIABLE_TX}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_TX(yytext, *driver.loc.back()); }
244247
["][!&]?{VARIABLE_COL}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
245248
["][!&]?{VARIABLE_COL}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_VARIABLE_COL(yytext, *driver.loc.back()); }
249+
["][!&]?{RUN_TIME_VAR_XML}(\:{DICT_ELEMENT})? { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
250+
["][!&]?{RUN_TIME_VAR_XML}(\:[\']{FREE_TEXT_QUOTE}[\'])?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_XML(yytext, *driver.loc.back()); }
246251
247252
["][!&]?{RUN_TIME_VAR_DUR}["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_DUR(yytext, *driver.loc.back()); }
248253
["][!&]?{RUN_TIME_VAR_ENV}(\:{DICT_ELEMENT})?["] { BEGIN(EXPECTING_OPERATOR); return yy::seclang_parser::make_RUN_TIME_VAR_ENV(yytext, *driver.loc.back()); }

0 commit comments

Comments
 (0)