Skip to content

Commit 74878c9

Browse files
committed
Reduce direct XML state coupling and formalize XML error model
1 parent 7778afd commit 74878c9

File tree

10 files changed

+425
-278
lines changed

10 files changed

+425
-278
lines changed

build/libxml.m4

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,69 @@ dnl LIBXML2_DISPLAY
88
dnl LIBXML2_FOUND
99

1010
AC_DEFUN([CHECK_LIBXML2], [
11-
MSC_CHECK_LIB([LIBXML2], [libxml-2.0], [libxml/parser.h], [xml2], [-DWITH_LIBXML2], [2.6.29], [libxml])
11+
AC_ARG_WITH([libxml2-source],
12+
[AS_HELP_STRING([--with-libxml2-source=SOURCE],
13+
[Select libxml2 source: vendor (others/libxml2) or system [default=vendor]])],
14+
[msc_libxml2_source="$withval"],
15+
[msc_libxml2_source="vendor"])
16+
17+
AS_CASE([$msc_libxml2_source],
18+
[vendor|system], [],
19+
[AC_MSG_ERROR([Unsupported --with-libxml2-source value '$msc_libxml2_source'. Use vendor or system.])])
20+
21+
if test "x$with_libxml" = "xno"; then
22+
AC_MSG_NOTICE([LIBXML2 support disabled via --without-libxml])
23+
LIBXML2_FOUND=2
24+
LIBXML2_CFLAGS=""
25+
LIBXML2_LDADD=""
26+
LIBXML2_LDFLAGS=""
27+
LIBXML2_VERSION=""
28+
LIBXML2_DISPLAY=""
29+
elif test "x$msc_libxml2_source" = "xvendor"; then
30+
LIBXML2_VENDOR_DIR="${PWD}/others/libxml2"
31+
LIBXML2_VENDOR_BUILD_DIR="${PWD}/others/libxml2-vendor-build"
32+
33+
if ! test -f "${LIBXML2_VENDOR_DIR}/CMakeLists.txt"; then
34+
AC_MSG_ERROR([\
35+
36+
37+
Vendored libxml2 was not found at ${LIBXML2_VENDOR_DIR}.
38+
Initialize submodules first:
39+
40+
$ git submodule update --init --recursive
41+
42+
])
43+
fi
44+
45+
AC_MSG_NOTICE([Configuring vendored libxml2 from ${LIBXML2_VENDOR_DIR}])
46+
AS_MKDIR_P(["${LIBXML2_VENDOR_BUILD_DIR}"])
47+
48+
LIBXML2_VENDOR_CONFIGURE_CMD="cmake -S \"${LIBXML2_VENDOR_DIR}\" -B \"${LIBXML2_VENDOR_BUILD_DIR}\" -DBUILD_SHARED_LIBS=OFF -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_PROGRAMS=OFF -DLIBXML2_WITH_TESTS=OFF -DLIBXML2_WITH_ZLIB=OFF -DLIBXML2_WITH_ICONV=OFF -DLIBXML2_WITH_ICU=OFF"
49+
AC_MSG_NOTICE([${LIBXML2_VENDOR_CONFIGURE_CMD}])
50+
if ! eval "${LIBXML2_VENDOR_CONFIGURE_CMD}"; then
51+
AC_MSG_ERROR([Failed to configure vendored libxml2 with CMake.])
52+
fi
53+
54+
LIBXML2_VENDOR_BUILD_CMD="cmake --build \"${LIBXML2_VENDOR_BUILD_DIR}\" --target LibXml2"
55+
AC_MSG_NOTICE([${LIBXML2_VENDOR_BUILD_CMD}])
56+
if ! eval "${LIBXML2_VENDOR_BUILD_CMD}"; then
57+
AC_MSG_ERROR([Failed to build vendored libxml2.])
58+
fi
59+
60+
LIBXML2_CFLAGS="-DWITH_LIBXML2 -I${LIBXML2_VENDOR_DIR}/include -I${LIBXML2_VENDOR_BUILD_DIR} -I${LIBXML2_VENDOR_BUILD_DIR}/libxml"
61+
LIBXML2_LDADD="-lxml2"
62+
LIBXML2_LDFLAGS="-L${LIBXML2_VENDOR_BUILD_DIR} -Wl,-rpath,${LIBXML2_VENDOR_BUILD_DIR}"
63+
LIBXML2_VERSION=`cd "${LIBXML2_VENDOR_DIR}" && git describe --tags --always 2>/dev/null || echo unknown`
64+
LIBXML2_DISPLAY="vendored source: ${LIBXML2_VENDOR_DIR}, build: ${LIBXML2_VENDOR_BUILD_DIR}"
65+
LIBXML2_FOUND=1
66+
else
67+
MSC_CHECK_LIB([LIBXML2], [libxml-2.0], [libxml/parser.h], [xml2], [-DWITH_LIBXML2], [2.6.29], [libxml])
68+
fi
69+
70+
AC_SUBST([LIBXML2_CFLAGS])
71+
AC_SUBST([LIBXML2_LDADD])
72+
AC_SUBST([LIBXML2_LDFLAGS])
73+
AC_SUBST([LIBXML2_VERSION])
74+
AC_SUBST([LIBXML2_DISPLAY])
75+
AC_SUBST([LIBXML2_FOUND])
1276
]) # AC_DEFUN [CHECK_LIBXML2]

