Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adds support to Python scripts on ModSecurity core.

Analog of what we have for Lua, Python support is now added by this commit.
This is very experimental.
  • Loading branch information...
commit 66118d3c214b0e3ef7be7bb4a0d6430d0a5cdbd8 1 parent c17bf6a
@zimmerle zimmerle authored
View
12 apache2/Makefile.am
@@ -20,6 +20,7 @@ mod_security2_la_SOURCES = acmp.c \
msc_multipart.c \
msc_parsers.c \
msc_pcre.c \
+ msc_python.c \
msc_release.c \
msc_reqbody.c \
msc_tree.c \
@@ -41,6 +42,7 @@ mod_security2_la_CFLAGS = @APR_CFLAGS@ \
@LUA_CFLAGS@ \
@MODSEC_EXTRA_CFLAGS@ \
@PCRE_CFLAGS@ \
+ @PYTHON_CFLAGS@ \
@YAJL_CFLAGS@
@@ -53,6 +55,7 @@ mod_security2_la_LIBADD = @APR_LDADD@ \
@LIBXML2_LDADD@ \
@LUA_LDADD@ \
@PCRE_LDADD@ \
+ @PYTHON_LDADD@ \
@YAJL_LDADD@
if AIX
@@ -63,7 +66,9 @@ mod_security2_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
+
endif
if HPUX
@@ -74,6 +79,7 @@ mod_security2_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -85,6 +91,7 @@ mod_security2_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -96,6 +103,7 @@ mod_security2_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -107,6 +115,7 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version -R @PCRE_LD_PATH
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -118,6 +127,7 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -129,6 +139,7 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -140,6 +151,7 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
View
51 apache2/apache2_config.c
@@ -26,6 +26,9 @@
#include "msc_lua.h"
#endif
+#ifdef WITH_PYTHON
+#include "msc_python.h"
+#endif
/* -- Directory context creation and initialisation -- */
@@ -771,11 +774,19 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type,
/* Create the rule now. */
switch(type) {
#if defined(WITH_LUA)
- case RULE_TYPE_LUA :
+ case RULE_TYPE_LUA:
rule = msre_rule_lua_create(dcfg->ruleset, cmd->directive->filename,
cmd->directive->line_num, p1, p2, &my_error_msg);
break;
#endif
+
+ #ifdef WITH_PYTHON
+ case RULE_TYPE_PYTHON:
+ rule = msre_rule_python_create(dcfg->ruleset, cmd->directive->filename,
+ cmd->directive->line_num, p1, p2, &my_error_msg);
+ break;
+ #endif
+
default :
rule = msre_rule_create(dcfg->ruleset, type, cmd->directive->filename,
cmd->directive->line_num, p1, p2, p3, &my_error_msg);
@@ -791,6 +802,9 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type,
#if defined(WITH_LUA)
type != RULE_TYPE_LUA &&
#endif
+#ifdef WITH_PYTHON
+ type != RULE_TYPE_PYTHON &&
+#endif
(dcfg->tmp_chain_starter == NULL))
if(rule->actionset == NULL)
return "ModSecurity: Rules must have at least id action";
@@ -800,11 +814,13 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type,
#if defined(WITH_LUA)
&& (type != RULE_TYPE_LUA)
#endif
+#ifdef WITH_PYTHON
+ && (type != RULE_TYPE_PYTHON)
+#endif
)
return "ModSecurity: No action id present within the rule";
-#if defined(WITH_LUA)
- if(type != RULE_TYPE_LUA)
-#endif
+
+ if(type != RULE_TYPE_LUA &&type != RULE_TYPE_PYTHON)
{
rid = apr_hash_get(dcfg->rule_id_htab, rule->actionset->id, APR_HASH_KEY_STRING);
if(rid != NULL) {
@@ -2246,13 +2262,30 @@ static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag)
static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg,
const char *p1, const char *p2)
{
- #if defined(WITH_LUA)
const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1);
- return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL);
- #else
- ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Ignoring SecRuleScript \"%s\" directive (%s:%d): No Lua scripting support.", p1, cmd->directive->filename, cmd->directive->line_num);
+
+ if (strlen(filename) > 3) {
+ const char *p = filename + strlen(filename) - 3;
+
+#ifdef WITH_PYTHON
+ if ((p[0] == '.')&&(p[1] == 'p')&&(p[2] == 'y'))
+ {
+ return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_PYTHON, filename, p2, NULL);
+ }
+#endif
+#ifdef WITH_LUA
+ if ((p[0] == 'l')&&(p[1] == 'u')&&(p[2] == 'a'))
+ {
+ return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL);
+ }
+#endif
+ }
+
+#if !defined(WITH_PYTHON) || !defined(WITH_LUA)
+ ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Ignoring SecRuleScript \"%s\" directive (%s:%d): No Lua scripting or Python support.", p1, cmd->directive->filename, cmd->directive->line_num);
+#endif
+
return NULL;
- #endif
}
static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg,
View
4 apache2/mod_security2.c
@@ -37,6 +37,10 @@
#include "msc_lua.h"
#endif
+#ifdef WITH_PYTHON
+#include "msc_python.h"
+#endif
+
#include "msc_status_engine.h"
/* ModSecurity structure */
View
4 apache2/modsecurity.h
@@ -60,6 +60,10 @@ typedef struct msc_parm msc_parm;
#include "msc_lua.h"
#endif
+#ifdef WITH_PYTHON
+#include "msc_python.h"
+#endif
+
#define PHASE_REQUEST_HEADERS 1
#define PHASE_REQUEST_BODY 2
#define PHASE_RESPONSE_HEADERS 3
View
6 apache2/modules.mk
@@ -1,11 +1,13 @@
MOD_SECURITY2 = mod_security2 apache2_config apache2_io apache2_util \
re re_operators re_actions re_tfns re_variables msc_json \
msc_logging msc_xml msc_multipart modsecurity msc_parsers msc_util msc_pcre \
- persist_dbm msc_reqbody pdf_protect msc_geo msc_gsb msc_crypt msc_tree msc_unicode acmp msc_lua
+ persist_dbm msc_reqbody pdf_protect msc_geo msc_gsb msc_crypt msc_tree msc_unicode acmp msc_lua \
+ msc_python
H = re.h modsecurity.h msc_logging.h msc_multipart.h msc_parsers.h msc_json.h \
msc_pcre.h msc_util.h msc_xml.h persist_dbm.h apache2.h pdf_protect.h \
- msc_geo.h msc_gsb.h msc_crypt.h msc_tree.h msc_unicode.h acmp.h utf8tables.h msc_lua.h
+ msc_geo.h msc_gsb.h msc_crypt.h msc_tree.h msc_unicode.h acmp.h utf8tables.h msc_lua.h \
+ msc_python.h
${MOD_SECURITY2:=.slo}: ${H}
${MOD_SECURITY2:=.lo}: ${H}
View
465 apache2/msc_python.c
@@ -0,0 +1,465 @@
+/*
+* ModSecurity for Apache 2.x, http://www.modsecurity.org/
+* Copyright (c) 2004-2013 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.
+*/
+
+#ifdef WITH_PYTHON
+
+#include "msc_python.h"
+
+#include "apr_lib.h"
+#include "apr_strmatch.h"
+#include "apr_strings.h"
+#include "apache2.h"
+#include <Python.h>
+
+#define PY_STR_DBG(arg, msr, z) { if (arg == NULL) { msr_log(msr, 8, "Python -%s-: wheee NULLL!!!", z); } else { PyObject *e = PyObject_Repr(arg); PyObject *a = PyUnicode_AsEncodedString(e, "utf-8", NULL); char *s = PyBytes_AsString(a); msr_log(msr, 8, "Python -%s-: %s", z, s); } }
+
+/* ModSecurityI */
+
+static PyObject *pyModSecurityI_log(PyObject *self, PyObject *args, PyObject *kwds) {
+ char *str = NULL;
+ int level = 0;
+ PyObject *capsuleModSecurity = NULL;
+ modsec_rec *msr = NULL;
+
+ if (PyArg_ParseTuple(args, "is", &level, &str) == 0)
+ {
+ /* PyArg already set this.
+ * PyErr_SetString(PyExc_TypeError, "log() takes exactly 2 arguments.");
+ */
+ goto end;
+ }
+
+ capsuleModSecurity = PyObject_GetAttrString(self, "capsuleModSecurity");
+ if (capsuleModSecurity == NULL)
+ {
+ // FIXME: Use the correct error paramenter.
+ PyErr_SetString(PyExc_TypeError, "log() needs ModSecurity core to be attached.");
+ goto end;
+ }
+
+ msr = PyCapsule_GetPointer(capsuleModSecurity, "modsecurity");
+ if (msr == NULL)
+ {
+ // FIXME: Use the correct error paramenter.
+ PyErr_SetString(PyExc_TypeError, "log() needs ModSecurity core to be attached.");
+ goto end_no_msr;
+ }
+
+ msr_log(msr, level, str);
+
+end_no_msr:
+ Py_DECREF(capsuleModSecurity);
+end:
+ Py_RETURN_NONE;
+}
+
+static PyObject *pyModSecurityI_setCapsuleModSecurity(PyObject *self, PyObject *args, PyObject *kwds) {
+ PyObject *capsule = NULL;
+
+ if (PyArg_ParseTuple(args, "O", &capsule) == 0)
+ {
+ /* PyArg already set this.
+ * PyErr_SetString(PyExc_TypeError, "setCapsuleModSecurity() takes exactly 1 argument.");
+ */
+ goto end;
+ }
+
+ if (capsule == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setCapsuleModSecurity() Capsule cannot be NULL.");
+ goto end;
+ }
+
+ if (PyModule_AddObject(self, "capsuleModSecurity", capsule) == -1)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setCapsuleModSecurity() Failed to save capsule.");
+ goto end;
+ }
+
+end:
+ Py_RETURN_NONE;
+}
+
+static PyObject *pyModSecurityI_setCapsuleRule(PyObject *self, PyObject *args, PyObject *kwds) {
+ PyObject *capsule = NULL;
+
+ if (PyArg_ParseTuple(args, "O", &capsule) == 0)
+ {
+ /* PyArg already set this.
+ * PyErr_SetString(PyExc_TypeError, "setCapsuleRule() takes exactly 1 argument.");
+ */
+ goto end;
+ }
+
+ if (capsule == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setCapsuleRule() Capsule cannot be NULL.");
+ goto end;
+ }
+
+ if (PyModule_AddObject(self, "capsuleRule", capsule) == -1)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setCapsuleRule() Failed to save capsule.");
+ goto end;
+ }
+
+end:
+ Py_RETURN_NONE;
+}
+
+static PyObject *pyModSecurityI_getVariable(PyObject *self, PyObject *args, PyObject *kwds) {
+ char *my_error_msg = NULL;
+ const char *var_name = NULL;
+ const apr_array_header_t *arr = NULL;
+ PyObject *ret = Py_None;
+ PyObject *capsuleRule = NULL;
+ PyObject *capsuleModSecurity = NULL;
+ msre_rule *rule = NULL;
+ msre_var *var = NULL;
+ modsec_rec *msr = NULL;
+ apr_table_t *vartab = NULL;
+
+ if (PyArg_ParseTuple(args, "s", &var_name) == 0)
+ {
+ goto end;
+ }
+
+ capsuleRule = PyObject_GetAttrString(self, "capsuleRule");
+ if (capsuleRule == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "getVariable() needs ModSecurity core to be attached.");
+ goto end_no_rule;
+ }
+
+ capsuleModSecurity = PyObject_GetAttrString(self, "capsuleModSecurity");
+ if (capsuleModSecurity == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "getVariable() needs ModSecurity core to be attached.");
+ goto end_no_msr;
+ }
+
+ rule = PyCapsule_GetPointer(capsuleRule, "rule");
+ msr = PyCapsule_GetPointer(capsuleModSecurity, "modsecurity");
+ if (rule == NULL || msr == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "getVariable() needs ModSecurity core to be attached.");
+ goto end_no_rule_or_msr;
+ }
+
+ var = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre,
+ var_name, '\0', msr, &my_error_msg);
+
+ if (var == NULL)
+ {
+ goto end_no_var;
+ }
+
+ vartab = apr_table_make(msr->msc_rule_mptmp, 16);
+ if (vartab == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "getVariable() Internal error, failed to create var table.");
+ goto end_no_vartab;
+ }
+
+ var->metadata->generate(msr, var, rule, vartab, msr->msc_rule_mptmp);
+ arr = apr_table_elts(vartab);
+
+ if (arr->nelts == 1)
+ {
+ msre_var *vx = generate_single_var(msr, var, NULL, rule, msr->msc_rule_mptmp);
+ if (vx != NULL)
+ {
+ ret = Py_BuildValue("s", vx->value);
+ }
+ }
+ else if (arr->nelts > 1)
+ {
+ // FIXME: We should have an object to encapsulate this dictionary, in order to auto-save the changes.
+ int i = 0;
+ const apr_table_entry_t *te = NULL;
+ PyObject *pDict = PyDict_New();
+
+ te = (apr_table_entry_t *)arr->elts;
+
+ for (i = 0; i < arr->nelts; i++)
+ {
+ msre_var *str = te[i].val;
+ PyDict_SetItemString(pDict, str->name + strlen(var->name) + 1, Py_BuildValue("s", str->value));
+ }
+
+ ret = pDict;
+ }
+
+end_no_vartab:
+end_no_var:
+end_no_rule_or_msr:
+ Py_DECREF(capsuleModSecurity);
+end_no_msr:
+ Py_DECREF(capsuleRule);
+end_no_rule:
+end:
+ return ret;
+}
+
+static PyObject *pyModSecurityI_applyTransformation(PyObject *self, PyObject *args, PyObject *kwds)
+{
+
+ // TODO: Implement.
+
+ PyErr_SetString(PyExc_TypeError, "applyTransformation() is not ready yet.");
+
+ Py_RETURN_NONE;
+}
+
+static PyObject *pyModSecurityI_setVariable(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ char *name = NULL;
+ char *value = NULL;
+ PyObject *ret = Py_None;
+ PyObject *capsuleRule = NULL;
+ PyObject *capsuleModSecurity = NULL;
+ msre_rule *rule = NULL;
+ modsec_rec *msr = NULL;
+
+ if (PyArg_ParseTuple(args, "ss", &name, &value) == 0)
+ {
+ goto end;
+ }
+
+ capsuleRule = PyObject_GetAttrString(self, "capsuleRule");
+ if (capsuleRule == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setVariable() needs ModSecurity core to be attached.");
+ goto end_no_rule;
+ }
+
+ capsuleModSecurity = PyObject_GetAttrString(self, "capsuleModSecurity");
+ if (capsuleModSecurity == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setVariable() needs ModSecurity core to be attached.");
+ goto end_no_msr;
+ }
+
+ rule = PyCapsule_GetPointer(capsuleRule, "rule");
+ msr = PyCapsule_GetPointer(capsuleModSecurity, "modsecurity");
+ if (rule == NULL || msr == NULL)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setVariable() needs ModSecurity core to be attached.");
+ goto end_no_rule_or_msr;
+ }
+
+
+ if (msre_action_setvar_execute(msr, msr->msc_rule_mptmp, rule, name, value) != 1)
+ {
+ // FIXME: Use the correct error object.
+ PyErr_SetString(PyExc_TypeError, "setVariable() Internal error. Failed to save variable.");
+ goto end_failed_to_set;
+ }
+
+end_failed_to_set:
+end_no_rule_or_msr:
+ Py_DECREF(capsuleModSecurity);
+end_no_msr:
+ Py_DECREF(capsuleRule);
+end_no_rule:
+end:
+ return ret;
+}
+
+
+static PyMethodDef pyModSecurityI_functions[] = {
+ { "log", (PyCFunction)pyModSecurityI_log, METH_VARARGS, NULL },
+ { "setCapsuleModSecurity", (PyCFunction)pyModSecurityI_setCapsuleModSecurity, METH_VARARGS, NULL },
+ { "setCapsuleRule", (PyCFunction)pyModSecurityI_setCapsuleRule, METH_VARARGS, NULL },
+ { "getVariable", (PyCFunction)pyModSecurityI_getVariable, METH_VARARGS, NULL },
+ { "setVariable", (PyCFunction)pyModSecurityI_setVariable, METH_VARARGS, NULL },
+ { "applyTransformation", (PyCFunction)pyModSecurityI_applyTransformation, METH_VARARGS, NULL },
+ { NULL, NULL }
+};
+
+
+static struct PyModuleDef pyModSecurityI_def = {
+ PyModuleDef_HEAD_INIT,
+ "ModSecurityI",
+ NULL,
+ 0,
+ pyModSecurityI_functions,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+// FIXME: Split files.
+
+/**
+ *
+ */
+char *python_load(msc_python_script **script, const char *filename, apr_pool_t *pool)
+{
+ PyObject *pName, *pModule;
+ const char *path = NULL;
+ const char *file = NULL;
+ const char *module = NULL;
+
+ /*
+ * Script path?
+ * FIXME: Avoid to use apr_ functions
+ */
+ file = apr_filepath_name_get(filename);
+ path = apr_pstrndup(pool, filename, strlen(filename) - strlen(file));
+
+ Py_Initialize();
+
+ PyObject* sysPath = PySys_GetObject((char*)"path");
+ PyList_Append(sysPath, PyUnicode_FromFormat("."));
+ PyList_Append(sysPath, PyUnicode_FromFormat(path));
+
+ module = apr_pstrndup(pool, file, strlen(file) - strlen(".py"));
+ Py_SetProgramName(module); /* optional but recommended */
+
+ pName = PyUnicode_FromString(module);
+ pModule = PyImport_Import(pName);
+
+ if (pModule == NULL) {
+ const char *s = NULL;
+ PyObject *err = NULL;
+ PyObject *exc_type = NULL, *exc_value = NULL, *exc_tb = NULL;
+
+ PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
+ err = PyObject_Repr(exc_value); //Now a unicode object
+ PyObject* pyStr = PyUnicode_AsEncodedString(err, "utf-8", NULL);
+ s = PyBytes_AS_STRING(pyStr);
+
+ return apr_psprintf(pool, "ModSecurity: Failed to load script: %s - %s",
+ filename, s);
+ }
+
+ (*script) = apr_pcalloc(pool, sizeof(msc_python_script));
+ (*script)->name = strdup(filename);
+ (*script)->pName = pName;
+ (*script)->pModule = pModule;
+ (*script)->extInstance = NULL;
+
+ Py_DECREF(pName);
+
+ return NULL;
+}
+
+
+// FIXME: Error handling
+int python_execute(msc_python_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg) {
+ apr_time_t time_before;
+ int ret = RULE_NO_MATCH;
+ PyObject* methodRes = NULL;
+ PyObject *extObject = NULL;
+
+ if (error_msg == NULL) {
+ return -1;
+ }
+
+ *error_msg = NULL;
+
+// PY_STR_DBG(script->pModule, msr, "pMdoule");
+/*
+ if (msr->txcfg->debuglog_level >= 8) {
+ msr_log(msr, 8, "Python: Executing script: %s %d", script->name, script->extInstance);
+ }
+*/
+// time_before = apr_time_now();
+
+ /* open script pModule (Done while the rule was created.)
+ * load the class ModSecurityExtension
+ * instantiate it
+ * */
+
+
+ if (script->extInstance == NULL)
+ {
+ extObject = PyObject_GetAttrString(script->pModule, "ModSecurityExtension");
+// PY_STR_DBG(extObject, msr, "extObject");
+ script->extInstance = PyObject_CallObject(extObject, NULL);
+ // PY_STR_DBG(extInstance, msr, "extInstance");
+
+ PyObject *logMod = PyModule_Create(&pyModSecurityI_def);
+ PyObject *capsule_msr = PyCapsule_New(msr, "modsecurity", NULL);
+ PyObject *capsule_rule = PyCapsule_New(rule, "rule", NULL);
+ PyObject* setCapsule1 = PyObject_CallMethod(logMod, "setCapsuleModSecurity", "(O)", capsule_msr);
+ PyObject* setCapsule2 = PyObject_CallMethod(logMod, "setCapsuleRule", "(O)", capsule_rule);
+
+
+ PyObject* setLog = PyObject_CallMethod(script->extInstance, "setModSecurityCore", "(O)", logMod);
+ if (setLog == NULL)
+ {
+ const char *s = NULL;
+ PyObject *exc_type = NULL, *exc_value = NULL, *exc_tb = NULL, *err = NULL;
+
+ PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
+ err = PyObject_Repr(exc_value); //Now a unicode objecta
+
+ PyObject* pyStr = PyUnicode_AsEncodedString(err, "utf-8", NULL);
+ s = PyBytes_AS_STRING(pyStr);
+ msr_log(msr, 8, "Python problem: %s", s);
+ }
+
+ // PY_STR_DBG(setLog, msr, "setLog");
+
+ }
+ methodRes = PyObject_CallMethod(script->extInstance, "process", NULL);
+// ////PY_STR_DBG(methodRes, msr, "methodRes");
+#if 1
+
+ if (methodRes == NULL)
+ {
+ //TODO: Place the error message in the log.
+ msr_log(msr, 8, "Python script failed");
+ goto end;
+ }
+ if (methodRes == Py_True)
+ {
+ ret = RULE_MATCH;
+ }
+
+ Py_DECREF(methodRes);
+#endif
+end:
+ /* Returns status code to caller. */
+ /*
+ if (msr->txcfg->debuglog_level >= 8)
+ {
+ msr_log(msr, 8, "Python: Script completed in %" APR_TIME_T_FMT " usec, returning: %s.",
+ (apr_time_now() - time_before), (ret == RULE_MATCH) ? "match" : "NOT match" );
+ }
+ */
+
+// Py_DECREF(extInstance);
+// Py_DECREF(extObject);
+
+
+ return ret;
+}
+
+#endif /* WITH_PYTHON */
View
38 apache2/msc_python.h
@@ -0,0 +1,38 @@
+/*
+* ModSecurity for Apache 2.x, http://www.modsecurity.org/
+* Copyright (c) 2004-2013 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.
+*/
+
+#ifdef WITH_PYTHON
+
+#ifndef _MSC_PYTHON_H_
+#define _MSC_PYTHON_H_
+
+typedef struct msc_python_script msc_python_script;
+
+#include <Python.h>
+
+#include "apr_general.h"
+#include "apr_tables.h"
+#include "modsecurity.h"
+
+struct msc_python_script {
+ const char *name;
+ PyObject *pName;
+ PyObject *pModule;
+ PyObject *extInstance;
+};
+
+char DSOLOCAL *python_load(msc_python_script **script, const char *filename, apr_pool_t *pool);
+
+#endif /* _MSC_PYTHON_H_ */
+#endif /* WITH_PYTHON */
View
107 apache2/re.c
@@ -20,6 +20,10 @@
#include "msc_lua.h"
#endif
+#ifdef WITH_PYTHON
+#include "msc_python.h"
+#endif
+
static const char *const severities[] = {
"EMERGENCY",
"ALERT",
@@ -2327,6 +2331,19 @@ char * msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, con
}
break;
#endif
+#ifdef WITH_PYTHON
+ case RULE_TYPE_PYTHON:
+ /* SecRuleScript */
+ if (r_actions == NULL) {
+ unparsed = apr_psprintf(pool, "SecRuleScript \"%s\"", r_args);
+ }
+ else {
+ unparsed = apr_psprintf(pool, "SecRuleScript \"%s\" \"%s\"",
+ r_args, log_escape(pool, r_actions));
+ }
+ break;
+#endif
+
}
return unparsed;
@@ -2466,6 +2483,54 @@ msre_rule *msre_rule_lua_create(msre_ruleset *ruleset,
}
#endif
+#ifdef WITH_PYTHON
+/**
+ * Allocate and initialize the main structure needed for Python
+ * script execution.
+ *
+ */
+msre_rule *msre_rule_python_create(msre_ruleset *ruleset,
+ const char *fn, int line, const char *script_filename,
+ const char *actions, char **error_msg)
+{
+ msre_rule *rule;
+ char *my_error_msg;
+ char *filename = (char *)rule->op_param;
+
+ if (error_msg == NULL) return NULL;
+ *error_msg = NULL;
+
+ rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule));
+ if (rule == NULL) return NULL;
+
+ rule->type = RULE_TYPE_PYTHON;
+ rule->ruleset = ruleset;
+ rule->filename = apr_pstrdup(ruleset->mp, fn);
+ rule->line_num = line;
+
+ /* Load the script */
+ *error_msg = python_load(&rule->python_script, script_filename, ruleset->mp);
+ if (*error_msg != NULL) {
+ return NULL;
+ }
+
+ /* Parse actions */
+ if (actions != NULL) {
+ /* Create per-rule actionset */
+ rule->actionset = msre_actionset_create(ruleset->engine, ruleset->mp, actions, &my_error_msg);
+ if (rule->actionset == NULL) {
+ *error_msg = apr_psprintf(ruleset->mp, "Error parsing actions: %s", my_error_msg);
+ return NULL;
+ }
+ }
+
+ /* Add the unparsed rule */
+ rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, script_filename, NULL);
+
+ return rule;
+}
+#endif
+
/**
* Perform non-disruptive actions associated with the provided actionset.
*/
@@ -3331,6 +3396,41 @@ static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) {
}
#endif
+#ifdef WITH_PYTHON
+static apr_status_t msre_rule_process_python(msre_rule *rule, modsec_rec *msr) {
+ msre_actionset *acting_actionset = NULL;
+ char *my_error_msg = NULL;
+ int rc = 0;
+ /* Choose the correct metadata/disruptive action actionset. */
+ acting_actionset = rule->actionset;
+ if (rule->chain_starter != NULL) {
+ acting_actionset = rule->chain_starter->actionset;
+ }
+
+ rc = python_execute(rule->python_script, NULL, msr, rule, &my_error_msg);
+ if (rc < 0) {
+ msr_log(msr, 1, "%s", my_error_msg);
+ return -1;
+ }
+
+ /* A non-NULL error message means the rule matched. */
+ if (my_error_msg != NULL) {
+ /* Perform non-disruptive actions. */
+ msre_perform_nondisruptive_actions(msr, rule, rule->actionset, msr->msc_rule_mptmp);
+
+ /* Perform disruptive actions, but only if
+ * this rule is not part of a chain.
+ */
+ if (rule->actionset->is_chained == 0) {
+ msre_perform_disruptive_actions(msr, rule, acting_actionset, msr->msc_rule_mptmp, my_error_msg);
+ }
+ }
+
+ return rc;
+}
+#endif
+
+
/**
*
*/
@@ -3350,6 +3450,13 @@ static apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) {
}
#endif
+ #ifdef WITH_PYTHON
+ if (rule->type == RULE_TYPE_PYTHON) {
+ return msre_rule_process_python(rule, msr);
+ }
+ #endif
+
+
return msre_rule_process_normal(rule, msr);
}
View
19 apache2/re.h
@@ -46,6 +46,10 @@ typedef struct msre_cache_rec msre_cache_rec;
#include "msc_lua.h"
#endif
+#ifdef WITH_PYTHON
+#include "msc_python.h"
+#endif
+
/* Actions, variables, functions and operator functions */
char DSOLOCAL *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *rule, const char *p2,
const char *p3);
@@ -136,7 +140,10 @@ int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset
#define RULE_TYPE_ACTION 1 /* SecAction */
#define RULE_TYPE_MARKER 2 /* SecMarker */
#if defined(WITH_LUA)
-#define RULE_TYPE_LUA 3 /* SecRuleScript */
+#define RULE_TYPE_LUA 3 /* SecRuleScript - lua */
+#endif
+#ifdef WITH_PYTHON
+#define RULE_TYPE_PYTHON 4 /* SecRuleScript - python */
#endif
struct msre_rule {
@@ -167,6 +174,10 @@ struct msre_rule {
msc_script *script;
#endif
+#ifdef WITH_PYTHON
+ msc_python_script *python_script;
+#endif
+
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0
ap_regex_t *sub_regex;
#else
@@ -192,6 +203,12 @@ msre_rule DSOLOCAL *msre_rule_lua_create(msre_ruleset *ruleset,
const char *actions, char **error_msg);
#endif
+#ifdef WITH_PYTHON
+msre_rule DSOLOCAL *msre_rule_python_create(msre_ruleset *ruleset,
+ const char *fn, int line, const char *script_filename,
+ const char *actions, char **error_msg);
+#endif
+
#define VAR_SIMPLE 0 /* REQUEST_URI */
#define VAR_LIST 1
View
19 apache2/re_operators.c
@@ -3733,6 +3733,23 @@ static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) {
filename = resolve_relative_path(rule->ruleset->mp, rule->filename, filename);
+#ifdef WITH_PYTHON
+ /* ENH Write & use string_ends(s, e). */
+ if (strlen(rule->op_param) > 3) {
+ char *p = filename + strlen(filename) - 3;
+ if ((p[0] == '.')&&(p[1] == 'p')&&(p[2] == 'y'))
+ {
+ msc_python_script *script = NULL;
+
+ /* Compile script. */
+ *error_msg = python_load(&script, filename, rule->ruleset->mp);
+ if (*error_msg != NULL) return -1;
+
+ rule->op_param_data = script;
+ }
+ }
+#endif
+
#if defined(WITH_LUA)
/* ENH Write & use string_ends(s, e). */
if (strlen(rule->op_param) > 4) {
@@ -3748,7 +3765,7 @@ static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) {
rule->op_param_data = script;
}
}
- #endif
+#endif
if (rule->op_param_data == NULL) {
/* ENH Verify the script exists and that we have
View
56 scripts/python/modsecurity.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import sys
+
+# Needs to be singlenton ?
+class Singleton(object):
+ _instances = {}
+"""
+ def __new__(class_, *args, **kwargs):
+ if class_ not in class_._instances:
+ class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
+ return class_._instances[class_]
+"""
+
+class ModSecurity():
+ def __init__(self):
+ self.default_attr = ["default_attr", "name", "modsecCore"]
+ self.name = None
+ self.modsecCore = None
+
+ def setModSecurityCore(self, cb):
+ self.modsecCore = cb
+ self.log(8, "Log attached");
+ return True
+
+ def log(self, level, msg):
+ if self.modsecCore == None:
+ print("ModSecurity Python: ", str(level) + " " + str(msg), file=sys.stderr)
+ else:
+ self.modsecCore.log(level, msg)
+ return True
+
+ def __getattribute__(self, key):
+ v = None
+ try:
+ v = object.__getattribute__(self, key)
+ if hasattr(v, '__get__'):
+ return v.__get__(None, self)
+ except:
+ if self.modsecCore != None:
+ v = self.modsecCore.getVariable(key)
+
+ return v
+
+ def __setattr__(self, name, value):
+ self.__dict__[name] = value
+
+ if name not in self.default_attr:
+ if self.modsecCore != None:
+ self.modsecCore.setVariable("tx." + name, value)
+
+
+"""
+TODO: transformation
+"""
View
15 scripts/python/setup.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup(name="ModSecurity Python extension",
+ version="0.1",
+ description="ModSecurity python externsion",
+ author="Felipe Zimmerle",
+ author_email="felipe@zimmerle.org",
+ url="http://www.modsecurity.org",
+ py_modules=['modsecurity'],
+ keywords="ModSecurity WAF Security",
+
+ )
+
View
50 scripts/python/skell.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# ModSecurity core binding.
+from modsecurity import ModSecurity
+
+class ModSecurityExtension(ModSecurity):
+ """
+ Class ModSecurityExtension should represents your custom module.
+ Nocite that this class should derivate from ModSecurity and should
+ implement the method process.
+ """
+
+ def __init__(self):
+ ModSecurity.__init__(self)
+
+ def process(self):
+ """
+ The method is called by ModSecurity core whenever a request should
+ is needed to be evaluated.
+ """
+
+ # self.log can be utilised to produce content inside the SecDebugLog
+ # (https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#secdebuglog)
+ self.log(8, "This is our custom Python script, it seems that I am working"
+ "like a charm.")
+
+
+ self.log(8, "Hum... Do you have some content that was uploaded? %s" %
+ self.FILES_TMPNAMES)
+
+ # Returns True whenever you want to send a "match" to ModSecurity core.
+ return True
+
+# Should be used to test our custom extension deattached from ModSecurity core.
+if __name__ == "__main__":
+ myExtension = ModSecurityExtension()
+
+ # Setting FILES_TMPNAMES property.
+ # https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#files_tmpnames
+ myExtension.FILES_TMPNAMES = [ "/etc/issue", "/etc/resolv.conf" ]
+
+ # Process the content.
+ ret = myExtension.process()
+
+ if ret == True:
+ print("Matched!")
+ else:
+ print("_not_ matched")
+
View
11 standalone/Makefile.am
@@ -21,6 +21,7 @@ standalone_la_SOURCES = ../apache2/acmp.c \
../apache2/msc_multipart.c \
../apache2/msc_parsers.c \
../apache2/msc_pcre.c \
+ ../apache2/msc_python.c \
../apache2/msc_release.c \
../apache2/msc_reqbody.c \
../apache2/msc_tree.c \
@@ -51,6 +52,7 @@ standalone_la_CFLAGS = -DVERSION_NGINX \
@LUA_CFLAGS@ \
@MODSEC_EXTRA_CFLAGS@ \
@PCRE_CFLAGS@ \
+ @PYTHON_CFLAGS@ \
@YAJL_CFLAGS@
standalone_la_CPPFLAGS = @APR_CPPFLAGS@ \
@@ -62,6 +64,7 @@ standalone_la_LIBADD = @APR_LDADD@ \
@LIBXML2_LDADD@ \
@LUA_LDADD@ \
@PCRE_LDADD@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDADD@
if AIX
@@ -72,6 +75,7 @@ standalone_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -83,6 +87,7 @@ standalone_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -94,6 +99,7 @@ standalone_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -105,6 +111,7 @@ standalone_la_LDFLAGS = -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -116,6 +123,7 @@ standalone_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -127,6 +135,7 @@ standalone_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -138,6 +147,7 @@ standalone_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
@@ -149,5 +159,6 @@ standalone_la_LDFLAGS = -no-undefined -module -avoid-version \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
endif
View
4 tests/Makefile.am
@@ -14,6 +14,7 @@ msc_test_SOURCES = msc_test.c \
$(top_srcdir)/apache2/msc_multipart.c \
$(top_srcdir)/apache2/msc_parsers.c \
$(top_srcdir)/apache2/msc_pcre.c \
+ $(top_srcdir)/apache2/msc_python.c \
$(top_srcdir)/apache2/msc_release.c \
$(top_srcdir)/apache2/msc_reqbody.c \
$(top_srcdir)/apache2/msc_tree.c \
@@ -36,6 +37,7 @@ msc_test_CFLAGS = @APR_CFLAGS@ \
@LUA_CFLAGS@ \
@MODSEC_EXTRA_CFLAGS@ \
@PCRE_CFLAGS@ \
+ @PYTHON_CFLAGS@ \
@YAJL_CFLAGS@
msc_test_CPPFLAGS = -I$(top_srcdir)/apache2 \
@@ -48,6 +50,7 @@ msc_test_LDADD = @APR_LDADD@ \
@LIBXML2_LDADD@ \
@LUA_LDADD@ \
@PCRE_LDADD@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDADD@
msc_test_LDFLAGS = @APR_LDFLAGS@ \
@@ -56,6 +59,7 @@ msc_test_LDFLAGS = @APR_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
+ @PYTHON_LDFLAGS@ \
@YAJL_LDFLAGS@
check_SCRIPTS = run-unit-tests.pl
Please sign in to comment.
Something went wrong with that request. Please try again.