src/operators/validate_dtd.cc

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,13 @@ bool ValidateDTD::init(const std::string &file, std::string *error) {
3838

3939

4040
bool ValidateDTD::evaluate(Transaction *transaction, const std::string &str) {
41-
42-
XmlDtdPtrManager dtd(xmlParseDTD(NULL, reinterpret_cast<const xmlChar *>(m_resource.c_str())));
43-
if (dtd.get() == NULL) {
44-
std::string err = std::string("XML: Failed to load DTD: ") \
45-
+ m_resource;
46-
ms_dbg_a(transaction, 4, err);
47-
return true;
48-
}
49-
50-
if (transaction->m_xml->m_data.doc == NULL) {
41+
if (!transaction->m_xml->hasDocument()) {
5142
ms_dbg_a(transaction, 4, "XML document tree could not "\
5243
"be found for DTD validation.");
5344
return true;
5445
}
5546

56-
if (transaction->m_xml->m_data.well_formed != 1) {
47+
if (!transaction->m_xml->isWellFormed()) {
5748
ms_dbg_a(transaction, 4, "XML: DTD validation failed because " \
5849
"content is not well formed.");
5950
return true;
@@ -69,28 +60,14 @@ bool ValidateDTD::evaluate(Transaction *transaction, const std::string &str) {
6960
}
7061
#endif
7162

72-
xmlValidCtxtPtr cvp = xmlNewValidCtxt();
73-
if (cvp == NULL) {
74-
ms_dbg_a(transaction, 4, "XML: Failed to create a validation context.");
75-
return true;
76-
}
77-
78-
/* Send validator errors/warnings to msr_log */
79-
cvp->error = (xmlSchemaValidityErrorFunc)error_runtime;
80-
cvp->warning = (xmlSchemaValidityErrorFunc)warn_runtime;
81-
cvp->userData = transaction;
82-
83-
if (!xmlValidateDtd(cvp, transaction->m_xml->m_data.doc, dtd.get())) {
63+
if (!transaction->m_xml->validateDocumentAgainstDtd(m_resource)) {
8464
ms_dbg_a(transaction, 4, "XML: DTD validation failed.");
85-
xmlFreeValidCtxt(cvp);
8665
return true;
8766
}
8867

8968
ms_dbg_a(transaction, 4, std::string("XML: Successfully validated " \
9069
"payload against DTD: ") + m_resource);
9170

92-
xmlFreeValidCtxt(cvp);
93-
9471
return false;
9572
}
9673
#endif

src/operators/validate_dtd.h

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,39 +19,16 @@
1919
#include <stdio.h>
2020
#include <stdarg.h>
2121
#include <string.h>
22-
#ifdef WITH_LIBXML2
23-
#include <libxml/xmlschemas.h>
24-
#include <libxml/xpath.h>
25-
#endif
2622
#include <string>
2723
#include <memory>
2824
#include <utility>
2925

3026
#include "src/operators/operator.h"
31-
#include "validate_schema.h"
3227

3328

3429
namespace modsecurity {
3530
namespace operators {
3631

37-
#ifdef WITH_LIBXML2
38-
class XmlDtdPtrManager {
39-
public:
40-
/** @ingroup ModSecurity_Operator */
41-
explicit XmlDtdPtrManager(xmlDtdPtr dtd)
42-
: m_dtd(dtd) { }
43-
~XmlDtdPtrManager() {
44-
if (m_dtd != NULL) {
45-
xmlFreeDtd(m_dtd);
46-
m_dtd = NULL;
47-
}
48-
}
49-
xmlDtdPtr get() const {return m_dtd;}
50-
private:
51-
xmlDtdPtr m_dtd; // The resource being managed
52-
};
53-
#endif
54-
5532
class ValidateDTD : public Operator {
5633
public:
5734
/** @ingroup ModSecurity_Operator */
@@ -62,22 +39,6 @@ class ValidateDTD : public Operator {
6239
bool init(const std::string &file, std::string *error) override;
6340

6441

65-
static void error_runtime(void *ctx, const char *msg, ...) {
66-
va_list args;
67-
va_start(args, msg);
68-
ValidateSchema::callback_func(ctx, ValidateSchema::log_msg, ValidateSchema::PREFIX_ERROR, msg, args);
69-
va_end(args);
70-
}
71-
72-
73-
static void warn_runtime(void *ctx, const char *msg, ...) {
74-
va_list args;
75-
va_start(args, msg);
76-
ValidateSchema::callback_func(ctx, ValidateSchema::log_msg, ValidateSchema::PREFIX_WARNING, msg, args);
77-
va_end(args);
78-
}
79-
80-
8142
private:
8243
std::string m_resource;
8344
#endif

src/operators/validate_schema.cc

Lines changed: 6 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -42,72 +42,23 @@ bool ValidateSchema::init(const std::string &file, std::string *error) {
4242
bool ValidateSchema::evaluate(Transaction *transaction,
4343
const std::string &str) {
4444

45-
if (transaction->m_xml->m_data.doc == NULL) {
45+
if (!transaction->m_xml->hasDocument()) {
4646
ms_dbg_a(transaction, 4, "XML document tree could not be found for " \
4747
"schema validation.");
4848
return true;
4949
}
5050

51-
if (transaction->m_xml->m_data.well_formed != 1) {
51+
if (!transaction->m_xml->isWellFormed()) {
5252
ms_dbg_a(transaction, 4, "XML: Schema validation failed because " \
5353
"content is not well formed.");
5454
return true;
5555
}
5656

57-
xmlSchemaParserCtxtPtr parserCtx = xmlSchemaNewParserCtxt(m_resource.c_str());
58-
if (parserCtx == NULL) {
59-
std::stringstream err;
60-
err << "XML: Failed to load Schema from file: ";
61-
err << m_resource;
62-
err << ". ";
63-
if (m_err.empty() == false) {
64-
err << m_err;
57+
m_err.clear();
58+
if (!transaction->m_xml->validateDocumentAgainstSchema(m_resource, &m_err)) {
59+
if (!m_err.empty()) {
60+
ms_dbg_a(transaction, 4, m_err);
6561
}
66-
ms_dbg_a(transaction, 4, err.str());
67-
return true;
68-
}
69-
70-
xmlSchemaSetParserErrors(parserCtx,
71-
(xmlSchemaValidityErrorFunc)error_load,
72-
(xmlSchemaValidityWarningFunc)warn_load, &m_err);
73-
74-
xmlSchemaPtr schema = xmlSchemaParse(parserCtx);
75-
if (schema == NULL) {
76-
std::stringstream err;
77-
err << "XML: Failed to load Schema: ";
78-
err << m_resource;
79-
err << ".";
80-
if (m_err.empty() == false) {
81-
err << " " << m_err;
82-
}
83-
ms_dbg_a(transaction, 4, err.str());
84-
xmlSchemaFreeParserCtxt(parserCtx);
85-
return true;
86-
}
87-
88-
xmlSchemaValidCtxtPtr validCtx = xmlSchemaNewValidCtxt(schema);
89-
if (validCtx == NULL) {
90-
std::stringstream err("XML: Failed to create validation context.");
91-
if (m_err.empty() == false) {
92-
err << " " << m_err;
93-
}
94-
ms_dbg_a(transaction, 4, err.str());
95-
xmlSchemaFree(schema);
96-
xmlSchemaFreeParserCtxt(parserCtx);
97-
return true;
98-
}
99-
100-
/* Send validator errors/warnings to msr_log */
101-
xmlSchemaSetValidErrors(validCtx,
102-
(xmlSchemaValidityErrorFunc)error_runtime,
103-
(xmlSchemaValidityWarningFunc)warn_runtime, transaction);
104-
105-
int rc = xmlSchemaValidateDoc(validCtx, transaction->m_xml->m_data.doc);
106-
107-
xmlSchemaFreeValidCtxt(validCtx);
108-
xmlSchemaFree(schema);
109-
xmlSchemaFreeParserCtxt(parserCtx);
110-
if (rc != 0) {
11162
ms_dbg_a(transaction, 4, "XML: Schema validation failed.");
11263
return true; /* No match. */
11364
} else {

src/operators/validate_schema.h

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@
1919
#include <stdio.h>
2020
#include <stdarg.h>
2121
#include <string.h>
22-
#ifdef WITH_LIBXML2
23-
#include <libxml/xmlschemas.h>
24-
#include <libxml/xpath.h>
25-
#endif
2622
#include <string>
2723
#include <memory>
2824
#include <utility>
@@ -43,61 +39,6 @@ class ValidateSchema : public Operator {
4339
bool evaluate(Transaction *transaction, const std::string &str) override;
4440
bool init(const std::string &file, std::string *error) override;
4541

46-
47-
static void error_load(void *ctx, const char *msg, ...) {
48-
va_list args;
49-
va_start(args, msg);
50-
callback_func(ctx, append_msg, PREFIX_ERROR, msg, args);
51-
va_end(args);
52-
}
53-
54-
55-
static void warn_load(void *ctx, const char *msg, ...) {
56-
va_list args;
57-
va_start(args, msg);
58-
callback_func(ctx, append_msg, PREFIX_WARNING, msg, args);
59-
va_end(args);
60-
}
61-
62-
63-
static void error_runtime(void *ctx, const char *msg, ...) {
64-
va_list args;
65-
va_start(args, msg);
66-
callback_func(ctx, log_msg, PREFIX_ERROR, msg, args);
67-
va_end(args);
68-
}
69-
70-
71-
static void warn_runtime(void *ctx, const char *msg, ...) {
72-
va_list args;
73-
va_start(args, msg);
74-
callback_func(ctx, log_msg, PREFIX_WARNING, msg, args);
75-
va_end(args);
76-
}
77-
78-
79-
template<typename Pred>
80-
static void callback_func(void *ctx, Pred pred, const char *base_msg, const char *msg, va_list args) {
81-
char buf[1024];
82-
const auto len = vsnprintf(buf, sizeof(buf), msg, args);
83-
84-
if (len > 0)
85-
pred(ctx, base_msg + std::string(buf));
86-
}
87-
88-
static void log_msg(const void *ctx, const std::string &msg) {
89-
auto t = reinterpret_cast<const Transaction *>(ctx);
90-
ms_dbg_a(t, 4, msg);
91-
}
92-
93-
static void append_msg(void *ctx, const std::string &msg) {
94-
auto s = reinterpret_cast<std::string*>(ctx);
95-
s->append(msg);
96-
}
97-
98-
static constexpr auto PREFIX_WARNING = "XML Warning: ";
99-
static constexpr auto PREFIX_ERROR = "XML Error: ";
100-
10142
private:
10243
std::string m_resource;
10344
std::string m_err;

0 commit comments

Comments
 (0)