From b60f206976a209bdf54aad5110e03807b0a2886f Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 16 May 2007 19:37:27 +0000 Subject: [PATCH 001/372] Remove use of GNU extention strnlen(). Fix CHANGES. --- CHANGES | 33 +++++++++++++++++---------------- apache2/msc_geo.c | 23 ++++++++++++++++++++--- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index fc531217..f4b620ce 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ ?? ??? 2007 - 2.2.0-trunk +------------------------- + + * Removed strnlen() calls for non-GNU platforms. + +11 May 2007 - 2.2.0-dev1 ------------------------- * Add SecGeoLookupsDb, @geoLookups and GEO collection to support @@ -18,10 +23,10 @@ * Added experimental variables RESPONSE_CONTENT_LENGTH, RESPONSE_CONTENT_TYPE, and RESPONSE_CONTENT_ENCODING. - * Added experimental support for content injection. Directive SecContentInjection - (On|Off) controls whether injection is taking place. Actions "prepend" - and "append" inject content when executed. Do note that it is your - responsibility to make sure the response is of the appropriate + * Added experimental support for content injection. Directive + SecContentInjection (On|Off) controls whether injection is taking place. + Actions "prepend" and "append" inject content when executed. Do note that + it is your responsibility to make sure the response is of the appropriate content type (e.g. HTML, plain text, etc). * Added string comparison operators with support for macro expansion: @@ -32,7 +37,7 @@ * Removed support for %0 - %9 capture macros as they were incorrectly expanding url encoded values. Use %{TX.0} - %{TX.9} instead. - + * Added t:length to transform a value to its character length. * Added t:trimLeft, t:trimRight, t:trim to remove whitespace @@ -58,25 +63,21 @@ and/or counting operator in the debug log. -05 Apr 2007 - 2.1.1-rc2 ------------------------ +11 Apr 2007 - 2.1.1 +------------------- * Add the PCRE_DOLLAR_ENDONLY option when compiling regular expression for the @rx operator and variables. - + * Really set PCRE_DOTALL option when compiling the regular expression for the @rx operator as the docs state. - - -11 Mar 2007 - 2.1.1-rc1 ------------------------ - + * Fixed potential memory corruption when expanding macros. - * Fixed error when a collection var was fetched in the same second as creation - by setting the rate to zero. + * Fixed error when a collection was retrieved from storage in the same second + as creation by setting the rate to zero. - * Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms + * Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms. * Fixed the faulty REQUEST_FILENAME variable, which used to change the internal Apache structures by mistake. diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 798d8d28..0cf74998 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -228,6 +228,23 @@ static int db_open(directory_config *dcfg, char **error_msg) return 1; } +static int field_length(const char *field, int maxlen) +{ + int i; + + if (field == NULL) { + return 0; + } + + for (i = 0; i < maxlen; i++) { + if (field[i] == '\0') { + break; + } + } + + return i; +} + /** * Initialise Geo data structure */ @@ -375,21 +392,21 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro remaining -= rec_offset; /* Region */ - field_len = strnlen((const char *)cbuf+rec_offset,remaining); + field_len = field_length((const char *)cbuf+rec_offset, remaining); msr_log(msr, 9, "GEO: region=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); georec->region = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); rec_offset += field_len + 1; remaining -= field_len + 1; /* City */ - field_len = strnlen((const char *)cbuf+rec_offset,remaining); + field_len = field_length((const char *)cbuf+rec_offset, remaining); msr_log(msr, 9, "GEO: city=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); georec->city = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); rec_offset += field_len + 1; remaining -= field_len + 1; /* Postal Code */ - field_len = strnlen((const char *)cbuf+rec_offset,remaining); + field_len = field_length((const char *)cbuf+rec_offset, remaining); msr_log(msr, 9, "GEO: postal_code=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); georec->postal_code = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); rec_offset += field_len + 1; From e03ea11f9aa57139dc8c30e04aef6e6b8d41f90c Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 16 May 2007 19:48:21 +0000 Subject: [PATCH 002/372] Only calculate debug data when we are debugging. --- CHANGES | 2 ++ apache2/re_operators.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index f4b620ce..58b2de35 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Only calculate debugging vars when we are debugging (more to come). + * Removed strnlen() calls for non-GNU platforms. 11 May 2007 - 2.2.0-dev1 diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 50ab6518..db0071f8 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -110,14 +110,17 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; /* Warn when the regex captures but "capture" is not set */ - if (msr->txcfg->debuglog_level >= 3) { + if (msr->txcfg->debuglog_level >= 4) { int capcount = 0; rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount); if ((capture == 0) && (capcount > 0)) { msr_log(msr, 4, "Ignoring regex captures since \"capture\" action is not enabled."); } if ((capture == 1) && (capcount == 0)) { - msr_log(msr, 3, "Notice. The \"capture\" action is enabled, but the regex does not have explicit captures."); + msr_log(msr, 5, "The \"capture\" action is enabled, but the regex does not have explicit captures."); + } + if ((capture == 0) && (capcount > 0)) { + msr_log(msr, 6, "The \"capture\" action is not enabled, but the regex contains captures."); } } From 97a1718d39e53a72cc21e13c6fa5d550b7150120 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 16 May 2007 19:55:13 +0000 Subject: [PATCH 003/372] Only calculate debug data when we are debugging. NOTE: Last commit message was wrong. --- apache2/re.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index cd630a70..18d55dfb 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1210,7 +1210,9 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, var->value_len)); } - time_before_regex = apr_time_now(); /* IMP1 time_before_regex? */ + if (msr->txcfg->debuglog_level >= 4) { + time_before_regex = apr_time_now(); /* IMP1 time_before_regex? */ + } rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg); if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", From eaa8e444dd1657eb8e6011cf03b0a1322111852d Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 16 May 2007 20:09:28 +0000 Subject: [PATCH 004/372] Fixed decoding full-width unicode in t:urlDecodeUni for ASCII range 0xFF01-0xFF5E. Probably need more work/testing. (See #122) --- CHANGES | 3 +++ apache2/msc_util.c | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 58b2de35..a545c6f5 100644 --- a/CHANGES +++ b/CHANGES @@ -2,10 +2,13 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Fixed decoding full-width unicode in t:urlDecodeUni. + * Only calculate debugging vars when we are debugging (more to come). * Removed strnlen() calls for non-GNU platforms. + 11 May 2007 - 2.2.0-dev1 ------------------------- diff --git a/apache2/msc_util.c b/apache2/msc_util.c index f9ebb611..acc33e94 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -561,8 +561,18 @@ int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) if ( (VALID_HEX(input[i + 2]))&&(VALID_HEX(input[i + 3])) &&(VALID_HEX(input[i + 4]))&&(VALID_HEX(input[i + 5])) ) { - /* We make use of the lower byte here, ignoring the higher byte. */ - *d++ = x2c(&input[i + 4]); + /* We first make use of the lower byte here, ignoring the higher byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) + { + *d += 0x20; + } + + d++; count++; i += 6; } else { From a627e96c75f9737bdc540698a8755772894e5386 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 17 May 2007 12:02:59 +0000 Subject: [PATCH 005/372] Lessen "capture" debug log messages. --- CHANGES | 2 ++ apache2/re_operators.c | 12 +++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index a545c6f5..3e96ad9e 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Remove excessive debug log entries about "capture" action. + * Fixed decoding full-width unicode in t:urlDecodeUni. * Only calculate debugging vars when we are debugging (more to come). diff --git a/apache2/re_operators.c b/apache2/re_operators.c index db0071f8..7cc2c27a 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -109,18 +109,12 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* Are we supposed to capture subexpressions? */ capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - /* Warn when the regex captures but "capture" is not set */ - if (msr->txcfg->debuglog_level >= 4) { + /* Show when the regex captures but "capture" is not set */ + if (msr->txcfg->debuglog_level >= 6) { int capcount = 0; rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount); if ((capture == 0) && (capcount > 0)) { - msr_log(msr, 4, "Ignoring regex captures since \"capture\" action is not enabled."); - } - if ((capture == 1) && (capcount == 0)) { - msr_log(msr, 5, "The \"capture\" action is enabled, but the regex does not have explicit captures."); - } - if ((capture == 0) && (capcount > 0)) { - msr_log(msr, 6, "The \"capture\" action is not enabled, but the regex contains captures."); + msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled."); } } From 3fbf2b93c9a7521a3dedf0f3e9b7769cf99fb5a1 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 21 May 2007 17:25:47 +0000 Subject: [PATCH 006/372] Modify docs for t:urlDecodeUni. (See #122) --- doc/modsecurity2-apache-reference.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 02b51d94..4196c7af 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3035,8 +3035,10 @@ SecRule XML:/xq:employees/employee/name/text() In addition to decoding %xx like urlDecode, urlDecodeUni also decodes %uXXXX encoding (only the - lower byte will be used, the higher byte will be discarded). + moreinfo="none"> %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.
@@ -4495,4 +4497,4 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd
- + \ No newline at end of file From a1a0c24b882c1a6ec18d5098b1baa087cb079339 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 23 May 2007 16:04:25 +0000 Subject: [PATCH 007/372] Do not compile on Solaris with visibility attributes. --- CHANGES | 3 +++ apache2/modsecurity.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3e96ad9e..8dd56635 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Add Solaris to the list of platforms not supporting the hidden + visibility attribute. + * Remove excessive debug log entries about "capture" action. * Fixed decoding full-width unicode in t:urlDecodeUni. diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 4b6abe59..d133535e 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -26,7 +26,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(NETWARE)) +#if !(defined(WIN32) || defined(NETWARE) || defined(SOLARIS2)) #define DSOLOCAL __attribute__((visibility("hidden"))) #else #define DSOLOCAL From 220abd3444cb9d675334d9769c3d05279ccf1404 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 24 May 2007 21:56:34 +0000 Subject: [PATCH 008/372] Quiet uninitialized warning. --- apache2/re.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index 18d55dfb..575a607b 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1175,7 +1175,7 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, msre_actionset *acting_actionset, apr_pool_t *mptmp) { - apr_time_t time_before_regex; + apr_time_t time_before_regex = 0; char *my_error_msg = NULL; const char *full_varname = NULL; int rc; From e11ff85421708585f388d815b92b6179e59cc4cd Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 25 May 2007 20:56:03 +0000 Subject: [PATCH 009/372] Fixed log_escape_raw when length was <= 0 --- apache2/msc_util.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apache2/msc_util.c b/apache2/msc_util.c index acc33e94..d15a5f73 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -448,9 +448,13 @@ char *log_escape_header_name(apr_pool_t *mp, const char *text) { } char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { - unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); + unsigned char *ret; unsigned long int i, j; + if (text_length <= 0) return NULL; + + ret = apr_palloc(mp, text_length * 4 + 1); + for (i = 0, j = 0; i < text_length; i++, j += 4) { apr_snprintf((char *)ret+j, 5, "\\x%02x", text[i]); } From 61238ca22fd7c84f620a5b8d25e988ec5e08a79d Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 25 May 2007 21:01:11 +0000 Subject: [PATCH 010/372] Argh! That last one was not meant to be checked in - reverting 281. --- apache2/msc_util.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apache2/msc_util.c b/apache2/msc_util.c index d15a5f73..acc33e94 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -448,13 +448,9 @@ char *log_escape_header_name(apr_pool_t *mp, const char *text) { } char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) { - unsigned char *ret; + unsigned char *ret = apr_palloc(mp, text_length * 4 + 1); unsigned long int i, j; - if (text_length <= 0) return NULL; - - ret = apr_palloc(mp, text_length * 4 + 1); - for (i = 0, j = 0; i < text_length; i++, j += 4) { apr_snprintf((char *)ret+j, 5, "\\x%02x", text[i]); } From 6cc0173cfac71b5e0e593c88f7cc142b5408acfc Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 25 May 2007 21:14:59 +0000 Subject: [PATCH 011/372] Add caching for transformations. See #14. --- apache2/Makefile | 1 + apache2/modsecurity.c | 3 + apache2/modsecurity.h | 17 ++++++ apache2/re.c | 124 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/apache2/Makefile b/apache2/Makefile index 049c9f23..c2095a54 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -29,6 +29,7 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 DEFS = -DWITH_LIBXML2 +#DEFS = -DWITH_LIBXML2 -DCACHE_DEBUG #LIBS = -Lmy/lib/dir -lmylib CFLAGS = -O2 -g -Wuninitialized -Wall -Wmissing-prototypes -Wshadow -Wunused-variable -Wunused-value -Wchar-subscripts -Wsign-compare diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index e890bc1b..2fd65234 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -297,6 +297,9 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { msr->collections_dirty = apr_table_make(msr->mp, 8); if (msr->collections_dirty == NULL) return -1; + msr->tcache = apr_hash_make(msr->mp); + if (msr->tcache == NULL) return -1; + return 1; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index d133535e..e9e74682 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -25,6 +25,7 @@ typedef struct msc_engine msc_engine; typedef struct msc_data_chunk msc_data_chunk; typedef struct msc_arg msc_arg; typedef struct msc_string msc_string; +typedef struct msc_cache_rec msc_cache_rec; #if !(defined(WIN32) || defined(NETWARE) || defined(SOLARIS2)) #define DSOLOCAL __attribute__((visibility("hidden"))) @@ -45,6 +46,7 @@ typedef struct msc_string msc_string; #include "ap_config.h" #include "apr_md5.h" #include "apr_strings.h" +#include "apr_hash.h" #include "httpd.h" #include "http_config.h" #include "http_log.h" @@ -59,6 +61,8 @@ typedef struct msc_string msc_string; #define PHASE_RESPONSE_HEADERS 3 #define PHASE_RESPONSE_BODY 4 #define PHASE_LOGGING 5 +#define PHASE_FIRST PHASE_REQUEST_HEADERS +#define PHASE_LAST PHASE_LOGGING #define NOT_SET -1 #define NOT_SET_P (void *)-1 @@ -319,6 +323,9 @@ struct modsec_rec { apr_off_t content_prepend_len; const char *content_append; apr_off_t content_append_len; + + /* data cache */ + apr_hash_t *tcache; }; struct directory_config { @@ -495,4 +502,14 @@ void DSOLOCAL msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, c apr_status_t DSOLOCAL modsecurity_request_body_clear(modsec_rec *msr); +/* Data Cache */ + +struct msc_cache_rec { + int hits; + int changed; + const char *key; + const char *val; + apr_size_t val_len; +}; + #endif diff --git a/apache2/re.c b/apache2/re.c index 575a607b..01994c48 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -294,7 +294,7 @@ int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, /* we are at the beginning of the name */ name = p; - while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; // ENH replace with isvarnamechar() + while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; /* ENH replace with isvarnamechar() */ /* get the name */ name = apr_pstrmemdup(mp, name, p - name); @@ -1353,11 +1353,27 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { { const apr_array_header_t *tarr; const apr_table_entry_t *telts; + msc_cache_rec **carr = NULL; + msc_cache_rec *crec = NULL; + char *tfnsvar = NULL; + char *tfnskey = NULL; + int tfnscount = 0; + int usecache = 0; apr_table_t *normtab; int k; msre_action *action; msre_tfn_metadata *metadata; + /* Is this var cacheable? */ + if (var->metadata->is_cacheable == VAR_CACHE) { + usecache = 1; + tfnsvar = apr_psprintf(msr->mp, "%lx;%s", (unsigned long)var, var->name); + tfnskey = tfnsvar; + } + else { + msr_log(msr, 9, "CACHE: %s transformations are not cacheable", var->name); + } + normtab = apr_table_make(mptmp, 10); if (normtab == NULL) return -1; tarr = apr_table_elts(rule->actionset->actions); @@ -1369,6 +1385,8 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (strcmp(telts[k].key, "t") == 0) { if (strcmp(action->param, "none") == 0) { apr_table_clear(normtab); + tfnskey = tfnsvar; + tfnscount = 0; continue; } @@ -1376,12 +1394,43 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { apr_table_unset(normtab, action->param); } else { apr_table_addn(normtab, action->param, (void *)action); + tfnskey = apr_psprintf(msr->mp, "%s,%s", tfnskey, action->param); + tfnscount++; } } } /* Perform transformations. */ + /* Try to fetch the full multi-transformation from cache */ + if (usecache && tfnscount > 1 && !multi_match) { + crec = NULL; + msr_log(msr, 9, "CACHE: Fetching %s (multi)", tfnskey); + carr = (msc_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); + if (carr != NULL) { + crec = carr[msr->phase]; + } + + /* Cache Miss - Reset the key to perform transformations */ + if (crec == NULL) { + tfnskey = tfnsvar; + } + /* Cache Hit - Use cache value and execute immediatly */ + else { + crec->hits++; + if (crec->changed) { + var->value = apr_pmemdup(msr->mp, crec->val, crec->val_len); + var->value_len = crec->val_len; + } + + msr_log(msr, 9, "T (%i) %s: \"%s\" [cached hits=%d]", crec->changed, (tfnskey + strlen(tfnsvar) + 1), log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); + + rc = execute_operator(var, rule, msr, acting_actionset, mptmp); + + return (rc < 0) ? : rc; + } + } + tarr = apr_table_elts(normtab); /* Make a copy of the variable value so that @@ -1394,6 +1443,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { /* Execute transformations in a loop. */ + tfnskey = tfnsvar; changed = 1; telts = (const apr_table_entry_t*)tarr->elts; for (k = 0; k < tarr->nelts; k++) { @@ -1431,6 +1481,31 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { action = (msre_action *)telts[k].val; metadata = (msre_tfn_metadata *)action->param_data; + /* Try to use the cache */ + if (usecache) { + /* Generate the cache key */ + tfnskey = apr_psprintf(msr->mp, "%s,%s", tfnskey, action->param); + + /* Try to fetch this transformation from cache */ + msr_log(msr, 9, "CACHE: Fetching %s", tfnskey); + crec = NULL; + carr = (msc_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); + if (carr != NULL) { + crec = carr[msr->phase]; + } + 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; + } + + msr_log(msr, 9, "T (%i) %s: \"%s\" [cached hits=%i]", 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, &rval, &rval_length); if (rc < 0) { @@ -1442,6 +1517,26 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { 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 */ + if (carr == NULL) { + carr = (msc_cache_rec **)apr_pcalloc(msr->mp, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); + if (carr == NULL) return -1; + memset(carr, 0, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); + apr_hash_set(msr->tcache, tfnskey, APR_HASH_KEY_STRING, carr); + } + crec = carr[msr->phase] = (msc_cache_rec *)apr_pcalloc(msr->mp, sizeof(msc_cache_rec)); + if (crec == NULL) return -1; + + crec->hits = 0; + crec->changed = changed; + crec->key = tfnskey; + crec->val = changed ? apr_pmemdup(msr->mp, rval, rval_length) : NULL; + crec->val_len = changed ? rval_length : -1; + msr_log(msr, 9, "CACHE: Caching %s=\"%.*s\"", tfnskey, crec->val_len, crec->val); + } + if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "T (%i) %s: \"%s\"", rc, metadata->name, log_escape_nq_ex(mptmp, var->value, var->value_len)); @@ -1477,6 +1572,33 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { } + msr_log(msr, 9, "CACHE: size=%u", apr_hash_count(msr->tcache)); + #ifdef CACHE_DEBUG + if (msr->txcfg->debuglog_level >= 9) { + apr_hash_index_t *hi; + void *dummy; + msc_cache_rec **rec; + int hn = 0; + int ri; + for (hi = apr_hash_first(msr->mp, msr->tcache); hi; hi = apr_hash_next(hi)) { + hn++; + apr_hash_this(hi, NULL, NULL, &dummy); + rec = (msc_cache_rec **)dummy; + if (rec == NULL) continue; + + for (ri = PHASE_FIRST; ri <= PHASE_LAST; ri++) { + if (rec[ri] == NULL) continue; + if (rec[ri]->changed) { + msr_log(msr, 9, "CACHE: %5d) phase=%d hits=%d %s=\"%s\"", hn, msr->phase, rec[ri]->hits, rec[ri]->key, log_escape_nq_ex(mptmp, rec[ri]->val, rec[ri]->val_len)); + } + else { + msr_log(msr, 9, "CACHE: %5d) phase=%d hits=%d %s=", hn, msr->phase, rec[ri]->hits, rec[ri]->key); + } + } + } + } + #endif + return (match_count ? RULE_MATCH : RULE_NO_MATCH); } From c594c205c313f0fb920bccd892ce3006ae35502d Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 29 May 2007 14:58:05 +0000 Subject: [PATCH 012/372] Fix new string operators to all resolve macros. Rename startsWith operator in code to match docs. See #54. --- apache2/re_operators.c | 55 +++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 7cc2c27a..8d9b1ff9 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -182,20 +182,29 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* contains */ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = (const char *)rule->op_param; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; unsigned int i, i_max; + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + if (error_msg == NULL) return -1; *error_msg = NULL; - if (match == NULL) { + if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; } + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -208,8 +217,6 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * target_length = var->value_len; } - match_length = strlen(match); - /* These are impossible to match */ if ((match_length == 0) || (match_length > target_length)) { /* No match. */ @@ -291,22 +298,31 @@ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var return 0; } -/* startsWith */ +/* beginsWith */ -static int msre_op_startsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = (const char *)rule->op_param; +static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + if (error_msg == NULL) return -1; *error_msg = NULL; - if (match == NULL) { + if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; } + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -319,8 +335,6 @@ static int msre_op_startsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var target_length = var->value_len; } - match_length = strlen(match); - /* These are impossible to match */ if ((match_length == 0) || (match_length > target_length)) { /* No match. */ @@ -342,19 +356,28 @@ static int msre_op_startsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var /* endsWith */ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = (const char *)rule->op_param; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + if (error_msg == NULL) return -1; *error_msg = NULL; - if (match == NULL) { + if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; } + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -367,8 +390,6 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * target_length = var->value_len; } - match_length = strlen(match); - /* These are impossible to match */ if ((match_length == 0) || (match_length > target_length)) { /* No match. */ @@ -1208,11 +1229,11 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_streq_execute ); - /* startsWith */ + /* beginsWith */ msre_engine_op_register(engine, - "startsWith", + "beginsWith", NULL, /* ENH init function to flag var substitution */ - msre_op_startsWith_execute + msre_op_beginsWith_execute ); /* endsWith */ From af6160b9c41f76e9fe8e9d145e299673a18d4433 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 30 May 2007 14:14:00 +0000 Subject: [PATCH 013/372] Fixed problem with subrequests not being intercepted. See #124. --- CHANGES | 2 ++ apache2/mod_security2.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 8dd56635..6828e833 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Fixed problem with subrequests not being intercepted (only logged). + * Add Solaris to the list of platforms not supporting the hidden visibility attribute. diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 34b25771..95bd7a1b 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -562,6 +562,10 @@ static int hook_request_late(request_rec *r) { /* Has this phase been completed already? */ if (msr->phase_request_body_complete) { + if (msr->was_intercepted) { + msr_log(msr, 4, "Phase REQUEST_BODY subrequest already intercepted with code %u.", msr->intercept_actionset->intercept_status); + return msr->intercept_actionset->intercept_status; + } if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Phase REQUEST_BODY already complete, skipping."); } From db04c64420e968abf586008849390bdb3c112836 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 30 May 2007 16:10:17 +0000 Subject: [PATCH 014/372] Cleanup --- apache2/modsecurity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index e9e74682..69e52c4c 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -325,7 +325,7 @@ struct modsec_rec { apr_off_t content_append_len; /* data cache */ - apr_hash_t *tcache; + apr_hash_t *tcache; }; struct directory_config { From f53c4241fdbb4d6a7b090f619084a4bffedbaa86 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 30 May 2007 16:13:22 +0000 Subject: [PATCH 015/372] Add entry to CHANGES. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 6828e833..5d05d88d 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Cache transformations per-request/phase so they are not repeated. + * Fixed problem with subrequests not being intercepted (only logged). * Add Solaris to the list of platforms not supporting the hidden From e887faac2b6573e5bd969eb5d09305bd82e0f8fa Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 30 May 2007 22:02:35 +0000 Subject: [PATCH 016/372] Add @pm/@pmfile operators (parallel patch). See #16. --- CHANGES | 4 + apache2/Makefile | 1 + apache2/Makefile.win | 2 +- apache2/acmp.c | 711 ++++++++++++++++++++++ apache2/acmp.h | 115 ++++ apache2/modules.mk | 4 +- apache2/re_operators.c | 145 +++++ apache2/utf8tables.h | 810 ++++++++++++++++++++++++++ doc/modsecurity2-apache-reference.xml | 34 ++ 9 files changed, 1823 insertions(+), 3 deletions(-) create mode 100644 apache2/acmp.c create mode 100644 apache2/acmp.h create mode 100644 apache2/utf8tables.h diff --git a/CHANGES b/CHANGES index 5d05d88d..6386ead5 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * Added new parallel matching operators, @pm and @pmfile. These use + an alternate set based matching engine to perform faster keyword + type matches. + * Cache transformations per-request/phase so they are not repeated. * Fixed problem with subrequests not being intercepted (only logged). diff --git a/apache2/Makefile b/apache2/Makefile index c2095a54..9cdbbeca 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -29,6 +29,7 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 DEFS = -DWITH_LIBXML2 +#DEFS = -DWITH_LIBXML2 -DDEBUG_CONF #DEFS = -DWITH_LIBXML2 -DCACHE_DEBUG #LIBS = -Lmy/lib/dir -lmylib diff --git a/apache2/Makefile.win b/apache2/Makefile.win index 071741c2..96284cd7 100644 --- a/apache2/Makefile.win +++ b/apache2/Makefile.win @@ -24,7 +24,7 @@ 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 + msc_geo.obj acmp.obj all: $(DLL) diff --git a/apache2/acmp.c b/apache2/acmp.c new file mode 100644 index 00000000..d5a6dba8 --- /dev/null +++ b/apache2/acmp.c @@ -0,0 +1,711 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 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. + * + */ +#include "acmp.h" +#include "utf8tables.h" +#include +#include +#include + + +/* + ******************************************************************************* + ******************************************************************************* + * Data structures for acmp parser + */ + + /** + * One node in trie + */ +typedef struct acmp_node_t acmp_node_t; +typedef struct acmp_btree_node_t acmp_btree_node_t; +struct acmp_node_t { + acmp_utf8_char_t letter; + int is_last; + acmp_callback_t callback; + void *callback_data; + int depth; + + acmp_node_t *child; + acmp_node_t *sibling; + acmp_node_t *fail; + acmp_node_t *parent; + acmp_node_t *o_match; + + acmp_btree_node_t *btree; + + apr_size_t hit_count; + + char *text; + char *pattern; +}; + +struct acmp_btree_node_t { + acmp_utf8_char_t letter; + acmp_btree_node_t *left; + acmp_btree_node_t *right; + acmp_node_t *node; +}; + +/** + * Data related to parser, not to individual nodes + */ +struct ACMP { + int is_utf8; + int is_case_sensitive; + apr_pool_t *parent_pool; + apr_pool_t *pool; + + int dict_count; + apr_size_t longest_entry; + + acmp_node_t *root_node; + + const char *data_start; + const char *data_end; + const char *data_pos; + apr_size_t data_len; + + apr_size_t *bp_buffer; + apr_size_t bp_buff_len; + + acmp_node_t *active_node; + char u8_buff[6]; + apr_size_t u8buff_len; + apr_size_t hit_count; + int is_failtree_done; + int is_active; + apr_size_t byte_pos; + apr_size_t char_pos; +}; + +/* + ******************************************************************************* + ******************************************************************************* + * Functions for UTF-8 support + */ + +/** + * Returns length of utf-8 sequence based on its first byte + */ +static int utf8_seq_len(const char *first_byte) { + return utf8_seq_lengths[(unsigned int)(unsigned char)first_byte[0]]; +} + +/** + * Returns length of utf8-encoded text + */ +static size_t utf8_strlen(const char *str) { + int len = 0; + const char *c = str; + while (*c != 0) { + c += utf8_seq_len(c); + len++; + } + return len; +} + +/** + * Returns ucs code for given utf-8 sequence + */ +static acmp_utf8_char_t utf8_decodechar(const char *str) { + int len = utf8_seq_len(str); + acmp_utf8_char_t ch = 0; + switch (len) { + case 6: ch += (unsigned char)*str++; ch <<= 6; + case 5: ch += (unsigned char)*str++; ch <<= 6; + case 4: ch += (unsigned char)*str++; ch <<= 6; + case 3: ch += (unsigned char)*str++; ch <<= 6; + case 2: ch += (unsigned char)*str++; ch <<= 6; + case 1: ch += (unsigned char)*str++; + } + ch -= utf8_offsets[len - 1]; + return ch; +} + +/** + * Returns lowercase for given unicode character. Searches through + * utf8_lcase_map table, if it doesn't find the code assumes + * it doesn't have a lowercase variant and returns code itself. + */ +static long utf8_lcase(acmp_utf8_char_t ucs_code) { + long mid, left, right; + left = 1; + right = UTF8_LCASEMAP_LEN * 2 + 1; + + while (left <= right) { + mid = (left + right) >> 1; + mid -= (mid % 2); mid++; + if (ucs_code > utf8_lcase_map[mid]) + left = mid + 2; + else if (ucs_code < utf8_lcase_map[mid]) + right = mid - 2; + else if (ucs_code == utf8_lcase_map[mid]) + return utf8_lcase_map[mid - 1]; + } + return ucs_code; +} + +/* + ******************************************************************************* + ******************************************************************************* + * Code for local / static utility functions + */ + +/** + * Returns length of given string for parser's encoding + */ +static size_t acmp_strlen(ACMP *parser, const char *str) { + return (parser->is_utf8 == 0) ? strlen(str) : utf8_strlen(str); +} + +/** + * Turns string to array of ucs values, depending on parser's encoding + * str - string to convert, doesn't have to be NULL-terminated + * ucs_chars - where to write ucs values + * len - length of input string + */ +static void acmp_strtoucs(ACMP *parser, const char *str, acmp_utf8_char_t *ucs_chars, int len) { + int i; + const char *c = str; + + if (parser->is_utf8 == 0) { + for (i = 0; i < len; i++) { + *ucs_chars++ = *c++; + } + } else { + for (i = 0; i < len; i++) { + *ucs_chars++ = utf8_decodechar(c); + c += utf8_seq_len(c); + } + } +} + +/** + * Returns node with given letter, or null if not found + */ +static acmp_node_t *acmp_child_for_code(acmp_node_t *parent_node, acmp_utf8_char_t ucs_code) { + acmp_node_t *node = parent_node->child; + if (node == NULL) return NULL; + for (;;) { + if (node->letter == ucs_code) return node; + node = node->sibling; + if (node == NULL) return NULL; + } +} + +/** + * Adds node to parent node, if it is not already there + */ +static void acmp_add_node_to_parent(acmp_node_t *parent, acmp_node_t *child) { + child->parent = parent; + if (parent->child == NULL) { + parent->child = child; + return; + } + acmp_node_t *node = parent->child; + for (;;) { + if (node == child) return; + if (node->sibling == NULL) { + node->sibling = child; + return; + } + node = node->sibling; + } +} + +/** + * Copies values from one node to another, without child/sibling/fail pointers + * and without state variables. + */ +static void acmp_clone_node_no_state(acmp_node_t *from, acmp_node_t *to) { + memcpy(to, from, sizeof(acmp_node_t)); + to->child = NULL; + to->sibling = NULL; + to->fail = NULL; + to->hit_count = 0; +} + +/** + * Copies sibling nodes and child node for from given "from" node to "to" node. + * Both nodes must already exist. + */ +static void acmp_copy_nodes_recursive(acmp_node_t *from, acmp_node_t *to, apr_pool_t *pool) { + acmp_node_t *old_node = from->child, *new_node, *nn2; + if (old_node == NULL) return; + nn2 = apr_pcalloc(pool, sizeof(acmp_node_t)); + acmp_clone_node_no_state(old_node, nn2); + nn2->parent = to; + to->child = nn2; + acmp_copy_nodes_recursive(from->child, to->child, pool); + + for (;;) { + old_node = old_node->sibling; + if (old_node == NULL) break; + new_node = apr_pcalloc(pool, sizeof(acmp_node_t)); + acmp_clone_node_no_state(old_node, new_node); + new_node->parent = to; + nn2->sibling = new_node; + nn2 = new_node; + acmp_copy_nodes_recursive(old_node, new_node, pool); + } +} + +static inline acmp_node_t *acmp_btree_find(acmp_node_t *node, acmp_utf8_char_t letter) { + acmp_btree_node_t *bnode = node->btree; + for (;;) { + if (bnode == NULL) return NULL; + if (bnode->letter == letter) return bnode->node; + if (bnode->letter > letter) { + bnode = bnode->left; + } else { + bnode = bnode->right; + } + } +} + +/** + * + */ +static inline acmp_node_t *acmp_goto(acmp_node_t *node, acmp_utf8_char_t letter) { + //return acmp_child_for_code(node, letter); + return acmp_btree_find(node, letter); +} + +/** + * Connects each node with its first fail node that is end of a phrase. + */ +static void acmp_connect_other_matches(ACMP *parser, acmp_node_t *node) { + acmp_node_t *child, *om; + + for (child = node->child; child != NULL; child = child->sibling) { + if (child->fail == NULL) continue; + for (om = child->fail; om != parser->root_node; om = om->fail) { + if (om->is_last) { + child->o_match = om; + break; + } + } + } + + /* Go recursively through children of this node that have a child node */ + for(child = node->child; child != NULL; child = child->sibling) { + if (child->child != NULL) acmp_connect_other_matches(parser, child); + } +} + +/** + * Adds leaves to binary tree, working from sorted array of keyword tree nodes + */ +static void acmp_add_btree_leaves(acmp_btree_node_t *node, acmp_node_t *nodes[], + int pos, int lb, int rb, apr_pool_t *pool) { + + int left = 0, right = 0; + if ((pos - lb) > 1) { + left = lb + (pos - lb) / 2; + node->left = apr_pcalloc(pool, sizeof(acmp_btree_node_t)); + node->left->node = nodes[left]; + node->left->letter = nodes[left]->letter; + /* printf("%c ->left %c \n", node->node->letter, node->left->node->letter); */ + } + if ((rb - pos) > 1) { + right = pos + (rb - pos) / 2; + node->right = apr_pcalloc(pool, sizeof(acmp_btree_node_t)); + node->right->node = nodes[right]; + node->right->letter = nodes[right]->letter; + /* printf("%c ->right %c \n", node->node->letter, node->right->node->letter); */ + } + if (node->right != NULL) { + acmp_add_btree_leaves(node->right, nodes, right, pos, rb, pool); + } + if (node->left != NULL) { + acmp_add_btree_leaves(node->left, nodes, left, lb, pos, pool); + } +} + +/** + * Builds balanced binary tree from children nodes of given node. + */ +static void acmp_build_binary_tree(ACMP *parser, acmp_node_t *node) { + apr_size_t count, i, j; + acmp_node_t *child = node->child; + + for (count = 0; child != NULL; child = child->sibling) count++; + acmp_node_t *nodes[count]; + child = node->child; + for (i = 0; i < count; i++) { + nodes[i] = child; + child = child->sibling; + }; + /* We have array with all children of the node and number of those children + */ + for (i = 0; i < count - 1; i++) + for (j = i + 1; j < count; j++) { + if (nodes[i]->letter < nodes[j]->letter) continue; + acmp_node_t *tmp = nodes[i]; + nodes[i] = nodes[j]; + nodes[j] = tmp; + } + node->btree = apr_pcalloc(parser->pool, sizeof(acmp_btree_node_t)); + apr_size_t pos = count / 2; + node->btree->node = nodes[pos]; + node->btree->letter = nodes[pos]->letter; + acmp_add_btree_leaves(node->btree, nodes, pos, -1, count, parser->pool); + for (i = 0; i < count; i++) { + if (nodes[i]->child != NULL) acmp_build_binary_tree(parser, nodes[i]); + } +} + +/** + * Constructs fail paths on keyword trie + */ +static apr_status_t acmp_connect_fail_branches(ACMP *parser) { + /* Already connected ? */ + if (parser->is_failtree_done != 0) return APR_SUCCESS; + acmp_node_t *child, *node, *goto_node; + apr_array_header_t *arr, *arr2, *tmp; + + parser->root_node->text = ""; + arr = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *)); + arr2 = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *)); + + parser->root_node->fail = parser->root_node; + + /* All first-level children will fail back to root node */ + for (child = parser->root_node->child; child != NULL; child = child->sibling) { + child->fail = parser->root_node; + *(acmp_node_t **)apr_array_push(arr) = child; + /* printf("fail direction: *%s* => *%s*\n", child->text, child->fail->text); */ + } + + for (;;) { + while (apr_is_empty_array(arr) == 0) { + node = *(acmp_node_t **)apr_array_pop(arr); + node->fail = parser->root_node; + if (node->parent != parser->root_node) { + goto_node = acmp_child_for_code(node->parent->fail, node->letter); + node->fail = (goto_node != NULL) ? goto_node : parser->root_node; + } + /* printf("fail direction: *%s* => *%s*\n", node->text, node->fail->text); */ + child = node->child; + while (child != NULL) { + *(acmp_node_t **)apr_array_push(arr2) = child; + child = child->sibling; + } + } + if (apr_is_empty_array(arr2) != 0) break; + + tmp = arr; + arr = arr2; + arr2 = tmp; + } + acmp_connect_other_matches(parser, parser->root_node); + if (parser->root_node->child != NULL) acmp_build_binary_tree(parser, parser->root_node); + parser->is_failtree_done = 1; + return APR_SUCCESS; +} + +/** + * Clears hit count of each node, called from acmp_reset() + */ +static void acmp_clear_hit_count_recursive(acmp_node_t *node) { + for (; node != NULL; node = node->sibling) { + node->hit_count = 0; + if (node->child != NULL) acmp_clear_hit_count_recursive(node->child); + } +} + +/** + * Called when a match is found + */ +static void acmp_found(ACMP *parser, acmp_node_t *node) { + if (node->callback) { + node->callback(parser, node->callback_data, + parser->bp_buffer[(parser->char_pos - node->depth - 1) % parser->bp_buff_len], + parser->char_pos - node->depth - 1); + } + /* printf("found: %s at position %d\n", node->pattern, parser->char_pos - node->depth - 1); */ + node->hit_count++; + parser->hit_count++; +} + +/* + ******************************************************************************* + ******************************************************************************* + * Code for functions from header file + */ + + +/** + * flags - OR-ed values of ACMP_FLAG constants + * pool - apr_pool to use as parent pool, can be set to NULL + */ +ACMP *acmp_create(int flags, apr_pool_t *pool) { + apr_status_t rc; + apr_pool_t *p; + rc = apr_pool_create(&p, pool); + if (rc != APR_SUCCESS) return NULL; + + ACMP *parser = apr_pcalloc(p, sizeof(ACMP)); + parser->pool = p; + parser->parent_pool = pool; + parser->is_utf8 = (flags & ACMP_FLAG_UTF8) == 0 ? 0 : 1; + parser->is_case_sensitive = (flags & ACMP_FLAG_CASE_SENSITIVE) == 0 ? 0 : 1; + parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t)); + return parser; +} + +/** + * Destroys previously created parser + */ +void acmp_destroy(ACMP *parser) { + /* + * All data is kept in parser's pool (including parser struct itself), so + * destroying the pool will destroy everything + */ + apr_pool_destroy(parser->pool); +} + +/** + * Creates parser with same options and same patterns + * parser - ACMP parser to duplicate + * pool - parent pool to use, if left as NULL original parser's parent pool is used + */ +ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool) { + apr_status_t rc; + apr_pool_t *p; + + if (pool == NULL) pool = parser->parent_pool; + rc = apr_pool_create(&p, pool); + if (rc != APR_SUCCESS) return NULL; + + ACMP *new_parser = apr_pcalloc(p, sizeof(ACMP)); + new_parser->pool = p; + new_parser->parent_pool = pool; + new_parser->is_utf8 = parser->is_utf8; + new_parser->is_case_sensitive = parser->is_case_sensitive; + new_parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t)); + new_parser->dict_count = parser->dict_count; + new_parser->longest_entry = parser->longest_entry; + acmp_copy_nodes_recursive(parser->root_node, new_parser->root_node, new_parser->pool); + acmp_prepare(new_parser); + return new_parser; +} + +/** + * Creates fail tree and initializes buffer + */ +apr_status_t acmp_prepare(ACMP *parser) { + if (parser->bp_buff_len < parser->longest_entry) { + parser->bp_buff_len = parser->longest_entry * 2; + parser->bp_buffer = apr_pcalloc(parser->pool, sizeof(apr_size_t) * parser->bp_buff_len); + } + apr_status_t st = acmp_connect_fail_branches(parser); + parser->active_node = parser->root_node; + if (st != APR_SUCCESS) return st; + parser->is_active = 1; + return APR_SUCCESS; +} + +/** + * Adds pattern to parser + * parser - ACMP parser + * pattern - string with pattern to match + * callback - Optional, pointer to an acmp_callback_t function + * data - pointer to data that will be passed to callback function, only used if callback + * is supplied + * len - Length of pattern in characters, if zero string length is used. + */ +apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, + acmp_callback_t callback, void *data, apr_size_t len) +{ + if (parser->is_active != 0) return APR_EGENERAL; + size_t length = (len == 0) ? acmp_strlen(parser, pattern) : len; + size_t i, j; + acmp_utf8_char_t ucs_chars[length]; + + acmp_node_t *parent = parser->root_node, *child; + acmp_strtoucs(parser, pattern, ucs_chars, length); + + for (i = 0; i < length; i++) { + acmp_utf8_char_t letter = ucs_chars[i]; + if (parser->is_case_sensitive == 0) { + letter = utf8_lcase(letter); + } + child = acmp_child_for_code(parent, letter); + if (child == NULL) { + child = apr_pcalloc(parser->pool, sizeof(acmp_node_t)); + child->pattern = ""; + child->letter = letter; + child->depth = i; + child->text = apr_pcalloc(parser->pool, strlen(pattern) + 2); + for (j = 0; j <= i; j++) child->text[j] = pattern[j]; + } + if (i == length - 1) { + if (child->is_last == 0) { + parser->dict_count++; + child->is_last = 1; + child->pattern = apr_pcalloc(parser->pool, strlen(pattern) + 2); + strcpy(child->pattern, pattern); + } + child->callback = callback; + child->callback_data = data; + } + acmp_add_node_to_parent(parent, child); + parent = child; + } + if (length > parser->longest_entry) parser->longest_entry = length; + parser->is_failtree_done = 0; + + return APR_SUCCESS; +} + +/** + * Called to process incoming data stream + * data - ptr to incoming data + * len - size of data in bytes + */ +apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len) { + if (parser->is_failtree_done == 0) acmp_prepare(parser); + acmp_node_t *node = parser->active_node, *go_to; + apr_size_t seq_length; + const char *end = (data + len); + + while (data < end) { + parser->bp_buffer[parser->char_pos % parser->bp_buff_len] = parser->byte_pos; + acmp_utf8_char_t letter; + if (parser->is_utf8) { + if (parser->u8buff_len > 0) { + /* Resuming partial utf-8 sequence */ + seq_length = utf8_seq_len(parser->u8_buff); + for (;;) { + parser->u8_buff[parser->u8buff_len++] = *data++; + if (parser->u8buff_len == seq_length) { + parser->u8buff_len = 0; + letter = utf8_decodechar(parser->u8_buff); + parser->byte_pos += seq_length; + parser->char_pos++; + break; + } + } + } else { + /* not resuming partial sequence, reading from the stream */ + seq_length = utf8_seq_len(data); + if ((data + seq_length) > end) { + while (data < end) parser->u8_buff[parser->u8buff_len++] = *data++; + return APR_SUCCESS; + } else { + letter = utf8_decodechar(data); + data += seq_length; + parser->byte_pos += seq_length; + parser->char_pos++; + } + } + } else { + letter = *data++; + parser->byte_pos++; + parser->char_pos++; + } + if (parser->is_case_sensitive == 0) letter = utf8_lcase(letter); + + go_to = NULL; + while (go_to == NULL) { + acmp_node_t *n2 = acmp_goto(node, letter); + go_to = acmp_child_for_code(node, letter); + if (n2 != go_to) { + n2 = acmp_goto(node, letter); + }; + if (go_to != NULL) { + if (go_to->is_last) { + acmp_found(parser, go_to); + } + } + if (node == parser->root_node) break; + if (go_to == NULL) node = node->fail; + } + if (go_to != NULL) node = go_to; + + /* We need to collect other nodes that are last letters of phrase. These + * will be fail node of current node if it has is_last flag set, and + * fail node of that node, recursively down to root node. + */ + go_to = node; + if (go_to != parser->root_node) { + for (go_to = go_to->o_match; go_to != NULL; go_to = go_to->o_match) { + acmp_found(parser, go_to); + } + } + } + parser->active_node = node; + return parser->hit_count > 0 ? 1 : 0; +} + +/** + * Resets the state of parser so you can start using it with new set of data. + * + * No need to clear buffer since it will be re-initialized at first run of + * acmp_process + */ +void acmp_reset(ACMP *parser) { + parser->is_active = 0; + parser->byte_pos = 0; + parser->char_pos = 0; + parser->hit_count = 0; + parser->u8buff_len = 0; + acmp_clear_hit_count_recursive(parser->root_node); +} + +/** + * Creates an ACMPT struct that will use parser's tree, without duplicating its data + */ +ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool) { + apr_pool_t *p = (pool != NULL) ? pool : parser->pool; + ACMPT *dup = apr_pcalloc(p, sizeof(ACMPT)); + dup->parser = parser; + return dup; +} + +/** + * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree + */ +apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len) { + if (acmpt->parser->is_failtree_done == 0) { + acmp_prepare(acmpt->parser); + }; + ACMP *parser = acmpt->parser; + if (acmpt->ptr == NULL) acmpt->ptr = parser->root_node; + acmp_node_t *node = acmpt->ptr, *go_to; + const char *end = (data + len); + + while (data < end) { + acmp_utf8_char_t letter = (unsigned char)*data++; + go_to = NULL; + while (go_to == NULL) { + go_to = acmp_goto(node, letter); + if (go_to != NULL) { + if (go_to->is_last) { + *match = go_to->text; + return 1; + } + } + if (node == parser->root_node) break; + if (go_to == NULL) node = node->fail; + } + if (go_to != NULL) node = go_to; + + /* If node has o_match, then we found a pattern */ + if (node->o_match != NULL) { + *match = node->text; + return 1; + } + } + acmpt->ptr = node; + return 0; +} diff --git a/apache2/acmp.h b/apache2/acmp.h new file mode 100644 index 00000000..40865ac7 --- /dev/null +++ b/apache2/acmp.h @@ -0,0 +1,115 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 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. + * + */ +#ifndef ACMP_H_ +#define ACMP_H_ + +#include +#include + +#define ACMP_FLAG_BYTE 0 +#define ACMP_FLAG_UTF8 0x100 +#define ACMP_FLAG_CASE_SENSITIVE 1 +#define ACMP_FLAG_CASE_INSENSITIVE 0 + +/** + * Opaque struct with parser data + */ +typedef struct ACMP ACMP; + +/** + * Used to separate state from the trie for acmp_process_quick function + */ +typedef struct { + ACMP *parser; + void *ptr; +} ACMPT; + +/** + * Callback function. Arguments are: + * ACMP * - acmp parser that initiated callback + * void * - custom data you supplied when adding callback + * apr_size_t - position in bytes where pattern was found + * apr_size_t - position in chars where pattern was found, for multibyte strings + */ +typedef void (*acmp_callback_t)(ACMP *, void *, apr_size_t, apr_size_t); + +/** + * flags - OR-ed values of ACMP_FLAG constants + * pool - apr_pool to use as parent pool, can be set to NULL + */ +ACMP *acmp_create(int flags, apr_pool_t *pool); + +/** + * Destroys previously created parser + */ +void acmp_destroy(ACMP *parser); + +/** + * Creates parser with same options and same patterns + * parser - ACMP parser to duplicate + * pool - parent pool to use, if left as NULL original parser's parent pool is used + */ +ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool); + +/** + * Adds pattern to parser. Cannot be done after starting the search. + * parser - ACMP parser + * pattern - string with pattern to match + * callback - Optional, pointer to an acmp_callback_t function + * data - pointer to data that will be passed to callback function, only used if callback + * is supplied + * len - Length of pattern in characters, if zero string length is used. + */ +apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, + acmp_callback_t callback, void *data, apr_size_t len); + +/** + * Called to process incoming data stream. You must call acmp_done after sending + * last data packet + * + * data - ptr to incoming data + * len - size of data in bytes + */ +apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len); + +/** + * Returns number of matches on all patterns combined + */ +apr_size_t acmp_match_count_total(ACMP *parser); + +/** + * Returns number of matches for given pattern + */ +apr_size_t acmp_match_count(ACMP *parser, const char *pattern); + +/** + * Resets the state of parser so you can start using it with new set of data, + * or add new patterns. + */ +void acmp_reset(ACMP *parser); + +/** + * Creates an ACMPT struct that will use parser's tree, without duplicating its data + */ +ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool); + +/** + * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree + */ +apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len); + +/** + * Prepares parser for searching + */ +apr_status_t acmp_prepare(ACMP *parser); + + +#endif /*ACMP_H_*/ diff --git a/apache2/modules.mk b/apache2/modules.mk index 2f9cfbb4..bce0eb7d 100644 --- a/apache2/modules.mk +++ b/apache2/modules.mk @@ -2,11 +2,11 @@ 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 + persist_dbm msc_reqbody pdf_protect msc_geo acmp 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 + msc_geo.h acmp.h utf8tables.h ${MOD_SECURITY2:=.slo}: ${H} ${MOD_SECURITY2:=.lo}: ${H} diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 8d9b1ff9..727be9c2 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -13,7 +13,9 @@ #include "re.h" #include "msc_pcre.h" #include "msc_geo.h" +#include "apr_lib.h" #include "apr_strmatch.h" +#include "acmp.h" /** * @@ -179,6 +181,135 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c return 0; } +/* pm */ + +static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'."); + return 0; /* ERROR */ + } + + ACMP *p = acmp_create(0, rule->ruleset->mp); + if (p == NULL) return 0; + + const char *s = rule->op_param; + const char *e = rule->op_param + strlen(rule->op_param); + + for (;;) { + while((isspace(*s) != 0) && (*s != 0)) s++; + if (*s == 0) break; + e = s; + while((isspace(*e) == 0) && (*e != 0)) e++; + acmp_add_pattern(p, s, NULL, NULL, e - s); + s = e; + } + acmp_prepare(p); + rule->op_param_data = p; + return 1; +} + +/* pmfile */ + +static int msre_op_pmfile_param_init(msre_rule *rule, char **error_msg) { + char errstr[1024]; + char buf[HUGE_STRING_LEN + 1]; + char *ptr = NULL; + apr_status_t rc; + apr_file_t *fd; + + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'."); + return 0; /* ERROR */ + } + + ACMP *p = acmp_create(0, rule->ruleset->mp); + if (p == NULL) return 0; + + char *fn = apr_pstrdup(rule->ruleset->mp, rule->op_param); + char *next = fn + strlen(rule->op_param); + + /* Loop through filenames */ + for (;;) { + int line = 0; + + /* Trim whitespace */ + while((isspace(*fn) != 0) && (*fn != 0)) fn++; + if (*fn == '\0') break; + next = fn; + while((isspace(*next) == 0) && (*next != '\0')) next++; + while((isspace(*next) != 0) && (*next != '\0')) *next++ = '\0'; + + /* Open file and read */ + rc = apr_file_open(&fd, fn, APR_READ | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open pmfile \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + return 0; + } + + #ifdef DEBUG_CONF + fprintf(stderr, "Loading pmfile: \"%s\"\n", fn); + #endif + + /* Read one pattern per line skipping empty/commented */ + for(;;) { + line++; + rc = apr_file_gets(buf, HUGE_STRING_LEN, fd); + if (rc == APR_EOF) break; + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); + return 0; + } + + /* Remove newline */ + ptr = buf; + while(*ptr != '\0') ptr++; + if ((ptr > buf) && (*(ptr - 1) == '\n')) *(ptr - 1) = '\0'; + + /* Ignore empty lines and comments */ + ptr = buf; + while((*ptr != '\0') && apr_isspace(*ptr)) ptr++; + if ((*ptr == '\0') || (*ptr == '#')) continue; + + #ifdef DEBUG_CONF + fprintf(stderr, "Adding pmfile pattern: \"%s\"\n", buf); + #endif + + acmp_add_pattern(p, buf, NULL, NULL, strlen(buf)); + } + fn = next; + } + if (fd != NULL) apr_file_close(fd); + acmp_prepare(p); + rule->op_param_data = p; + return 1; +} + +static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + const char *match = NULL; + apr_status_t rc = 0; + + /* Nothing to read */ + if ((var->value == NULL) || (var->value_len == 0)) return 0; + + ACMPT pt = {(ACMP *)rule->op_param_data, NULL}; + + rc = acmp_process_quick(&pt, &match, var->value, var->value_len); + if (rc) { + char *pattern_escaped = log_escape(msr->mp, match ? match : ""); + + /* This message will be logged. */ + if (strlen(pattern_escaped) > 252) { + *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.", + pattern_escaped, var->name); + } else { + *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", + pattern_escaped, var->name); + } + return 1; + } + return rc; +} + /* contains */ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { @@ -1215,6 +1346,20 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_rx_execute ); + /* pm */ + msre_engine_op_register(engine, + "pm", + msre_op_pm_param_init, + msre_op_pm_execute + ); + + /* pmfile */ + msre_engine_op_register(engine, + "pmfile", + msre_op_pmfile_param_init, + msre_op_pm_execute + ); + /* contains */ msre_engine_op_register(engine, "contains", diff --git a/apache2/utf8tables.h b/apache2/utf8tables.h new file mode 100644 index 00000000..f12dc19b --- /dev/null +++ b/apache2/utf8tables.h @@ -0,0 +1,810 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 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. + * + */ +#ifndef UTF8TABLES_H_ +#define UTF8TABLES_H_ + +/** + * This include file is used by acmp.c only, it's not included anywhere else + */ + +typedef long acmp_utf8_char_t; + +static const char utf8_seq_lengths[256] = { + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,6,6, +}; + +static const acmp_utf8_char_t utf8_offsets[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +/** + * How many element pairs are there in utf8_lcase_map + */ +#define UTF8_LCASEMAP_LEN 759 + +/** + * Table mapping is from PHP's mbstring extension, maps uppercase + */ +static const acmp_utf8_char_t utf8_lcase_map[UTF8_LCASEMAP_LEN * 2] = { + 0x00000061, 0x00000041, + 0x00000062, 0x00000042, + 0x00000063, 0x00000043, + 0x00000064, 0x00000044, + 0x00000065, 0x00000045, + 0x00000066, 0x00000046, + 0x00000067, 0x00000047, + 0x00000068, 0x00000048, + 0x00000069, 0x00000049, + 0x0000006a, 0x0000004a, + 0x0000006b, 0x0000004b, + 0x0000006c, 0x0000004c, + 0x0000006d, 0x0000004d, + 0x0000006e, 0x0000004e, + 0x0000006f, 0x0000004f, + 0x00000070, 0x00000050, + 0x00000071, 0x00000051, + 0x00000072, 0x00000052, + 0x00000073, 0x00000053, + 0x00000074, 0x00000054, + 0x00000075, 0x00000055, + 0x00000076, 0x00000056, + 0x00000077, 0x00000057, + 0x00000078, 0x00000058, + 0x00000079, 0x00000059, + 0x0000007a, 0x0000005a, + 0x000000b5, 0x0000039c, + 0x000000e0, 0x000000c0, + 0x000000e1, 0x000000c1, + 0x000000e2, 0x000000c2, + 0x000000e3, 0x000000c3, + 0x000000e4, 0x000000c4, + 0x000000e5, 0x000000c5, + 0x000000e6, 0x000000c6, + 0x000000e7, 0x000000c7, + 0x000000e8, 0x000000c8, + 0x000000e9, 0x000000c9, + 0x000000ea, 0x000000ca, + 0x000000eb, 0x000000cb, + 0x000000ec, 0x000000cc, + 0x000000ed, 0x000000cd, + 0x000000ee, 0x000000ce, + 0x000000ef, 0x000000cf, + 0x000000f0, 0x000000d0, + 0x000000f1, 0x000000d1, + 0x000000f2, 0x000000d2, + 0x000000f3, 0x000000d3, + 0x000000f4, 0x000000d4, + 0x000000f5, 0x000000d5, + 0x000000f6, 0x000000d6, + 0x000000f8, 0x000000d8, + 0x000000f9, 0x000000d9, + 0x000000fa, 0x000000da, + 0x000000fb, 0x000000db, + 0x000000fc, 0x000000dc, + 0x000000fd, 0x000000dd, + 0x000000fe, 0x000000de, + 0x000000ff, 0x00000178, + 0x00000101, 0x00000100, + 0x00000103, 0x00000102, + 0x00000105, 0x00000104, + 0x00000107, 0x00000106, + 0x00000109, 0x00000108, + 0x0000010b, 0x0000010a, + 0x0000010d, 0x0000010c, + 0x0000010f, 0x0000010e, + 0x00000111, 0x00000110, + 0x00000113, 0x00000112, + 0x00000115, 0x00000114, + 0x00000117, 0x00000116, + 0x00000119, 0x00000118, + 0x0000011b, 0x0000011a, + 0x0000011d, 0x0000011c, + 0x0000011f, 0x0000011e, + 0x00000121, 0x00000120, + 0x00000123, 0x00000122, + 0x00000125, 0x00000124, + 0x00000127, 0x00000126, + 0x00000129, 0x00000128, + 0x0000012b, 0x0000012a, + 0x0000012d, 0x0000012c, + 0x0000012f, 0x0000012e, + 0x00000131, 0x00000049, + 0x00000133, 0x00000132, + 0x00000135, 0x00000134, + 0x00000137, 0x00000136, + 0x0000013a, 0x00000139, + 0x0000013c, 0x0000013b, + 0x0000013e, 0x0000013d, + 0x00000140, 0x0000013f, + 0x00000142, 0x00000141, + 0x00000144, 0x00000143, + 0x00000146, 0x00000145, + 0x00000148, 0x00000147, + 0x0000014b, 0x0000014a, + 0x0000014d, 0x0000014c, + 0x0000014f, 0x0000014e, + 0x00000151, 0x00000150, + 0x00000153, 0x00000152, + 0x00000155, 0x00000154, + 0x00000157, 0x00000156, + 0x00000159, 0x00000158, + 0x0000015b, 0x0000015a, + 0x0000015d, 0x0000015c, + 0x0000015f, 0x0000015e, + 0x00000161, 0x00000160, + 0x00000163, 0x00000162, + 0x00000165, 0x00000164, + 0x00000167, 0x00000166, + 0x00000169, 0x00000168, + 0x0000016b, 0x0000016a, + 0x0000016d, 0x0000016c, + 0x0000016f, 0x0000016e, + 0x00000171, 0x00000170, + 0x00000173, 0x00000172, + 0x00000175, 0x00000174, + 0x00000177, 0x00000176, + 0x0000017a, 0x00000179, + 0x0000017c, 0x0000017b, + 0x0000017e, 0x0000017d, + 0x0000017f, 0x00000053, + 0x00000183, 0x00000182, + 0x00000185, 0x00000184, + 0x00000188, 0x00000187, + 0x0000018c, 0x0000018b, + 0x00000192, 0x00000191, + 0x00000195, 0x000001f6, + 0x00000199, 0x00000198, + 0x0000019e, 0x00000220, + 0x000001a1, 0x000001a0, + 0x000001a3, 0x000001a2, + 0x000001a5, 0x000001a4, + 0x000001a8, 0x000001a7, + 0x000001ad, 0x000001ac, + 0x000001b0, 0x000001af, + 0x000001b4, 0x000001b3, + 0x000001b6, 0x000001b5, + 0x000001b9, 0x000001b8, + 0x000001bd, 0x000001bc, + 0x000001bf, 0x000001f7, + 0x000001c6, 0x000001c4, + 0x000001c9, 0x000001c7, + 0x000001cc, 0x000001ca, + 0x000001ce, 0x000001cd, + 0x000001d0, 0x000001cf, + 0x000001d2, 0x000001d1, + 0x000001d4, 0x000001d3, + 0x000001d6, 0x000001d5, + 0x000001d8, 0x000001d7, + 0x000001da, 0x000001d9, + 0x000001dc, 0x000001db, + 0x000001dd, 0x0000018e, + 0x000001df, 0x000001de, + 0x000001e1, 0x000001e0, + 0x000001e3, 0x000001e2, + 0x000001e5, 0x000001e4, + 0x000001e7, 0x000001e6, + 0x000001e9, 0x000001e8, + 0x000001eb, 0x000001ea, + 0x000001ed, 0x000001ec, + 0x000001ef, 0x000001ee, + 0x000001f3, 0x000001f1, + 0x000001f5, 0x000001f4, + 0x000001f9, 0x000001f8, + 0x000001fb, 0x000001fa, + 0x000001fd, 0x000001fc, + 0x000001ff, 0x000001fe, + 0x00000201, 0x00000200, + 0x00000203, 0x00000202, + 0x00000205, 0x00000204, + 0x00000207, 0x00000206, + 0x00000209, 0x00000208, + 0x0000020b, 0x0000020a, + 0x0000020d, 0x0000020c, + 0x0000020f, 0x0000020e, + 0x00000211, 0x00000210, + 0x00000213, 0x00000212, + 0x00000215, 0x00000214, + 0x00000217, 0x00000216, + 0x00000219, 0x00000218, + 0x0000021b, 0x0000021a, + 0x0000021d, 0x0000021c, + 0x0000021f, 0x0000021e, + 0x00000223, 0x00000222, + 0x00000225, 0x00000224, + 0x00000227, 0x00000226, + 0x00000229, 0x00000228, + 0x0000022b, 0x0000022a, + 0x0000022d, 0x0000022c, + 0x0000022f, 0x0000022e, + 0x00000231, 0x00000230, + 0x00000233, 0x00000232, + 0x00000253, 0x00000181, + 0x00000254, 0x00000186, + 0x00000256, 0x00000189, + 0x00000257, 0x0000018a, + 0x00000259, 0x0000018f, + 0x0000025b, 0x00000190, + 0x00000260, 0x00000193, + 0x00000263, 0x00000194, + 0x00000268, 0x00000197, + 0x00000269, 0x00000196, + 0x0000026f, 0x0000019c, + 0x00000272, 0x0000019d, + 0x00000275, 0x0000019f, + 0x00000280, 0x000001a6, + 0x00000283, 0x000001a9, + 0x00000288, 0x000001ae, + 0x0000028a, 0x000001b1, + 0x0000028b, 0x000001b2, + 0x00000292, 0x000001b7, + 0x00000345, 0x00000399, + 0x000003ac, 0x00000386, + 0x000003ad, 0x00000388, + 0x000003ae, 0x00000389, + 0x000003af, 0x0000038a, + 0x000003b1, 0x00000391, + 0x000003b2, 0x00000392, + 0x000003b3, 0x00000393, + 0x000003b4, 0x00000394, + 0x000003b5, 0x00000395, + 0x000003b6, 0x00000396, + 0x000003b7, 0x00000397, + 0x000003b8, 0x00000398, + 0x000003b9, 0x00000399, + 0x000003ba, 0x0000039a, + 0x000003bb, 0x0000039b, + 0x000003bc, 0x0000039c, + 0x000003bd, 0x0000039d, + 0x000003be, 0x0000039e, + 0x000003bf, 0x0000039f, + 0x000003c0, 0x000003a0, + 0x000003c1, 0x000003a1, + 0x000003c2, 0x000003a3, + 0x000003c3, 0x000003a3, + 0x000003c4, 0x000003a4, + 0x000003c5, 0x000003a5, + 0x000003c6, 0x000003a6, + 0x000003c7, 0x000003a7, + 0x000003c8, 0x000003a8, + 0x000003c9, 0x000003a9, + 0x000003ca, 0x000003aa, + 0x000003cb, 0x000003ab, + 0x000003cc, 0x0000038c, + 0x000003cd, 0x0000038e, + 0x000003ce, 0x0000038f, + 0x000003d0, 0x00000392, + 0x000003d1, 0x00000398, + 0x000003d5, 0x000003a6, + 0x000003d6, 0x000003a0, + 0x000003d9, 0x000003d8, + 0x000003db, 0x000003da, + 0x000003dd, 0x000003dc, + 0x000003df, 0x000003de, + 0x000003e1, 0x000003e0, + 0x000003e3, 0x000003e2, + 0x000003e5, 0x000003e4, + 0x000003e7, 0x000003e6, + 0x000003e9, 0x000003e8, + 0x000003eb, 0x000003ea, + 0x000003ed, 0x000003ec, + 0x000003ef, 0x000003ee, + 0x000003f0, 0x0000039a, + 0x000003f1, 0x000003a1, + 0x000003f2, 0x000003a3, + 0x000003f5, 0x00000395, + 0x00000430, 0x00000410, + 0x00000431, 0x00000411, + 0x00000432, 0x00000412, + 0x00000433, 0x00000413, + 0x00000434, 0x00000414, + 0x00000435, 0x00000415, + 0x00000436, 0x00000416, + 0x00000437, 0x00000417, + 0x00000438, 0x00000418, + 0x00000439, 0x00000419, + 0x0000043a, 0x0000041a, + 0x0000043b, 0x0000041b, + 0x0000043c, 0x0000041c, + 0x0000043d, 0x0000041d, + 0x0000043e, 0x0000041e, + 0x0000043f, 0x0000041f, + 0x00000440, 0x00000420, + 0x00000441, 0x00000421, + 0x00000442, 0x00000422, + 0x00000443, 0x00000423, + 0x00000444, 0x00000424, + 0x00000445, 0x00000425, + 0x00000446, 0x00000426, + 0x00000447, 0x00000427, + 0x00000448, 0x00000428, + 0x00000449, 0x00000429, + 0x0000044a, 0x0000042a, + 0x0000044b, 0x0000042b, + 0x0000044c, 0x0000042c, + 0x0000044d, 0x0000042d, + 0x0000044e, 0x0000042e, + 0x0000044f, 0x0000042f, + 0x00000450, 0x00000400, + 0x00000451, 0x00000401, + 0x00000452, 0x00000402, + 0x00000453, 0x00000403, + 0x00000454, 0x00000404, + 0x00000455, 0x00000405, + 0x00000456, 0x00000406, + 0x00000457, 0x00000407, + 0x00000458, 0x00000408, + 0x00000459, 0x00000409, + 0x0000045a, 0x0000040a, + 0x0000045b, 0x0000040b, + 0x0000045c, 0x0000040c, + 0x0000045d, 0x0000040d, + 0x0000045e, 0x0000040e, + 0x0000045f, 0x0000040f, + 0x00000461, 0x00000460, + 0x00000463, 0x00000462, + 0x00000465, 0x00000464, + 0x00000467, 0x00000466, + 0x00000469, 0x00000468, + 0x0000046b, 0x0000046a, + 0x0000046d, 0x0000046c, + 0x0000046f, 0x0000046e, + 0x00000471, 0x00000470, + 0x00000473, 0x00000472, + 0x00000475, 0x00000474, + 0x00000477, 0x00000476, + 0x00000479, 0x00000478, + 0x0000047b, 0x0000047a, + 0x0000047d, 0x0000047c, + 0x0000047f, 0x0000047e, + 0x00000481, 0x00000480, + 0x0000048b, 0x0000048a, + 0x0000048d, 0x0000048c, + 0x0000048f, 0x0000048e, + 0x00000491, 0x00000490, + 0x00000493, 0x00000492, + 0x00000495, 0x00000494, + 0x00000497, 0x00000496, + 0x00000499, 0x00000498, + 0x0000049b, 0x0000049a, + 0x0000049d, 0x0000049c, + 0x0000049f, 0x0000049e, + 0x000004a1, 0x000004a0, + 0x000004a3, 0x000004a2, + 0x000004a5, 0x000004a4, + 0x000004a7, 0x000004a6, + 0x000004a9, 0x000004a8, + 0x000004ab, 0x000004aa, + 0x000004ad, 0x000004ac, + 0x000004af, 0x000004ae, + 0x000004b1, 0x000004b0, + 0x000004b3, 0x000004b2, + 0x000004b5, 0x000004b4, + 0x000004b7, 0x000004b6, + 0x000004b9, 0x000004b8, + 0x000004bb, 0x000004ba, + 0x000004bd, 0x000004bc, + 0x000004bf, 0x000004be, + 0x000004c2, 0x000004c1, + 0x000004c4, 0x000004c3, + 0x000004c6, 0x000004c5, + 0x000004c8, 0x000004c7, + 0x000004ca, 0x000004c9, + 0x000004cc, 0x000004cb, + 0x000004ce, 0x000004cd, + 0x000004d1, 0x000004d0, + 0x000004d3, 0x000004d2, + 0x000004d5, 0x000004d4, + 0x000004d7, 0x000004d6, + 0x000004d9, 0x000004d8, + 0x000004db, 0x000004da, + 0x000004dd, 0x000004dc, + 0x000004df, 0x000004de, + 0x000004e1, 0x000004e0, + 0x000004e3, 0x000004e2, + 0x000004e5, 0x000004e4, + 0x000004e7, 0x000004e6, + 0x000004e9, 0x000004e8, + 0x000004eb, 0x000004ea, + 0x000004ed, 0x000004ec, + 0x000004ef, 0x000004ee, + 0x000004f1, 0x000004f0, + 0x000004f3, 0x000004f2, + 0x000004f5, 0x000004f4, + 0x000004f9, 0x000004f8, + 0x00000501, 0x00000500, + 0x00000503, 0x00000502, + 0x00000505, 0x00000504, + 0x00000507, 0x00000506, + 0x00000509, 0x00000508, + 0x0000050b, 0x0000050a, + 0x0000050d, 0x0000050c, + 0x0000050f, 0x0000050e, + 0x00000561, 0x00000531, + 0x00000562, 0x00000532, + 0x00000563, 0x00000533, + 0x00000564, 0x00000534, + 0x00000565, 0x00000535, + 0x00000566, 0x00000536, + 0x00000567, 0x00000537, + 0x00000568, 0x00000538, + 0x00000569, 0x00000539, + 0x0000056a, 0x0000053a, + 0x0000056b, 0x0000053b, + 0x0000056c, 0x0000053c, + 0x0000056d, 0x0000053d, + 0x0000056e, 0x0000053e, + 0x0000056f, 0x0000053f, + 0x00000570, 0x00000540, + 0x00000571, 0x00000541, + 0x00000572, 0x00000542, + 0x00000573, 0x00000543, + 0x00000574, 0x00000544, + 0x00000575, 0x00000545, + 0x00000576, 0x00000546, + 0x00000577, 0x00000547, + 0x00000578, 0x00000548, + 0x00000579, 0x00000549, + 0x0000057a, 0x0000054a, + 0x0000057b, 0x0000054b, + 0x0000057c, 0x0000054c, + 0x0000057d, 0x0000054d, + 0x0000057e, 0x0000054e, + 0x0000057f, 0x0000054f, + 0x00000580, 0x00000550, + 0x00000581, 0x00000551, + 0x00000582, 0x00000552, + 0x00000583, 0x00000553, + 0x00000584, 0x00000554, + 0x00000585, 0x00000555, + 0x00000586, 0x00000556, + 0x00001e01, 0x00001e00, + 0x00001e03, 0x00001e02, + 0x00001e05, 0x00001e04, + 0x00001e07, 0x00001e06, + 0x00001e09, 0x00001e08, + 0x00001e0b, 0x00001e0a, + 0x00001e0d, 0x00001e0c, + 0x00001e0f, 0x00001e0e, + 0x00001e11, 0x00001e10, + 0x00001e13, 0x00001e12, + 0x00001e15, 0x00001e14, + 0x00001e17, 0x00001e16, + 0x00001e19, 0x00001e18, + 0x00001e1b, 0x00001e1a, + 0x00001e1d, 0x00001e1c, + 0x00001e1f, 0x00001e1e, + 0x00001e21, 0x00001e20, + 0x00001e23, 0x00001e22, + 0x00001e25, 0x00001e24, + 0x00001e27, 0x00001e26, + 0x00001e29, 0x00001e28, + 0x00001e2b, 0x00001e2a, + 0x00001e2d, 0x00001e2c, + 0x00001e2f, 0x00001e2e, + 0x00001e31, 0x00001e30, + 0x00001e33, 0x00001e32, + 0x00001e35, 0x00001e34, + 0x00001e37, 0x00001e36, + 0x00001e39, 0x00001e38, + 0x00001e3b, 0x00001e3a, + 0x00001e3d, 0x00001e3c, + 0x00001e3f, 0x00001e3e, + 0x00001e41, 0x00001e40, + 0x00001e43, 0x00001e42, + 0x00001e45, 0x00001e44, + 0x00001e47, 0x00001e46, + 0x00001e49, 0x00001e48, + 0x00001e4b, 0x00001e4a, + 0x00001e4d, 0x00001e4c, + 0x00001e4f, 0x00001e4e, + 0x00001e51, 0x00001e50, + 0x00001e53, 0x00001e52, + 0x00001e55, 0x00001e54, + 0x00001e57, 0x00001e56, + 0x00001e59, 0x00001e58, + 0x00001e5b, 0x00001e5a, + 0x00001e5d, 0x00001e5c, + 0x00001e5f, 0x00001e5e, + 0x00001e61, 0x00001e60, + 0x00001e63, 0x00001e62, + 0x00001e65, 0x00001e64, + 0x00001e67, 0x00001e66, + 0x00001e69, 0x00001e68, + 0x00001e6b, 0x00001e6a, + 0x00001e6d, 0x00001e6c, + 0x00001e6f, 0x00001e6e, + 0x00001e71, 0x00001e70, + 0x00001e73, 0x00001e72, + 0x00001e75, 0x00001e74, + 0x00001e77, 0x00001e76, + 0x00001e79, 0x00001e78, + 0x00001e7b, 0x00001e7a, + 0x00001e7d, 0x00001e7c, + 0x00001e7f, 0x00001e7e, + 0x00001e81, 0x00001e80, + 0x00001e83, 0x00001e82, + 0x00001e85, 0x00001e84, + 0x00001e87, 0x00001e86, + 0x00001e89, 0x00001e88, + 0x00001e8b, 0x00001e8a, + 0x00001e8d, 0x00001e8c, + 0x00001e8f, 0x00001e8e, + 0x00001e91, 0x00001e90, + 0x00001e93, 0x00001e92, + 0x00001e95, 0x00001e94, + 0x00001e9b, 0x00001e60, + 0x00001ea1, 0x00001ea0, + 0x00001ea3, 0x00001ea2, + 0x00001ea5, 0x00001ea4, + 0x00001ea7, 0x00001ea6, + 0x00001ea9, 0x00001ea8, + 0x00001eab, 0x00001eaa, + 0x00001ead, 0x00001eac, + 0x00001eaf, 0x00001eae, + 0x00001eb1, 0x00001eb0, + 0x00001eb3, 0x00001eb2, + 0x00001eb5, 0x00001eb4, + 0x00001eb7, 0x00001eb6, + 0x00001eb9, 0x00001eb8, + 0x00001ebb, 0x00001eba, + 0x00001ebd, 0x00001ebc, + 0x00001ebf, 0x00001ebe, + 0x00001ec1, 0x00001ec0, + 0x00001ec3, 0x00001ec2, + 0x00001ec5, 0x00001ec4, + 0x00001ec7, 0x00001ec6, + 0x00001ec9, 0x00001ec8, + 0x00001ecb, 0x00001eca, + 0x00001ecd, 0x00001ecc, + 0x00001ecf, 0x00001ece, + 0x00001ed1, 0x00001ed0, + 0x00001ed3, 0x00001ed2, + 0x00001ed5, 0x00001ed4, + 0x00001ed7, 0x00001ed6, + 0x00001ed9, 0x00001ed8, + 0x00001edb, 0x00001eda, + 0x00001edd, 0x00001edc, + 0x00001edf, 0x00001ede, + 0x00001ee1, 0x00001ee0, + 0x00001ee3, 0x00001ee2, + 0x00001ee5, 0x00001ee4, + 0x00001ee7, 0x00001ee6, + 0x00001ee9, 0x00001ee8, + 0x00001eeb, 0x00001eea, + 0x00001eed, 0x00001eec, + 0x00001eef, 0x00001eee, + 0x00001ef1, 0x00001ef0, + 0x00001ef3, 0x00001ef2, + 0x00001ef5, 0x00001ef4, + 0x00001ef7, 0x00001ef6, + 0x00001ef9, 0x00001ef8, + 0x00001f00, 0x00001f08, + 0x00001f01, 0x00001f09, + 0x00001f02, 0x00001f0a, + 0x00001f03, 0x00001f0b, + 0x00001f04, 0x00001f0c, + 0x00001f05, 0x00001f0d, + 0x00001f06, 0x00001f0e, + 0x00001f07, 0x00001f0f, + 0x00001f10, 0x00001f18, + 0x00001f11, 0x00001f19, + 0x00001f12, 0x00001f1a, + 0x00001f13, 0x00001f1b, + 0x00001f14, 0x00001f1c, + 0x00001f15, 0x00001f1d, + 0x00001f20, 0x00001f28, + 0x00001f21, 0x00001f29, + 0x00001f22, 0x00001f2a, + 0x00001f23, 0x00001f2b, + 0x00001f24, 0x00001f2c, + 0x00001f25, 0x00001f2d, + 0x00001f26, 0x00001f2e, + 0x00001f27, 0x00001f2f, + 0x00001f30, 0x00001f38, + 0x00001f31, 0x00001f39, + 0x00001f32, 0x00001f3a, + 0x00001f33, 0x00001f3b, + 0x00001f34, 0x00001f3c, + 0x00001f35, 0x00001f3d, + 0x00001f36, 0x00001f3e, + 0x00001f37, 0x00001f3f, + 0x00001f40, 0x00001f48, + 0x00001f41, 0x00001f49, + 0x00001f42, 0x00001f4a, + 0x00001f43, 0x00001f4b, + 0x00001f44, 0x00001f4c, + 0x00001f45, 0x00001f4d, + 0x00001f51, 0x00001f59, + 0x00001f53, 0x00001f5b, + 0x00001f55, 0x00001f5d, + 0x00001f57, 0x00001f5f, + 0x00001f60, 0x00001f68, + 0x00001f61, 0x00001f69, + 0x00001f62, 0x00001f6a, + 0x00001f63, 0x00001f6b, + 0x00001f64, 0x00001f6c, + 0x00001f65, 0x00001f6d, + 0x00001f66, 0x00001f6e, + 0x00001f67, 0x00001f6f, + 0x00001f70, 0x00001fba, + 0x00001f71, 0x00001fbb, + 0x00001f72, 0x00001fc8, + 0x00001f73, 0x00001fc9, + 0x00001f74, 0x00001fca, + 0x00001f75, 0x00001fcb, + 0x00001f76, 0x00001fda, + 0x00001f77, 0x00001fdb, + 0x00001f78, 0x00001ff8, + 0x00001f79, 0x00001ff9, + 0x00001f7a, 0x00001fea, + 0x00001f7b, 0x00001feb, + 0x00001f7c, 0x00001ffa, + 0x00001f7d, 0x00001ffb, + 0x00001f80, 0x00001f88, + 0x00001f81, 0x00001f89, + 0x00001f82, 0x00001f8a, + 0x00001f83, 0x00001f8b, + 0x00001f84, 0x00001f8c, + 0x00001f85, 0x00001f8d, + 0x00001f86, 0x00001f8e, + 0x00001f87, 0x00001f8f, + 0x00001f90, 0x00001f98, + 0x00001f91, 0x00001f99, + 0x00001f92, 0x00001f9a, + 0x00001f93, 0x00001f9b, + 0x00001f94, 0x00001f9c, + 0x00001f95, 0x00001f9d, + 0x00001f96, 0x00001f9e, + 0x00001f97, 0x00001f9f, + 0x00001fa0, 0x00001fa8, + 0x00001fa1, 0x00001fa9, + 0x00001fa2, 0x00001faa, + 0x00001fa3, 0x00001fab, + 0x00001fa4, 0x00001fac, + 0x00001fa5, 0x00001fad, + 0x00001fa6, 0x00001fae, + 0x00001fa7, 0x00001faf, + 0x00001fb0, 0x00001fb8, + 0x00001fb1, 0x00001fb9, + 0x00001fb3, 0x00001fbc, + 0x00001fbe, 0x00000399, + 0x00001fc3, 0x00001fcc, + 0x00001fd0, 0x00001fd8, + 0x00001fd1, 0x00001fd9, + 0x00001fe0, 0x00001fe8, + 0x00001fe1, 0x00001fe9, + 0x00001fe5, 0x00001fec, + 0x00001ff3, 0x00001ffc, + 0x00002170, 0x00002160, + 0x00002171, 0x00002161, + 0x00002172, 0x00002162, + 0x00002173, 0x00002163, + 0x00002174, 0x00002164, + 0x00002175, 0x00002165, + 0x00002176, 0x00002166, + 0x00002177, 0x00002167, + 0x00002178, 0x00002168, + 0x00002179, 0x00002169, + 0x0000217a, 0x0000216a, + 0x0000217b, 0x0000216b, + 0x0000217c, 0x0000216c, + 0x0000217d, 0x0000216d, + 0x0000217e, 0x0000216e, + 0x0000217f, 0x0000216f, + 0x000024d0, 0x000024b6, + 0x000024d1, 0x000024b7, + 0x000024d2, 0x000024b8, + 0x000024d3, 0x000024b9, + 0x000024d4, 0x000024ba, + 0x000024d5, 0x000024bb, + 0x000024d6, 0x000024bc, + 0x000024d7, 0x000024bd, + 0x000024d8, 0x000024be, + 0x000024d9, 0x000024bf, + 0x000024da, 0x000024c0, + 0x000024db, 0x000024c1, + 0x000024dc, 0x000024c2, + 0x000024dd, 0x000024c3, + 0x000024de, 0x000024c4, + 0x000024df, 0x000024c5, + 0x000024e0, 0x000024c6, + 0x000024e1, 0x000024c7, + 0x000024e2, 0x000024c8, + 0x000024e3, 0x000024c9, + 0x000024e4, 0x000024ca, + 0x000024e5, 0x000024cb, + 0x000024e6, 0x000024cc, + 0x000024e7, 0x000024cd, + 0x000024e8, 0x000024ce, + 0x000024e9, 0x000024cf, + 0x0000ff41, 0x0000ff21, + 0x0000ff42, 0x0000ff22, + 0x0000ff43, 0x0000ff23, + 0x0000ff44, 0x0000ff24, + 0x0000ff45, 0x0000ff25, + 0x0000ff46, 0x0000ff26, + 0x0000ff47, 0x0000ff27, + 0x0000ff48, 0x0000ff28, + 0x0000ff49, 0x0000ff29, + 0x0000ff4a, 0x0000ff2a, + 0x0000ff4b, 0x0000ff2b, + 0x0000ff4c, 0x0000ff2c, + 0x0000ff4d, 0x0000ff2d, + 0x0000ff4e, 0x0000ff2e, + 0x0000ff4f, 0x0000ff2f, + 0x0000ff50, 0x0000ff30, + 0x0000ff51, 0x0000ff31, + 0x0000ff52, 0x0000ff32, + 0x0000ff53, 0x0000ff33, + 0x0000ff54, 0x0000ff34, + 0x0000ff55, 0x0000ff35, + 0x0000ff56, 0x0000ff36, + 0x0000ff57, 0x0000ff37, + 0x0000ff58, 0x0000ff38, + 0x0000ff59, 0x0000ff39, + 0x0000ff5a, 0x0000ff3a, + 0x00010428, 0x00010400, + 0x00010429, 0x00010401, + 0x0001042a, 0x00010402, + 0x0001042b, 0x00010403, + 0x0001042c, 0x00010404, + 0x0001042d, 0x00010405, + 0x0001042e, 0x00010406, + 0x0001042f, 0x00010407, + 0x00010430, 0x00010408, + 0x00010431, 0x00010409, + 0x00010432, 0x0001040a, + 0x00010433, 0x0001040b, + 0x00010434, 0x0001040c, + 0x00010435, 0x0001040d, + 0x00010436, 0x0001040e, + 0x00010437, 0x0001040f, + 0x00010438, 0x00010410, + 0x00010439, 0x00010411, + 0x0001043a, 0x00010412, + 0x0001043b, 0x00010413, + 0x0001043c, 0x00010414, + 0x0001043d, 0x00010415, + 0x0001043e, 0x00010416, + 0x0001043f, 0x00010417, + 0x00010440, 0x00010418, + 0x00010441, 0x00010419, + 0x00010442, 0x0001041a, + 0x00010443, 0x0001041b, + 0x00010444, 0x0001041c, + 0x00010445, 0x0001041d, + 0x00010446, 0x0001041e, + 0x00010447, 0x0001041f, + 0x00010448, 0x00010420, + 0x00010449, 0x00010421, + 0x0001044a, 0x00010422, + 0x0001044b, 0x00010423, + 0x0001044c, 0x00010424, + 0x0001044d, 0x00010425, +}; + +#endif /*UTF8TABLES_H_*/ diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 4196c7af..c6221372 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4287,6 +4287,40 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" role="bold">@lt 15" +
+ <literal>pm</literal> + + Description: Parallel Match + operator. This operator uses a set based matching engine for faster + matches of keyword lists. + + 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. +
+ +
+ <literal>pmfile</literal> + + Description: Parallel Match + operator. This operator uses a set based matching engine for faster + matches of keyword lists. It is the same as @pm + except that it takes a list of files arguments. The contents of the + files should be one pattern per line. + + Example: + + SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 /path/to/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. +
+
<literal>rbl</literal> From 84c0ca303ee1e9063053f71af2bc23a5fd9f8a65 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 31 May 2007 15:42:42 +0000 Subject: [PATCH 017/372] Fixed patch for subrequests to be more complete. See #124. --- CHANGES | 4 +++ apache2/mod_security2.c | 58 +++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/CHANGES b/CHANGES index 6386ead5..2f560266 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- + * TODO: Add file/line to audit log + + * TODO: Detect static files (trac #15) + * Added new parallel matching operators, @pm and @pmfile. These use an alternate set based matching engine to perform faster keyword type matches. diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 95bd7a1b..47b267de 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -55,6 +55,7 @@ int perform_interception(modsec_rec *msr) { msre_actionset *actionset = NULL; const char *message = NULL; const char *phase_text = ""; + const char *subreq_text = (msr->r->main == NULL) ? "" : "Subrequest. "; int status = DECLINED; int log_level = 1; @@ -95,14 +96,14 @@ int perform_interception(modsec_rec *msr) { case ACTION_DENY : if (actionset->intercept_status != 0) { status = actionset->intercept_status; - message = apr_psprintf(msr->mp, "Access denied with code %i%s.", status, - phase_text); + message = apr_psprintf(msr->mp, "%sAccess denied with code %i%s.", + subreq_text, status, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Internal Error: Invalid status code requested %i).", phase_text, - actionset->intercept_status); + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + "(Internal Error: Invalid status code requested %i).", + subreq_text, phase_text, actionset->intercept_status); } break; @@ -111,23 +112,25 @@ int perform_interception(modsec_rec *msr) { if (ap_find_linked_module("mod_proxy.c") == NULL) { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Configuration Error: Proxy action to %s requested but mod_proxy not found).", - phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); + subreq_text, phase_text, + log_escape_nq(msr->mp, actionset->intercept_uri)); } else { msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri); msr->r->proxyreq = PROXYREQ_REVERSE; msr->r->handler = "proxy-server"; status = OK; - message = apr_psprintf(msr->mp, "Access denied using proxy to %s%s.", - phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); + message = apr_psprintf(msr->mp, "%sAccess denied using proxy to %s%s.", + subreq_text, phase_text, + log_escape_nq(msr->mp, actionset->intercept_uri)); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Configuration Error: Proxy action requested but it does not work in output phases).", - phase_text); + subreq_text, phase_text); } break; @@ -144,29 +147,30 @@ int perform_interception(modsec_rec *msr) { if (csd) { if (apr_socket_close(csd) == APR_SUCCESS) { status = HTTP_FORBIDDEN; - message = apr_psprintf(msr->mp, "Access denied with connection close%s.", - phase_text); + message = apr_psprintf(msr->mp, "%sAccess denied with connection close%s.", + subreq_text, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop requested but failed to close the " - " socket).", phase_text); + " socket).", + subreq_text, phase_text); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop requested but socket not found.", - phase_text); + subreq_text, phase_text); } } #else log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop not implemented on this platform).", - phase_text); + subreq_text, phase_text); #endif break; @@ -179,23 +183,25 @@ int perform_interception(modsec_rec *msr) { } else { status = HTTP_MOVED_TEMPORARILY; } - message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " - "status %i%s.", log_escape_nq(msr->mp, actionset->intercept_uri), status, + message = apr_psprintf(msr->mp, "%sAccess denied with redirection to %s using " + "status %i%s.", + subreq_text, + log_escape_nq(msr->mp, actionset->intercept_uri), status, phase_text); break; case ACTION_ALLOW : status = DECLINED; - message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text); + message = apr_psprintf(msr->mp, "%sAccess allowed%s.", subreq_text, phase_text); msr->was_intercepted = 0; break; default : log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Internal Error: invalid interception action %i).", - phase_text, actionset->intercept_action); + subreq_text, phase_text, actionset->intercept_action); break; } @@ -563,8 +569,8 @@ static int hook_request_late(request_rec *r) { /* Has this phase been completed already? */ if (msr->phase_request_body_complete) { if (msr->was_intercepted) { - msr_log(msr, 4, "Phase REQUEST_BODY subrequest already intercepted with code %u.", msr->intercept_actionset->intercept_status); - return msr->intercept_actionset->intercept_status; + msr_log(msr, 4, "Phase REQUEST_BODY request already intercepted. Intercepting additional request."); + return perform_interception(msr); } if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Phase REQUEST_BODY already complete, skipping."); From 86f648d267ff325218b702fa47f8be308ba410ad Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 1 Jun 2007 13:04:13 +0000 Subject: [PATCH 018/372] Remove extraneous debug log message. --- apache2/re.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index 01994c48..2cae6d96 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1282,9 +1282,6 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { /* Use a fresh memory sub-pool for processing each rule */ if (msr->msc_rule_mptmp == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Creating new rule processing memory pool"); - } if (apr_pool_create(&msr->msc_rule_mptmp, msr->mp) != APR_SUCCESS) { return -1; } From f1607d007bc9a258ee4d2f689c1293be9b7c6d7d Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 1 Jun 2007 15:21:04 +0000 Subject: [PATCH 019/372] Cleanup message output. See #16. --- apache2/re_operators.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 727be9c2..13806d63 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -295,15 +295,15 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c rc = acmp_process_quick(&pt, &match, var->value, var->value_len); if (rc) { - char *pattern_escaped = log_escape(msr->mp, match ? match : ""); + char *match_escaped = log_escape(msr->mp, match ? match : ""); /* This message will be logged. */ - if (strlen(pattern_escaped) > 252) { - *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.", - pattern_escaped, var->name); + if (strlen(match_escaped) > 252) { + *error_msg = apr_psprintf(msr->mp, "Matched substring \"%.252s ...\" at %s.", + match_escaped, var->name); } else { - *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", - pattern_escaped, var->name); + *error_msg = apr_psprintf(msr->mp, "Matched substring \"%s\" at %s.", + match_escaped, var->name); } return 1; } From e5c00d156acd34c0a16bfb5e44c6a70dbeeb7ae3 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 1 Jun 2007 15:32:08 +0000 Subject: [PATCH 020/372] Added rule file/line to audit log messages. See #49. --- CHANGES | 4 ++-- apache2/re.c | 28 +++++++++++++++++++++------- apache2/re.h | 1 + 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 2f560266..a0efa01a 100644 --- a/CHANGES +++ b/CHANGES @@ -2,10 +2,10 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- - * TODO: Add file/line to audit log - * TODO: Detect static files (trac #15) + * Added rule file/line to audit log messages. + * Added new parallel matching operators, @pm and @pmfile. These use an alternate set based matching engine to perform faster keyword type matches. diff --git a/apache2/re.c b/apache2/re.c index 2cae6d96..debd6c7a 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -421,6 +421,7 @@ msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, actionset->msg = NOT_SET_P; actionset->phase = NOT_SET; actionset->severity = -1; + actionset->rule = NOT_SET_P; /* Flow */ actionset->is_chained = NOT_SET; @@ -495,6 +496,7 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent if (child->msg != NOT_SET_P) merged->msg = child->msg; if (child->severity != NOT_SET) merged->severity = child->severity; if (child->phase != NOT_SET) merged->phase = child->phase; + if (child->rule != NOT_SET_P) merged->rule = child->rule; /* Flow */ merged->is_chained = child->is_chained; @@ -550,6 +552,7 @@ static void msre_actionset_set_defaults(msre_actionset *actionset) { if (actionset->msg == NOT_SET_P) actionset->msg = NULL; if (actionset->phase == NOT_SET) actionset->phase = 2; if (actionset->severity == -1); /* leave at -1 */ + if (actionset->rule == NOT_SET_P) actionset->rule = NULL; /* Flow */ if (actionset->is_chained == NOT_SET) actionset->is_chained = 0; @@ -844,6 +847,8 @@ int msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase) { */ msre_actionset_set_defaults(rule->actionset); + rule->actionset->rule = rule; + *(const msre_rule **)apr_array_push(arr) = rule; return 1; @@ -972,14 +977,23 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { char *msg = ""; char *severity = ""; char *tags = ""; + char *fn = ""; int k; if (actionset == NULL) return ""; - if (actionset->id != NULL) id = apr_psprintf(msr->mp, " [id \"%s\"]", - log_escape(msr->mp, actionset->id)); - if (actionset->rev != NULL) rev = apr_psprintf(msr->mp, " [rev \"%s\"]", - log_escape(msr->mp, actionset->rev)); + if ((actionset->rule != NULL) && (actionset->rule->filename != NULL)) { + fn = apr_psprintf(msr->mp, " [file \"%s\"] [line \"%d\"]", + actionset->rule->filename, actionset->rule->line_num); + } + if (actionset->id != NULL) { + id = apr_psprintf(msr->mp, " [id \"%s\"]", + log_escape(msr->mp, actionset->id)); + } + if (actionset->rev != NULL) { + rev = apr_psprintf(msr->mp, " [rev \"%s\"]", + log_escape(msr->mp, actionset->rev)); + } if (actionset->msg != NULL) { /* Expand variables in the message string. */ msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); @@ -988,11 +1002,11 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { expand_macros(msr, var, NULL, msr->mp); msg = apr_psprintf(msr->mp, " [msg \"%s\"]", - log_escape_ex(msr->mp, var->value, var->value_len)); + log_escape_ex(msr->mp, var->value, var->value_len)); } if ((actionset->severity >= 0)&&(actionset->severity <= 7)) { severity = apr_psprintf(msr->mp, " [severity \"%s\"]", - msre_format_severity(actionset->severity)); + msre_format_severity(actionset->severity)); } /* Extract rule tags from the action list. */ @@ -1007,7 +1021,7 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { } } - return apr_pstrcat(msr->mp, id, rev, msg, severity, tags, NULL); + return apr_pstrcat(msr->mp, fn, id, rev, msg, severity, tags, NULL); } /** diff --git a/apache2/re.h b/apache2/re.h index 203a5089..b459ce01 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -226,6 +226,7 @@ struct msre_actionset { const char *msg; int severity; int phase; + msre_rule *rule; /* Flow */ int is_chained; From 11456dd87a67533c6b80a9ab21c3b0f00486d457 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 4 Jun 2007 20:16:48 +0000 Subject: [PATCH 021/372] Use pmFromFile instead of pmfile and p=phrase instead of parallel in docs. See #16. --- CHANGES | 6 +++--- apache2/re_operators.c | 20 ++++++++++---------- doc/modsecurity2-apache-reference.xml | 22 +++++++++++++--------- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/CHANGES b/CHANGES index a0efa01a..f5aaea4f 100644 --- a/CHANGES +++ b/CHANGES @@ -6,9 +6,9 @@ * Added rule file/line to audit log messages. - * Added new parallel matching operators, @pm and @pmfile. These use - an alternate set based matching engine to perform faster keyword - type matches. + * Added new phrase matching operators, @pm and @pmFromFile. These use + an alternate set based matching engine to perform faster phrase + type matches such as black/white lists, spam keywords, etc. * Cache transformations per-request/phase so they are not repeated. diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 13806d63..257fa491 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -208,9 +208,9 @@ static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { return 1; } -/* pmfile */ +/* pmFromFile */ -static int msre_op_pmfile_param_init(msre_rule *rule, char **error_msg) { +static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { char errstr[1024]; char buf[HUGE_STRING_LEN + 1]; char *ptr = NULL; @@ -242,12 +242,12 @@ static int msre_op_pmfile_param_init(msre_rule *rule, char **error_msg) { /* Open file and read */ rc = apr_file_open(&fd, fn, APR_READ | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); if (rc != APR_SUCCESS) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open pmfile \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open phrase file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); return 0; } #ifdef DEBUG_CONF - fprintf(stderr, "Loading pmfile: \"%s\"\n", fn); + fprintf(stderr, "Loading phrase file: \"%s\"\n", fn); #endif /* Read one pattern per line skipping empty/commented */ @@ -271,7 +271,7 @@ static int msre_op_pmfile_param_init(msre_rule *rule, char **error_msg) { if ((*ptr == '\0') || (*ptr == '#')) continue; #ifdef DEBUG_CONF - fprintf(stderr, "Adding pmfile pattern: \"%s\"\n", buf); + fprintf(stderr, "Adding phrase file pattern: \"%s\"\n", buf); #endif acmp_add_pattern(p, buf, NULL, NULL, strlen(buf)); @@ -299,10 +299,10 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* This message will be logged. */ if (strlen(match_escaped) > 252) { - *error_msg = apr_psprintf(msr->mp, "Matched substring \"%.252s ...\" at %s.", + *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.", match_escaped, var->name); } else { - *error_msg = apr_psprintf(msr->mp, "Matched substring \"%s\" at %s.", + *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.", match_escaped, var->name); } return 1; @@ -1353,10 +1353,10 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_pm_execute ); - /* pmfile */ + /* pmFromFile */ msre_engine_op_register(engine, - "pmfile", - msre_op_pmfile_param_init, + "pmFromFile", + msre_op_pmFromFile_param_init, msre_op_pm_execute ); diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index c6221372..6a108498 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4290,9 +4290,10 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}"
<literal>pm</literal> - Description: Parallel Match - operator. This operator uses a set based matching engine for faster - matches of keyword lists. + 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: @@ -4304,13 +4305,16 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}"
- <literal>pmfile</literal> + <literal>pmFromFile</literal> - Description: Parallel Match - operator. This operator uses a set based matching engine for faster - matches of keyword lists. It is the same as @pm - except that it takes a list of files arguments. The contents of the - files should be one pattern per line. + 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. 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. Example: From dd6755985cbca4f5dd0f69301b06efda5744848f Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 5 Jun 2007 18:20:44 +0000 Subject: [PATCH 022/372] Move the transformation cache recort into re.h. See #14. --- apache2/modsecurity.h | 10 ---------- apache2/re.c | 12 ++++++------ apache2/re.h | 12 ++++++++++++ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 69e52c4c..0a523168 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -502,14 +502,4 @@ void DSOLOCAL msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, c apr_status_t DSOLOCAL modsecurity_request_body_clear(modsec_rec *msr); -/* Data Cache */ - -struct msc_cache_rec { - int hits; - int changed; - const char *key; - const char *val; - apr_size_t val_len; -}; - #endif diff --git a/apache2/re.c b/apache2/re.c index debd6c7a..47286f0f 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1364,8 +1364,8 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { { const apr_array_header_t *tarr; const apr_table_entry_t *telts; - msc_cache_rec **carr = NULL; - msc_cache_rec *crec = NULL; + msre_cache_rec **carr = NULL; + msre_cache_rec *crec = NULL; char *tfnsvar = NULL; char *tfnskey = NULL; int tfnscount = 0; @@ -1417,7 +1417,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (usecache && tfnscount > 1 && !multi_match) { crec = NULL; msr_log(msr, 9, "CACHE: Fetching %s (multi)", tfnskey); - carr = (msc_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); + carr = (msre_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); if (carr != NULL) { crec = carr[msr->phase]; } @@ -1500,7 +1500,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { /* Try to fetch this transformation from cache */ msr_log(msr, 9, "CACHE: Fetching %s", tfnskey); crec = NULL; - carr = (msc_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); + carr = (msre_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); if (carr != NULL) { crec = carr[msr->phase]; } @@ -1532,12 +1532,12 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (usecache) { /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ if (carr == NULL) { - carr = (msc_cache_rec **)apr_pcalloc(msr->mp, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); + carr = (msre_cache_rec **)apr_pcalloc(msr->mp, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); if (carr == NULL) return -1; memset(carr, 0, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); apr_hash_set(msr->tcache, tfnskey, APR_HASH_KEY_STRING, carr); } - crec = carr[msr->phase] = (msc_cache_rec *)apr_pcalloc(msr->mp, sizeof(msc_cache_rec)); + crec = carr[msr->phase] = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); if (crec == NULL) return -1; crec->hits = 0; diff --git a/apache2/re.h b/apache2/re.h index b459ce01..d011ea57 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -28,6 +28,7 @@ typedef struct msre_tfn_metadata msre_tfn_metadata; typedef struct msre_actionset msre_actionset; typedef struct msre_action_metadata msre_action_metadata; typedef struct msre_action msre_action; +typedef struct msre_cache_rec msre_cache_rec; #include "apr_general.h" #include "apr_tables.h" @@ -304,4 +305,15 @@ apr_status_t DSOLOCAL msre_parse_vars(msre_ruleset *ruleset, const char *text, char DSOLOCAL *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset); + +/* -- Data Cache -- */ + +struct msre_cache_rec { + int hits; + int changed; + const char *key; + const char *val; + apr_size_t val_len; +}; + #endif From 71eb6e17a42c8af818a753cde81221b15bc203e9 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 8 Jun 2007 15:48:02 +0000 Subject: [PATCH 023/372] Added XPath references. --- doc/modsecurity2-apache-reference.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 6a108498..16d910ba 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -2814,6 +2814,22 @@ SecRule REQBODY_PROCESSOR "!^XML$" skip:2 SecRule XML:/employees/employee/name/text() Fred SecRule XML:/xq:employees/employee/name/text() Fred \ xmlns:xq=http://www.example.com/employees + + To learn more about XPath we suggest the following + resources: + + + + XPath + Standard + + + + XPath + Tutorial + +
From 46d7a5ec6f23167f1851139ae0677e67cb560e9e Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 11 Jun 2007 21:15:14 +0000 Subject: [PATCH 024/372] Move transformation cache rec def re.h from modsecurity.h --- apache2/modsecurity.h | 1 - apache2/re.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 0a523168..fb106284 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -25,7 +25,6 @@ typedef struct msc_engine msc_engine; typedef struct msc_data_chunk msc_data_chunk; typedef struct msc_arg msc_arg; typedef struct msc_string msc_string; -typedef struct msc_cache_rec msc_cache_rec; #if !(defined(WIN32) || defined(NETWARE) || defined(SOLARIS2)) #define DSOLOCAL __attribute__((visibility("hidden"))) diff --git a/apache2/re.c b/apache2/re.c index 47286f0f..36700ff5 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1532,9 +1532,9 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (usecache) { /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ if (carr == NULL) { - carr = (msre_cache_rec **)apr_pcalloc(msr->mp, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); + carr = (msre_cache_rec **)apr_pcalloc(msr->mp, (sizeof(msre_cache_rec *) * (PHASE_LAST + 1))); if (carr == NULL) return -1; - memset(carr, 0, (sizeof(msc_cache_rec *) * (PHASE_LAST + 1))); + memset(carr, 0, (sizeof(msre_cache_rec *) * (PHASE_LAST + 1))); apr_hash_set(msr->tcache, tfnskey, APR_HASH_KEY_STRING, carr); } crec = carr[msr->phase] = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); @@ -1588,13 +1588,13 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (msr->txcfg->debuglog_level >= 9) { apr_hash_index_t *hi; void *dummy; - msc_cache_rec **rec; + msre_cache_rec **rec; int hn = 0; int ri; for (hi = apr_hash_first(msr->mp, msr->tcache); hi; hi = apr_hash_next(hi)) { hn++; apr_hash_this(hi, NULL, NULL, &dummy); - rec = (msc_cache_rec **)dummy; + rec = (msre_cache_rec **)dummy; if (rec == NULL) continue; for (ri = PHASE_FIRST; ri <= PHASE_LAST; ri++) { From 23bd6b43316dae0edf3fdabb23f3b77c09f5e61d Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 11 Jun 2007 21:20:07 +0000 Subject: [PATCH 025/372] Do not pause if we are not the main request. See #124. --- apache2/mod_security2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 47b267de..d25ffa81 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -83,8 +83,8 @@ int perform_interception(modsec_rec *msr) { */ log_level = (actionset->log != 1) ? 4 : 1; - /* Pause the request first (if configured to do so). */ - if (actionset->intercept_pause) { + /* Pause the request first (if configured to do so and the main request). */ + if (actionset->intercept_pause && (msr->r->main == NULL)) { msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " "%i msec.", actionset->intercept_pause); /* apr_sleep accepts microseconds */ From 6350e2badc9b9f2d4ce724539772a099a7643830 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 11 Jun 2007 21:28:03 +0000 Subject: [PATCH 026/372] Do not log alert message for subrequests. See #124. Cleanup CHANGES. --- CHANGES | 8 ++++---- apache2/mod_security2.c | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index f5aaea4f..72942b08 100644 --- a/CHANGES +++ b/CHANGES @@ -2,9 +2,9 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- - * TODO: Detect static files (trac #15) + * Do not trigger "pause" action for subrequests. - * Added rule file/line to audit log messages. + * Added matching rule filename and line number to audit log. * Added new phrase matching operators, @pm and @pmFromFile. These use an alternate set based matching engine to perform faster phrase @@ -14,10 +14,10 @@ * Fixed problem with subrequests not being intercepted (only logged). - * Add Solaris to the list of platforms not supporting the hidden + * Added Solaris to the list of platforms not supporting the hidden visibility attribute. - * Remove excessive debug log entries about "capture" action. + * Removed excessive debug log entries about "capture" action. * Fixed decoding full-width unicode in t:urlDecodeUni. diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index d25ffa81..638a1d62 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -79,9 +79,10 @@ int perform_interception(modsec_rec *msr) { phase_text = apr_psprintf(msr->mp, " (phase %i)", msr->phase); /* By default we log at level 1 but we switch to 4 - * if a nolog action was used to hide the message. + * if a nolog action was used or this is a subrequest + * to hide the message. */ - log_level = (actionset->log != 1) ? 4 : 1; + log_level = ((actionset->log != 1) || (msr->r->main != NULL)) ? 4 : 1; /* Pause the request first (if configured to do so and the main request). */ if (actionset->intercept_pause && (msr->r->main == NULL)) { From eec279c8d91641ccc4b4064d672af98d4f65a415 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 14 Jun 2007 14:43:35 +0000 Subject: [PATCH 027/372] Cleanup code. --- apache2/pdf_protect.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index 33e3b29d..c6ce8452 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -11,7 +11,6 @@ * */ #include "modsecurity.h" -// #include "apache2.h" #include "pdf_protect.h" #include @@ -225,7 +224,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, - "ModSecurity: Internal Error: msr is null in PDF output filter."); + "ModSecurity: Internal Error: Unable to retrieve context in PDF output filter."); ap_remove_output_filter(f); return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } @@ -235,7 +234,8 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: r->content_type=%s, header C-T=%s", - f->r->content_type, h_content_type); + log_escape_nq(msr->mp, f->r->content_type), + log_escape_nq(msr->mp, h_content_type)); } /* Have we been asked to tweak the headers? */ @@ -255,6 +255,10 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } /* Proceed to detect dynamically-generated PDF files. */ + + // 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 if (((f->r->content_type != NULL)&&(strcasecmp(f->r->content_type, "application/pdf") == 0)) || ((h_content_type != NULL)&&(strcasecmp(h_content_type, "application/pdf") == 0))) { @@ -263,7 +267,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: Detected a dynamically-generated PDF in %s", - r->uri); + log_escape_nq(msr->mp, r->uri)); } /* Is this a non-GET request? */ @@ -298,10 +302,11 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { /* Redirect user to the new URI. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: PDF request without a token - " - "redirecting to %s.", new_uri); + "redirecting to %s.", log_escape_nq(msr->mp, new_uri)); } apr_table_set(r->headers_out, "Location", new_uri); + return send_error_bucket(f, REDIRECT_STATUS); } } else { /* Token found. */ @@ -353,6 +358,7 @@ int pdfp_check(modsec_rec *msr) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Not enabled here."); } + return 0; } @@ -365,17 +371,18 @@ int pdfp_check(modsec_rec *msr) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Unable to inspect URI because it is NULL."); } - /* TODO Should we return -1 instead? */ - return 0; + + return -1; /* Error. */ } if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: URI=%s, filename=%s, QUERY_STRING=%s.", - msr->r->uri, msr->r->filename, msr->r->args); + log_escape_nq(msr->mp, msr->r->uri), log_escape_nq(msr->mp, msr->r->filename), + log_escape_nq(msr->mp, msr->r->args)); } uri = apr_pstrdup(msr->mp, msr->r->uri); - if (uri == NULL) return -1; + if (uri == NULL) return -1; /* Error. */ ap_str_tolower(uri); /* Attempt to figure out if this is a request for a PDF file. We are @@ -389,6 +396,7 @@ int pdfp_check(modsec_rec *msr) { msr_log(msr, 4, "PdfProtect: No indication in the URI this is a " "request for a PDF file."); } + return 0; } @@ -398,8 +406,9 @@ int pdfp_check(modsec_rec *msr) { if ((msr->r->method_number != M_GET)&&(cfg->pdfp_only_get != 0)) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Configured not to intercept non-GET requests " - "(method=%s/%i).", msr->r->method, msr->r->method_number); + "(method=%s/%i).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); } + return 0; } @@ -421,7 +430,7 @@ int pdfp_check(modsec_rec *msr) { /* Redirect user to the new URI. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: PDF request without a token - redirecting to %s.", - new_uri); + log_escape_nq(msr->mp, new_uri)); } apr_table_set(msr->r->headers_out, "Location", new_uri); @@ -437,6 +446,7 @@ int pdfp_check(modsec_rec *msr) { msr_log(msr, 9, "PdfProtect: PDF request with a valid token - " "serving PDF file normally."); } + return 0; } else { /* Not valid. */ /* The token is not valid. We will tweak the response From c7f5dc3355f99c8c2b093d66c00a8460797971af Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 14 Jun 2007 14:54:23 +0000 Subject: [PATCH 028/372] Configure PDF protection by token redirection to only work on GET and HEAD requests. If we attempted to work on other request methods we would probably break something as there is no way to preserve request bodies. The default was previously been to work on all requests. This behavious can still be changed using the SecPdfProtectInterceptGETOnly directive but I am going to leave it undocumented. --- apache2/apache2_config.c | 4 ++-- apache2/pdf_protect.c | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index e19da940..8c975d45 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -456,7 +456,7 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->pdfp_secret == NOT_SET_P) dcfg->pdfp_secret = NULL; if (dcfg->pdfp_timeout == NOT_SET) dcfg->pdfp_timeout = 10; if (dcfg->pdfp_token_name == NOT_SET_P) dcfg->pdfp_token_name = "PDFPTOKEN"; - if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 0; + if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 1; /* Geo Lookup */ if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; @@ -1547,7 +1547,7 @@ const command_rec module_directives[] = { cmd_pdf_protect_intercept_get_only, NULL, RSRC_CONF, - "whether or not to intercept only GET requess." + "whether or not to intercept only GET and HEAD requess. Defaults to true." ), AP_INIT_TAKE1 ( diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index c6ce8452..438934ca 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -400,12 +400,14 @@ int pdfp_check(modsec_rec *msr) { return 0; } - /* Ignore request methods other than GET if + /* Ignore request methods other than GET and HEAD if * configured to do so. */ - if ((msr->r->method_number != M_GET)&&(cfg->pdfp_only_get != 0)) { + if ((msr->r->method_number != M_GET)&&(msr->r->method_number != M_HEAD) + &&(cfg->pdfp_only_get != 0)) + { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "PdfProtect: Configured not to intercept non-GET requests " + msr_log(msr, 4, "PdfProtect: Not intercepting a GET/HEAD request " "(method=%s/%i).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); } From 8b843127ba3c8c9d3c627aa7e6a62d78a06a03b8 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 14 Jun 2007 14:59:48 +0000 Subject: [PATCH 029/372] Revert incorrect change to GET/HEAD detection code. This will teach me to always compile before I commit. --- apache2/pdf_protect.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index 438934ca..e77f1933 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -403,9 +403,7 @@ int pdfp_check(modsec_rec *msr) { /* Ignore request methods other than GET and HEAD if * configured to do so. */ - if ((msr->r->method_number != M_GET)&&(msr->r->method_number != M_HEAD) - &&(cfg->pdfp_only_get != 0)) - { + if ((msr->r->method_number != M_GET)&&(cfg->pdfp_only_get != 0)) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Not intercepting a GET/HEAD request " "(method=%s/%i).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); From 74738b29b0fb51a954ef188f230f1fdb5a224dec Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 14 Jun 2007 15:26:08 +0000 Subject: [PATCH 030/372] Added new directive (SecPdfProtectMethod) to enable the user to choose between using token redirection (falling back on forced download in some cases) and forced download (in all cases). --- apache2/apache2_config.c | 33 ++++++++++++++++++++++++++++++++- apache2/modsecurity.h | 1 + apache2/pdf_protect.c | 38 +++++++++++++++++++++++++++++++++++--- apache2/pdf_protect.h | 3 +++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 8c975d45..16606402 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -14,7 +14,7 @@ #include "modsecurity.h" #include "msc_logging.h" - +#include "pdf_protect.h" #include "http_log.h" /* #define DEBUG_CONF 1 */ @@ -90,6 +90,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->pdfp_timeout = NOT_SET; dcfg->pdfp_token_name = NOT_SET_P; dcfg->pdfp_only_get = NOT_SET; + dcfg->pdfp_method = NOT_SET; /* Geo Lookups */ dcfg->geo = NOT_SET_P; @@ -384,6 +385,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { ? parent->pdfp_token_name : child->pdfp_token_name); merged->pdfp_only_get = (child->pdfp_only_get == NOT_SET ? parent->pdfp_only_get : child->pdfp_only_get); + merged->pdfp_method = (child->pdfp_method == NOT_SET + ? parent->pdfp_method : child->pdfp_method); /* Geo Lookup */ merged->geo = (child->geo == NOT_SET_P @@ -457,6 +460,7 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->pdfp_timeout == NOT_SET) dcfg->pdfp_timeout = 10; if (dcfg->pdfp_token_name == NOT_SET_P) dcfg->pdfp_token_name = "PDFPTOKEN"; if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 1; + if (dcfg->pdfp_method == NOT_SET) dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; /* Geo Lookup */ if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; @@ -1195,6 +1199,25 @@ static const char *cmd_pdf_protect_intercept_get_only(cmd_parms *cmd, void *_dcf return NULL; } +static const char *cmd_pdf_protect_method(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "TokenRedirection") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; + } else + if (strcasecmp(p1, "ForcedDownload") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_FORCED_DOWNLOAD; + } else { + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecPdfProtectMethod: %s", p1); + } + + return NULL; +} + /* -- Geo Lookup configuration -- */ static const char *cmd_geo_lookups_db(cmd_parms *cmd, void *_dcfg, @@ -1550,6 +1573,14 @@ const command_rec module_directives[] = { "whether or not to intercept only GET and HEAD requess. Defaults to true." ), + AP_INIT_TAKE1 ( + "SecPdfProtectMethod", + cmd_pdf_protect_method, + NULL, + RSRC_CONF, + "protection method to use. Can be 'TokenRedirection' (default) or 'ForcedDownload'" + ), + AP_INIT_TAKE1 ( "SecGeoLookupsDb", cmd_geo_lookups_db, diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index fb106284..6a34ca5b 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -412,6 +412,7 @@ struct directory_config { int pdfp_timeout; const char *pdfp_token_name; int pdfp_only_get; + int pdfp_method; /* Geo Lookup */ geo_db *geo; diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index e77f1933..87e2df2b 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -225,11 +225,14 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, "ModSecurity: Internal Error: Unable to retrieve context in PDF output filter."); + ap_remove_output_filter(f); + return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } if (msr->txcfg->pdfp_enabled == 1) { + // TODO Should we look at err_headers_out too? const char *h_content_type = apr_table_get(f->r->headers_out, "Content-Type"); if (msr->txcfg->debuglog_level >= 9) { @@ -258,7 +261,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { // 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 + // application/acrobat, text/pdf, text/x-pdf ??? if (((f->r->content_type != NULL)&&(strcasecmp(f->r->content_type, "application/pdf") == 0)) || ((h_content_type != NULL)&&(strcasecmp(h_content_type, "application/pdf") == 0))) { @@ -270,13 +273,32 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { log_escape_nq(msr->mp, r->uri)); } + /* If we are configured with ForcedDownload protection method then we + * can do our thing here and finish early. + */ + if (msr->txcfg->pdfp_method == PDF_PROTECT_METHOD_FORCED_DOWNLOAD) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically " + "generated PDF file."); + } + + apr_table_set(f->r->headers_out, "Content-Disposition", DISPOSITION_VALUE); + f->r->content_type = ATTACHMENT_MIME_TYPE; + + ap_remove_output_filter(f); + + return ap_pass_brigade(f->next, bb_in); + } + + /* If we are here that means TokenRedirection is the desired protection method. */ + /* Is this a non-GET request? */ if ((f->r->method_number != M_GET)&& ((msr->txcfg->pdfp_only_get == 1)||(msr->txcfg->pdfp_only_get == -1)) ) { /* This is a non-GET request and we have been configured - * not to intercept it. We are not going to do that but - * we are going to tweak the headers to force download. + * not to intercept it. So we are going to tweak the headers + * to force download. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically " @@ -287,6 +309,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { f->r->content_type = ATTACHMENT_MIME_TYPE; ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb_in); } @@ -362,6 +385,15 @@ int pdfp_check(modsec_rec *msr) { return 0; } + if (msr->txcfg->pdfp_method != PDF_PROTECT_METHOD_TOKEN_REDIRECTION) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "PdfProtect: Configured with ForcedDownload as protection method, " + "skipping detection on the inbound."); + } + + return 0; + } + /* Then determine whether we need to act at * all. If the request is not for a PDF file * return straight away. diff --git a/apache2/pdf_protect.h b/apache2/pdf_protect.h index 3502a6e1..086bc77f 100644 --- a/apache2/pdf_protect.h +++ b/apache2/pdf_protect.h @@ -14,6 +14,9 @@ #ifndef _PDF_PROTECT_H_ #define _PDF_PROTECT_H_ +#define PDF_PROTECT_METHOD_TOKEN_REDIRECTION 1 +#define PDF_PROTECT_METHOD_FORCED_DOWNLOAD 2 + apr_status_t DSOLOCAL pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in); int DSOLOCAL pdfp_check(modsec_rec *msr); From c39723c3aadfb9283fd12ad665e5ebae6200e546 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 14 Jun 2007 15:48:53 +0000 Subject: [PATCH 031/372] Document SecPdfProtectMethod. --- doc/modsecurity2-apache-reference.xml | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 16d910ba..20ab481c 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.2.0-trunk / (May 3, 2007) + Version 2.2.0-trunk / (June 14, 2007) 2004-2007 @@ -1087,7 +1087,7 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtect (Experimental) + <literal>SecPdfProtect</literal> (Experimental) Description: Enables the PDF XSS protection functionality. Once enabled access to PDF files is tracked. @@ -1101,7 +1101,25 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtectSecret (Experimental) + <literal>SecPdfProtectMethod</literal> (Experimental) + + 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!"). + + Default: + TokenRedirection +
+ +
+ <literal>SecPdfProtectSecret</literal> (Experimental) Description: Defines the secret that will be used to construct one-time tokens. You should use a @@ -1113,7 +1131,7 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtectTimeout (Experimental) + <literal>SecPdfProtectTimeout</literal> (Experimental) Description: Defines the token timeout. After token expires it can no longer be used to allow access to @@ -1125,7 +1143,7 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtectTokenName (Experimental) + <literal>SecPdfProtectTokenName</literal> (Experimental) Description: Defines the name of the token. The only reason you would want to change the name of the From 81d0f84ad3f3ccbc5e41485f67afac8cbb371051 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Jun 2007 16:05:45 +0000 Subject: [PATCH 032/372] Update copyright text to Breach Security, Inc. Merge in changes from branches/2.1.x --- CHANGES | 9 ++++-- README.TXT | 8 ++++-- apache2/apache2.h | 15 ++++++---- apache2/apache2_config.c | 6 ++-- apache2/apache2_io.c | 6 ++-- apache2/apache2_util.c | 9 +++--- apache2/mod_security2.c | 59 ++++++++++++++++++++++++---------------- apache2/modsecurity.c | 6 ++-- apache2/modsecurity.h | 6 ++-- apache2/msc_geo.c | 4 +-- apache2/msc_geo.h | 4 +-- apache2/msc_logging.c | 6 ++-- apache2/msc_logging.h | 6 ++-- apache2/msc_multipart.c | 6 ++-- apache2/msc_multipart.h | 6 ++-- apache2/msc_parsers.c | 6 ++-- apache2/msc_parsers.h | 6 ++-- apache2/msc_pcre.c | 6 ++-- apache2/msc_pcre.h | 6 ++-- apache2/msc_reqbody.c | 6 ++-- apache2/msc_util.c | 6 ++-- apache2/msc_util.h | 6 ++-- apache2/msc_xml.c | 6 ++-- apache2/msc_xml.h | 6 ++-- apache2/pdf_protect.c | 6 ++-- apache2/pdf_protect.h | 6 ++-- apache2/persist_dbm.c | 6 ++-- apache2/persist_dbm.h | 6 ++-- apache2/re.c | 20 ++++++++++---- apache2/re.h | 6 ++-- apache2/re_actions.c | 6 ++-- apache2/re_operators.c | 6 ++-- apache2/re_tfns.c | 6 ++-- apache2/re_variables.c | 6 ++-- rules/README | 2 +- 35 files changed, 134 insertions(+), 152 deletions(-) diff --git a/CHANGES b/CHANGES index 72942b08..fe56ffc6 100644 --- a/CHANGES +++ b/CHANGES @@ -2,7 +2,7 @@ ?? ??? 2007 - 2.2.0-trunk ------------------------- - * Do not trigger "pause" action for subrequests. + * Do not trigger "pause" action for internal requests. * Added matching rule filename and line number to audit log. @@ -12,7 +12,9 @@ * Cache transformations per-request/phase so they are not repeated. - * Fixed problem with subrequests not being intercepted (only logged). + * Fixed issue with requests that use internal requests. These had the + potential to be intercepted incorrectly when other Apache httpd modules + that used internal requests were used with mod_security. * Added Solaris to the list of platforms not supporting the hidden visibility attribute. @@ -21,7 +23,8 @@ * Fixed decoding full-width unicode in t:urlDecodeUni. - * Only calculate debugging vars when we are debugging (more to come). + * Lessen some overhead of debugging messages and calculations + TODO: more to come * Removed strnlen() calls for non-GNU platforms. diff --git a/README.TXT b/README.TXT index 666c25a0..2c0ba1d8 100644 --- a/README.TXT +++ b/README.TXT @@ -1,6 +1,10 @@ +ModSecurity for Apache 2.x, http://www.modsecurity.org/ +Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) -ModSecurity for Apache (http://www.modsecurity.org) -Copyright (C) 2004-2006 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. DOCUMENTATION diff --git a/apache2/apache2.h b/apache2/apache2.h index 6f8d68e6..ddd14320 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id$ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _APACHE2_H_ @@ -65,7 +63,14 @@ char DSOLOCAL *get_env_var(request_rec *r, char *name); void DSOLOCAL internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, int level, const char *text, va_list ap); -void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...); + +/* msr_log is now a macro to avoid function call overhead. Nothing + * is done to avoid expansion of arguments, so do not call with + * arguments that cannot be duplicated (ie no level++, etc.) + */ +void DSOLOCAL _msr_log(modsec_rec *msr, int level, const char *text, ...); +#define msr_log(msr, lvl, ...) \ + do { if ((msr->txcfg != NULL) && (lvl <= msr->txcfg->debuglog_level)) _msr_log(msr, lvl, __VA_ARGS__); } while(0) char DSOLOCAL *format_error_log_message(apr_pool_t *mp, error_message *em); diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 16606402..526934c3 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: apache2_config.c,v 1.8 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index c1656773..72d05141 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: apache2_io.c,v 1.6 2007/01/23 16:08:15 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 817ddc03..afa994b4 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: apache2_util.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" @@ -247,7 +245,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, * Logs one message at the given level to the debug log (and to the * Apache error log if the message is important enough. */ -void msr_log(modsec_rec *msr, int level, const char *text, ...) { +void _msr_log(modsec_rec *msr, int level, const char *text, ...) { va_list ap; va_start(ap, text); @@ -255,6 +253,7 @@ void msr_log(modsec_rec *msr, int level, const char *text, ...) { va_end(ap); } + /** * Converts an Apache error log message into one line of text. */ diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 638a1d62..21ee6a8c 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: mod_security2.c,v 1.11 2006/12/15 15:06:04 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include @@ -55,15 +53,30 @@ int perform_interception(modsec_rec *msr) { msre_actionset *actionset = NULL; const char *message = NULL; const char *phase_text = ""; - const char *subreq_text = (msr->r->main == NULL) ? "" : "Subrequest. "; + const char *intreq_text = ""; + int is_initial_req = ap_is_initial_req(msr->r); int status = DECLINED; int log_level = 1; + /* Check for an initial request */ + + if (is_initial_req == 0) { + if (msr->r->main != NULL) { + intreq_text = "Sub-Request: "; + } + else if (msr->r->prev != NULL) { + intreq_text = "Internal Redirect: "; + } + else { + intreq_text = "Internal Request: "; + } + } + + /* Sanity checks first. */ if (msr->was_intercepted == 0) { msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero"); - msr->was_intercepted = 0; return DECLINED; } @@ -79,13 +92,13 @@ int perform_interception(modsec_rec *msr) { phase_text = apr_psprintf(msr->mp, " (phase %i)", msr->phase); /* By default we log at level 1 but we switch to 4 - * if a nolog action was used or this is a subrequest + * if a nolog action was used or this is not the initial request * to hide the message. */ - log_level = ((actionset->log != 1) || (msr->r->main != NULL)) ? 4 : 1; + log_level = ((actionset->log != 1) || (is_initial_req == 0)) ? 4 : 1; - /* Pause the request first (if configured to do so and the main request). */ - if (actionset->intercept_pause && (msr->r->main == NULL)) { + /* Pause the request first (if configured and the initial request). */ + if (actionset->intercept_pause && (is_initial_req == 1)) { msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " "%i msec.", actionset->intercept_pause); /* apr_sleep accepts microseconds */ @@ -98,13 +111,13 @@ int perform_interception(modsec_rec *msr) { if (actionset->intercept_status != 0) { status = actionset->intercept_status; message = apr_psprintf(msr->mp, "%sAccess denied with code %i%s.", - subreq_text, status, phase_text); + intreq_text, status, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Internal Error: Invalid status code requested %i).", - subreq_text, phase_text, actionset->intercept_status); + intreq_text, phase_text, actionset->intercept_status); } break; @@ -115,15 +128,15 @@ int perform_interception(modsec_rec *msr) { status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Configuration Error: Proxy action to %s requested but mod_proxy not found).", - subreq_text, phase_text, + intreq_text, phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); } else { msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri); msr->r->proxyreq = PROXYREQ_REVERSE; msr->r->handler = "proxy-server"; status = OK; - message = apr_psprintf(msr->mp, "%sAccess denied using proxy to %s%s.", - subreq_text, phase_text, + message = apr_psprintf(msr->mp, "%sAccess denied using proxy to%s %s.", + intreq_text, phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); } } else { @@ -131,7 +144,7 @@ int perform_interception(modsec_rec *msr) { status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Configuration Error: Proxy action requested but it does not work in output phases).", - subreq_text, phase_text); + intreq_text, phase_text); } break; @@ -149,21 +162,21 @@ int perform_interception(modsec_rec *msr) { if (apr_socket_close(csd) == APR_SUCCESS) { status = HTTP_FORBIDDEN; message = apr_psprintf(msr->mp, "%sAccess denied with connection close%s.", - subreq_text, phase_text); + intreq_text, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop requested but failed to close the " " socket).", - subreq_text, phase_text); + intreq_text, phase_text); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop requested but socket not found.", - subreq_text, phase_text); + intreq_text, phase_text); } } #else @@ -171,7 +184,7 @@ int perform_interception(modsec_rec *msr) { status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop not implemented on this platform).", - subreq_text, phase_text); + intreq_text, phase_text); #endif break; @@ -186,14 +199,14 @@ int perform_interception(modsec_rec *msr) { } message = apr_psprintf(msr->mp, "%sAccess denied with redirection to %s using " "status %i%s.", - subreq_text, + intreq_text, log_escape_nq(msr->mp, actionset->intercept_uri), status, phase_text); break; case ACTION_ALLOW : status = DECLINED; - message = apr_psprintf(msr->mp, "%sAccess allowed%s.", subreq_text, phase_text); + message = apr_psprintf(msr->mp, "%sAccess allowed%s.", intreq_text, phase_text); msr->was_intercepted = 0; break; @@ -202,7 +215,7 @@ int perform_interception(modsec_rec *msr) { status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Internal Error: invalid interception action %i).", - subreq_text, phase_text, actionset->intercept_action); + intreq_text, phase_text, actionset->intercept_action); break; } diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 2fd65234..8eab51f8 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: modsecurity.c,v 1.7 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 6a34ca5b..ce18f20f 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: modsecurity.h,v 1.27 2007/02/05 12:44:40 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MODSECURITY_H_ diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 0cf74998..4f0df69c 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -1,11 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_geo.h" diff --git a/apache2/msc_geo.h b/apache2/msc_geo.h index 418cd0b3..4c962ffa 100644 --- a/apache2/msc_geo.h +++ b/apache2/msc_geo.h @@ -1,11 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_GEO_H_ diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index abe5a2a6..9935ef57 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_logging.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_logging.h" diff --git a/apache2/msc_logging.h b/apache2/msc_logging.h index 26ddeb0a..6cfaa181 100644 --- a/apache2/msc_logging.h +++ b/apache2/msc_logging.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_logging.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_LOGGING_H_ diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index a540ce46..f953bd90 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_multipart.c,v 1.2 2006/10/16 04:41:51 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h index 81bf65d6..cfcc35dc 100644 --- a/apache2/msc_multipart.h +++ b/apache2/msc_multipart.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_multipart.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_MULTIPART_H_ diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index b3720554..69be93e4 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_parsers.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_parsers.h" diff --git a/apache2/msc_parsers.h b/apache2/msc_parsers.h index ff7c0b8d..d4370c02 100644 --- a/apache2/msc_parsers.h +++ b/apache2/msc_parsers.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_parsers.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_PARSERS_H_ diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c index 11fd9e72..c4f99117 100644 --- a/apache2/msc_pcre.c +++ b/apache2/msc_pcre.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_pcre.c,v 1.2 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_pcre.h" diff --git a/apache2/msc_pcre.h b/apache2/msc_pcre.h index d9af38b5..6067de43 100644 --- a/apache2/msc_pcre.h +++ b/apache2/msc_pcre.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_pcre.h,v 1.3 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_PCRE_H_ diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index f0f40530..f1586019 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_reqbody.c,v 1.2 2006/12/04 21:54:10 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/msc_util.c b/apache2/msc_util.c index acc33e94..e4ac75a0 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_util.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_util.h" diff --git a/apache2/msc_util.h b/apache2/msc_util.h index f1ee8169..9a5548ae 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_util.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _UTIL_H_ diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index a8f7025d..df1e62d5 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_xml.c,v 1.2 2006/12/04 20:04:09 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifdef WITH_LIBXML2 diff --git a/apache2/msc_xml.h b/apache2/msc_xml.h index cea27cef..f8da4adb 100644 --- a/apache2/msc_xml.h +++ b/apache2/msc_xml.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_xml.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_XML_H_ diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index 87e2df2b..15c2caaf 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id$ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/pdf_protect.h b/apache2/pdf_protect.h index 086bc77f..8936eb55 100644 --- a/apache2/pdf_protect.h +++ b/apache2/pdf_protect.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2007 Thinking Stone (http://www.thinkingstone.com) - * - * $Id$ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index cb616c97..f5c31cef 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: persist_dbm.c,v 1.3 2006/12/21 19:57:41 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "persist_dbm.h" diff --git a/apache2/persist_dbm.h b/apache2/persist_dbm.h index fe006a43..6a5af98f 100644 --- a/apache2/persist_dbm.h +++ b/apache2/persist_dbm.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: persist_dbm.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _PERSIST_DBM_H_ diff --git a/apache2/re.c b/apache2/re.c index 36700ff5..20dfadb1 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re.c,v 1.15 2006/12/29 10:44:25 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include @@ -1438,7 +1436,19 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { rc = execute_operator(var, rule, msr, acting_actionset, mptmp); - return (rc < 0) ? : rc; + if (rc < 0) { + return -1; + } + if (rc == RULE_MATCH) { + /* Return straight away if the transaction + * was intercepted - no need to process the remaining + * targets. + */ + if (msr->was_intercepted) { + return RULE_MATCH; + } + } + continue; /* next target */ } } diff --git a/apache2/re.h b/apache2/re.h index d011ea57..09111279 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re.h,v 1.7 2006/12/29 10:31:38 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_RE_H_ diff --git a/apache2/re_actions.c b/apache2/re_actions.c index ede2fc7d..fd0453a2 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_actions.c,v 1.9 2007/02/02 18:16:41 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "re.h" diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 257fa491..5b7c50e6 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_operators.c,v 1.7 2007/01/23 16:08:15 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "re.h" diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 967e38ee..97a4664b 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_tfns.c,v 1.3 2006/12/04 12:00:24 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include diff --git a/apache2/re_variables.c b/apache2/re_variables.c index faefac89..58409b29 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_variables.c,v 1.7 2007/01/23 16:08:15 ivanr Exp $ + * Copyright (c) 2004-2007 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 Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "http_core.h" diff --git a/rules/README b/rules/README index fb6d6597..a11680e6 100644 --- a/rules/README +++ b/rules/README @@ -95,7 +95,7 @@ malicious activity. 4. Trojan Protection - Detecting access to Trojans horses. -5. Errors Hiding – Disguising error messages sent by the server +5. Errors Hiding - Disguising error messages sent by the server In addition the rule set also hints at the power of ModSecurity beyond providing security by reporting access from the major search engines to your From d55e023bf7ff481b13074d1baf231c250422af27 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Jun 2007 16:13:53 +0000 Subject: [PATCH 033/372] Revert msr_log as macro (still work-in-progress) --- apache2/apache2.h | 9 +-------- apache2/apache2_util.c | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/apache2/apache2.h b/apache2/apache2.h index ddd14320..1f3e9571 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -63,14 +63,7 @@ char DSOLOCAL *get_env_var(request_rec *r, char *name); void DSOLOCAL internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, int level, const char *text, va_list ap); - -/* msr_log is now a macro to avoid function call overhead. Nothing - * is done to avoid expansion of arguments, so do not call with - * arguments that cannot be duplicated (ie no level++, etc.) - */ -void DSOLOCAL _msr_log(modsec_rec *msr, int level, const char *text, ...); -#define msr_log(msr, lvl, ...) \ - do { if ((msr->txcfg != NULL) && (lvl <= msr->txcfg->debuglog_level)) _msr_log(msr, lvl, __VA_ARGS__); } while(0) +void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...); char DSOLOCAL *format_error_log_message(apr_pool_t *mp, error_message *em); diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index afa994b4..f60d1ce8 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -245,7 +245,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, * Logs one message at the given level to the debug log (and to the * Apache error log if the message is important enough. */ -void _msr_log(modsec_rec *msr, int level, const char *text, ...) { +void msr_log(modsec_rec *msr, int level, const char *text, ...) { va_list ap; va_start(ap, text); From 6569c444d8b0bcf63ef1472afe98568a3c38a5b4 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Jun 2007 16:42:04 +0000 Subject: [PATCH 034/372] Make rules/README UNIX style EOL. Merge another branch/2.1.x change. --- apache2/mod_security2.c | 4 +- rules/README | 356 ++++++++++++++++++++-------------------- 2 files changed, 179 insertions(+), 181 deletions(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 21ee6a8c..98f0c75c 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -60,7 +60,7 @@ int perform_interception(modsec_rec *msr) { /* Check for an initial request */ - if (is_initial_req == 0) { + if (is_initial_req != 1) { if (msr->r->main != NULL) { intreq_text = "Sub-Request: "; } @@ -95,7 +95,7 @@ int perform_interception(modsec_rec *msr) { * if a nolog action was used or this is not the initial request * to hide the message. */ - log_level = ((actionset->log != 1) || (is_initial_req == 0)) ? 4 : 1; + log_level = ((actionset->log != 1) || (is_initial_req != 1)) ? 4 : 1; /* Pause the request first (if configured and the initial request). */ if (actionset->intercept_pause && (is_initial_req == 1)) { diff --git a/rules/README b/rules/README index a11680e6..98a28508 100644 --- a/rules/README +++ b/rules/README @@ -1,179 +1,177 @@ - - -ModSecurity Core Rule Set -============================== - -(c) 2006 Breach Secuiry Inc. - -The ModSecurity Core Rule Set is provided to you under the terms and -conditions of GPL version 2 - -This directory contains the files for Core ModSecurity Rule Set -The rules are compatible with ModSecurity 2.1 (as of version 1.3.2) - - -Overview --------- - -Using ModSecurity requires rules. In order to enable users to take full -advantage of ModSecurity immediately, Breach Security Inc. is providing a free -Core rule set. Unlike intrusion detection and prevention systems which -rely on signature specific to known vulnerabilities, the Core Rule Set -provides generic protection from unknown vulnerabilities often found in web -application that are in most cases custom coded. - -Keep in mind that a predefined rule set is only part of the work required to -protect your web site. We strongly urge you to consult Ivan Ristic's book, -"Apache Security" in order to harden your Apache web server. You may also -consider writing custom rules for providing a positive security envelope to -your application or critical parts of it. Breach Security can provide you with -training and professional services to assist you in doing that. The Core -Rule Set is heavily commented to allow it to be used as a step-by-step -deployment guide for ModSecurity. - -For more information refer to the Core Rule Set page at -http://www.modsecurity.org/ - - -Core Rule Set Structure & Usage ------------------------------------- - -To activate the rules for your web server installation: - - 1) You may want to edit and customize modsecurity_crs_10_config.conf. - Additionally you may want to edit modsecurity_crs_30_http_policy.conf - which enforces an application specific HTTP protocol usage. - - 2) Add the following line to your httpd.conf (assuming - you've placed the rule files into conf/modsecurity/): - - Include conf/modsecurity/*.conf - - 3) Restart web server. - - 4) Make sure your web sites are still running fine. - - 5) Simulate an attack against the web server. Then check - the attack was correctly logged in the Apache error log, - ModSecurity debug log (if you enabled it) and ModSecurity - audit log (if you enabled it). - - 6) If you configured your audit log entries to be transported - to ModSecurity Console in real time, check the alert was - correctly recorded there too. - -About Regular Expressions -------------------------- - -One of the advantages of the Core Rule Set, being a set of text files is your -ability to modify it. However you will find that the regular expressions used -are very complex. - -Since regular expressions are much more efficient if assembled into a single -expression and optimized, a generation script takes a list of patterns that -are required for a rule and optimize them into a most efficient regular -expression. - -We plan to release the optimization script shortly to allow much easier editing -of regular expressions. - - -Core Rule Set Content --------------------------- - -In order to provide generic web applications protection, the Core Rule Set -uses the following techniques: - -1. HTTP protection - detecting violations of the HTTP protocol and a locally -defined usage policy. - -2. Common Web Attacks Protection - detecting common web application security -attack. - -3. Automation detection - Detecting bots, crawlers, scanners and other surface -malicious activity. - -4. Trojan Protection - Detecting access to Trojans horses. - -5. Errors Hiding - Disguising error messages sent by the server - -In addition the rule set also hints at the power of ModSecurity beyond -providing security by reporting access from the major search engines to your -site. - - -HTTP Protection - This first line of protection ensures that all abnormal HTTP -requests are detected. This line of defense eliminates a large number of -automated and non targeted attacks as well as protects the web server itself. -Common Web Attacks Protection Rules on the second level address the common web -application security attack methods. These are the issues that can appear in -any web application. Some of the issues addressed are: - -- SQL Injection -- Cross-Site Scripting (XSS) -- OS Command execution -- Remote code inclusion -- LDAP Injection -- SSI Injection -- Information leak -- Buffer overflows -- File disclosure - -Automation Detection - Automated clients are both a security risk and a -commercial risk. Automated crawlers collect information from your site, consume -bandwidth and might also search for vulnerabilities on the web site. Automation -detection is especially useful for generic detection of comments spam. - - -Trojan Protection - ModSecurity Core Rule Set detects access to back doors -installed on a web server. This feature is very important in a hosting -environment when some of this backdoors may be uploaded in a legitimate way and -used maliciously. In addition the Core Rule Set includes a hook for adding -an Anti-Virus program such as ClamAV for checking file uploads. - -Errors Hiding - If all fails, the Core Rule Set will detect errors sent by -the web server. Detecting and blocking errors prevents attackers from -collecting reconnaissance information about the web application and also server -as a last line of defense in case an attack was not detected eariler. - - -Few Word of Caution -------------------- - -As with every new technology, using the ModSecurity Core Rule Set requires some caution: - -- Every Rule Set can have false positive in new environments and any new -installation should initially use the log only Rule Set version or if no such -version is available, set ModSecurity to Detection only using the SecRuleEngine -DetectionOnly command. - -After running ModSecurity in a detection only mode for a while review the evens -generated and decide if any modification to the rule set should be made before -moving to protection mode. - -- Freely available wide spread signatures have their down side as attackers may -examine them and find ways to bypass them. Especially note that the automation -detection signatures are relatively easy to evade and should not be viewed as a -security mechanism but only as a "nuisance reduction" mechanism. - - -Road Map --------- - -This rule set is both young and old. Breach Security has a long experience with -rules and signatures for application security protection and the Core Rule -Set is based on this experience. On the other hand, this is a first cut of a -ModSecurity rule set so your feedback and remarks, either directly or through -the ModSecurity mailing list would be greatly appreciated. - -Going forward we plan to: - -- Utilize ModSecurity 2.0 support for events correlation to detect denial of -service attacks, brute force attacks and attack reconnaissance - -- Add a framework for validating SOAP requests. - -- Add signatures for key known vulnerabilities. - -Anything else you would want? - +ModSecurity Core Rule Set +============================== + +(c) 2006 Breach Secuiry Inc. + +The ModSecurity Core Rule Set is provided to you under the terms and +conditions of GPL version 2 + +This directory contains the files for Core ModSecurity Rule Set +The rules are compatible with ModSecurity 2.1 (as of version 1.3.2) + + +Overview +-------- + +Using ModSecurity requires rules. In order to enable users to take full +advantage of ModSecurity immediately, Breach Security Inc. is providing a free +Core rule set. Unlike intrusion detection and prevention systems which +rely on signature specific to known vulnerabilities, the Core Rule Set +provides generic protection from unknown vulnerabilities often found in web +application that are in most cases custom coded. + +Keep in mind that a predefined rule set is only part of the work required to +protect your web site. We strongly urge you to consult Ivan Ristic's book, +"Apache Security" in order to harden your Apache web server. You may also +consider writing custom rules for providing a positive security envelope to +your application or critical parts of it. Breach Security can provide you with +training and professional services to assist you in doing that. The Core +Rule Set is heavily commented to allow it to be used as a step-by-step +deployment guide for ModSecurity. + +For more information refer to the Core Rule Set page at +http://www.modsecurity.org/ + + +Core Rule Set Structure & Usage +------------------------------------ + +To activate the rules for your web server installation: + + 1) You may want to edit and customize modsecurity_crs_10_config.conf. + Additionally you may want to edit modsecurity_crs_30_http_policy.conf + which enforces an application specific HTTP protocol usage. + + 2) Add the following line to your httpd.conf (assuming + you've placed the rule files into conf/modsecurity/): + + Include conf/modsecurity/*.conf + + 3) Restart web server. + + 4) Make sure your web sites are still running fine. + + 5) Simulate an attack against the web server. Then check + the attack was correctly logged in the Apache error log, + ModSecurity debug log (if you enabled it) and ModSecurity + audit log (if you enabled it). + + 6) If you configured your audit log entries to be transported + to ModSecurity Console in real time, check the alert was + correctly recorded there too. + +About Regular Expressions +------------------------- + +One of the advantages of the Core Rule Set, being a set of text files is your +ability to modify it. However you will find that the regular expressions used +are very complex. + +Since regular expressions are much more efficient if assembled into a single +expression and optimized, a generation script takes a list of patterns that +are required for a rule and optimize them into a most efficient regular +expression. + +We plan to release the optimization script shortly to allow much easier editing +of regular expressions. + + +Core Rule Set Content +-------------------------- + +In order to provide generic web applications protection, the Core Rule Set +uses the following techniques: + +1. HTTP protection - detecting violations of the HTTP protocol and a locally +defined usage policy. + +2. Common Web Attacks Protection - detecting common web application security +attack. + +3. Automation detection - Detecting bots, crawlers, scanners and other surface +malicious activity. + +4. Trojan Protection - Detecting access to Trojans horses. + +5. Errors Hiding - Disguising error messages sent by the server + +In addition the rule set also hints at the power of ModSecurity beyond +providing security by reporting access from the major search engines to your +site. + + +HTTP Protection - This first line of protection ensures that all abnormal HTTP +requests are detected. This line of defense eliminates a large number of +automated and non targeted attacks as well as protects the web server itself. +Common Web Attacks Protection Rules on the second level address the common web +application security attack methods. These are the issues that can appear in +any web application. Some of the issues addressed are: + +- SQL Injection +- Cross-Site Scripting (XSS) +- OS Command execution +- Remote code inclusion +- LDAP Injection +- SSI Injection +- Information leak +- Buffer overflows +- File disclosure + +Automation Detection - Automated clients are both a security risk and a +commercial risk. Automated crawlers collect information from your site, consume +bandwidth and might also search for vulnerabilities on the web site. Automation +detection is especially useful for generic detection of comments spam. + + +Trojan Protection - ModSecurity Core Rule Set detects access to back doors +installed on a web server. This feature is very important in a hosting +environment when some of this backdoors may be uploaded in a legitimate way and +used maliciously. In addition the Core Rule Set includes a hook for adding +an Anti-Virus program such as ClamAV for checking file uploads. + +Errors Hiding - If all fails, the Core Rule Set will detect errors sent by +the web server. Detecting and blocking errors prevents attackers from +collecting reconnaissance information about the web application and also server +as a last line of defense in case an attack was not detected eariler. + + +Few Word of Caution +------------------- + +As with every new technology, using the ModSecurity Core Rule Set requires some caution: + +- Every Rule Set can have false positive in new environments and any new +installation should initially use the log only Rule Set version or if no such +version is available, set ModSecurity to Detection only using the SecRuleEngine +DetectionOnly command. + +After running ModSecurity in a detection only mode for a while review the evens +generated and decide if any modification to the rule set should be made before +moving to protection mode. + +- Freely available wide spread signatures have their down side as attackers may +examine them and find ways to bypass them. Especially note that the automation +detection signatures are relatively easy to evade and should not be viewed as a +security mechanism but only as a "nuisance reduction" mechanism. + + +Road Map +-------- + +This rule set is both young and old. Breach Security has a long experience with +rules and signatures for application security protection and the Core Rule +Set is based on this experience. On the other hand, this is a first cut of a +ModSecurity rule set so your feedback and remarks, either directly or through +the ModSecurity mailing list would be greatly appreciated. + +Going forward we plan to: + +- Utilize ModSecurity 2.0 support for events correlation to detect denial of +service attacks, brute force attacks and attack reconnaissance + +- Add a framework for validating SOAP requests. + +- Add signatures for key known vulnerabilities. + +Anything else you would want? + From a4835b73fffb47c27eeb00f7d50e769d8969e485 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 14 Jun 2007 18:46:58 +0000 Subject: [PATCH 035/372] Fix bad merge of mem pool fix from trunk. Update to latest core rules. --- ...odsecurity_crs_20_protocol_violations.conf | 158 ++++++++-------- ...modsecurity_crs_21_protocol_anomalies.conf | 105 ++++++----- .../modsecurity_crs_40_generic_attacks.conf | 177 ++++++++++-------- 3 files changed, 233 insertions(+), 207 deletions(-) diff --git a/rules/blocking/modsecurity_crs_20_protocol_violations.conf b/rules/blocking/modsecurity_crs_20_protocol_violations.conf index 484085af..e2c03e76 100644 --- a/rules/blocking/modsecurity_crs_20_protocol_violations.conf +++ b/rules/blocking/modsecurity_crs_20_protocol_violations.conf @@ -1,74 +1,84 @@ -# --------------------------------------------------------------- -# Core ModSecurity Rule Set -# Copyright (C) 2006 Breach Security Inc. All rights reserved. -# -# The ModSecuirty Core Rule Set is distributed under GPL version 2 -# Please see the enclosed LICENCE file for full details. -# --------------------------------------------------------------- - - -# -# TODO in some cases a valid client (usually automated) generates requests that -# violates the HTTP protocol. Create exceptions for those clients, but try -# to limit the exception to a source IP or other additional properties of -# the request such as URL and not allow the violation generally. -# -# - -# Use status code 400 response status code by default as protocol violations -# are in essence bad requests. -SecDefaultAction "log,pass,phase:1,status:400" - -# 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'" - -# 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'" -SecRule REQUEST_HEADERS:Content-Length "!^0?$" - -# 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',severity:'4'" -SecRule &REQUEST_HEADERS:Content-Length "@eq 0" - -# 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 HTTP_Transfer-Encoding "!^$" "deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',id:'960013',severity:'5'" - -# Check decodings -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "@validateUrlEncoding" \ - "chain, deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',id:'950107',severity:'4'" -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\%(?![0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" - -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',id:'950801',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 ^http:/ "deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',id:'960014'" - -# -# Restrict type of characters sent -# -# NOTE In order to be broad and support localized applications this rule -# only validates that NULL Is not used. -# -# The strict policy version also validates that protocol and application -# generated fields are limited to printable ASCII. -# -# TODO If your application use the range 32-126 for parameters. -# -SecRule REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer \ - "@validateByteRange 32-126" \ - "deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015',t:urlDecodeUni,phase:1" - -SecRule ARGS|ARGS_NAMES "@validateByteRange 1-255" \ - "deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960901',severity:'4',t:urlDecodeUni,phase:2" +# --------------------------------------------------------------- +# Core ModSecurity Rule Set +# Copyright (C) 2006 Breach Security Inc. All rights reserved. +# +# The ModSecuirty Core Rule Set is distributed under GPL version 2 +# Please see the enclosed LICENCE file for full details. +# --------------------------------------------------------------- + + +# +# TODO in some cases a valid client (usually automated) generates requests that +# violates the HTTP protocol. Create exceptions for those clients, but try +# to limit the exception to a source IP or other additional properties of +# the request such as URL and not allow the violation generally. +# +# + +# Use status code 400 response status code by default as protocol violations +# are in essence bad requests. +SecDefaultAction "log,pass,phase:2,status:400" + + +# Validate request line +SecRule REQUEST_LINE "!^[a-z]{3,10}\s*(?:http\:\/\/[\w\-\.\/]*)??\/[\w\-\.\/]*(?:\?[\S]*)??\s*http\/[01]\.[901]$" \ + "t:none,t:lowercase,deny,log,auditlog,status:400,msg:'Invalid HTTP Request Line',,id:'960911',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'," + +# 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'," +SecRule REQUEST_HEADERS:Content-Length "!^0?$" + +# 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',severity:'4'" +SecRule &REQUEST_HEADERS:Content-Length "@eq 0" + +# 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',severity:'3'" + +# Check decodings +SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUrlEncoding" \ + "chain, deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',,id:'950107',severity:'4'" +SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\%(?!$|[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" + +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',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'" + +# 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 ^http:/ "deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',,id:'960014'," + +# +# Restrict type of characters sent +# +# NOTE In order to be broad and support localized applications this rule +# only validates that NULL Is not used. +# +# The strict policy version also validates that protocol and application +# generated fields are limited to printable ASCII. +# +# TODO If your application use the range 32-126 for parameters. +# +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',severity:'4',t:urlDecodeUni,phase:1" + +SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS:Referer "@validateByteRange 1-255" \ + "deny,log,auditlog,status:400,msg:'Invalid character in request',,id:'960901',severity:'4',t:urlDecodeUni,phase:2" diff --git a/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf b/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf index 03084e36..448b9a8b 100644 --- a/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf +++ b/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf @@ -1,50 +1,55 @@ -# --------------------------------------------------------------- -# Core ModSecurity Rule Set -# Copyright (C) 2006 Breach Security Inc. All rights reserved. -# -# The ModSecuirty Core Rule Set is distributed under GPL version 2 -# Please see the enclosed LICENCE file for full details. -# --------------------------------------------------------------- - - -# -# TODO in some cases a valid client (usually automated) generates requests that -# violates the HTTP protocol. Create exceptions for those clients, but try -# to limit the exception to a source IP or other additional properties of -# the request such as URL and not allow the violation generally. -# - -# Use status code 400 response status code by default as protocol violations -# are in essence bad requests. -SecDefaultAction "log,pass,phase:1,status:400" - -# Do not accept requests without common headers. -# -# Implies either an attacker or a legitimate automation client. -# -SecRule REQUEST_URI "^/$" "chain,skip:4" -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" \ - "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',severity:'4'" -SecRule REQUEST_HEADERS:Host "^$" \ - "deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',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'" -SecRule REQUEST_METHOD "!OPTIONS" -SecRule REQUEST_HEADERS:Accept "^$" \ - "chain,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015'" -SecRule REQUEST_METHOD "!OPTIONS" - -SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ - "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',severity:'4'" -SecRule REQUEST_HEADERS:User-Agent "^$" \ - "deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',severity:'4'" - - -# 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'" +# --------------------------------------------------------------- +# Core ModSecurity Rule Set +# Copyright (C) 2006 Breach Security Inc. All rights reserved. +# +# The ModSecuirty Core Rule Set is distributed under GPL version 2 +# Please see the enclosed LICENCE file for full details. +# --------------------------------------------------------------- + + +# +# TODO in some cases a valid client (usually automated) generates requests that +# violates the HTTP protocol. Create exceptions for those clients, but try +# to limit the exception to a source IP or other additional properties of +# the request such as URL and not allow the violation generally. +# + +# Use status code 400 response status code by default as protocol violations +# are in essence bad requests. +SecDefaultAction "log,pass,phase:2,status:400" + +# Do not accept requests without common headers. +# +# Implies either an attacker or a legitimate automation client. +# +SecRule REQUEST_URI "^/$" "chain,skip:4" +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" \ + "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',,id:'960008',severity:'4'" +SecRule REQUEST_HEADERS:Host "^$" \ + "deny,log,auditlog,status:400,msg:'Request Missing a Host Header',,id:'960008',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'," +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'," +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',severity:'4'" +SecRule REQUEST_HEADERS:User-Agent "^$" \ + "deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',,id:'960009',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$" + + +# 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'," diff --git a/rules/blocking/modsecurity_crs_40_generic_attacks.conf b/rules/blocking/modsecurity_crs_40_generic_attacks.conf index dc3a9e7e..2d0b013d 100644 --- a/rules/blocking/modsecurity_crs_40_generic_attacks.conf +++ b/rules/blocking/modsecurity_crs_40_generic_attacks.conf @@ -1,83 +1,94 @@ -# --------------------------------------------------------------- -# Core ModSecurity Rule Set -# Copyright (C) 2006 Breach Security Inc. All rights reserved. -# -# The ModSecuirty Core Rule Set is distributed under GPL version 2 -# Please see the enclosed LICENCE file for full details. -# --------------------------------------------------------------- - - -# -# TODO While some of the pattern groups such as command injection are usually -# safe of false positives, other pattern groups such as SQL injection and -# XSS may require setting exceptions and therefore are set to log only by -# default. -# -# Start ModSecurity in monitoring only mode and check whether your -# application requires exceptions for a specific URL, Pattern or source IP -# before moving to blocking mode. - -SecDefaultAction "log,pass,phase:2,status:500,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase" - -# Session fixation -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "(?:\.cookie\b.*?;\W*?(?:expires|domain)\W*?=|\bhttp-equiv\W+set-cookie\b)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Session Fixation. Matched signature <%{TX.0}>',id:'950009',severity:'2'" - -# Blind SQL injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!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)|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',id:'950007',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\b(?:benchmark|encode)\b" \ -# "chain,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',id:'950903',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "[\\(\)\%#]\|--" -SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|!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:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',id:'950904',severity:'2'" - -# SQL injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!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|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r|autonomous_transaction|open(?:rowset|query)|dbms_java)\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*?\()|(?:having|or|and)\b\s+?(?:\d{1,10}|'[^=]{1,10}')\s*?[=<>]+|(?:print\]\b\W*?\@|root)\@|c(?:ast\b\W*?\(|oalesce\b))|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',id:'950001',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',id:'950905',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "[\\(\)\%#]\|--" -SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|!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:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',id:'950906',severity:'2'" - -# XSS -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "(?:\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)|ivescript)|(?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha):|type\b\W*?\b(?:text\b(?:\W*?\b(?:j(?:ava)?|ecma)script\b| [vbscript])|application\b\W*?\bx-(?:java|vb)script\b)|s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b(?:(?:java|vb)script|shell|http):)|(?:c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder)\b|a(?:ctivexobject\b|lert\b\W*?\())|<(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\\btype\b\W*?\bimage)\b|!\[CDATA\[|script|meta)|(?:\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack. Matched signature <%{TX.0}>',id:'950004',severity:'2'" - -# file injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "(?:\b(?:\.(?:ht(?:access|passwd|group)|www_?acl)|global\.asa|httpd\.conf|boot\.ini)\b|\/etc\/)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt. Matched signature <%{TX.0}>',id:'950005',severity:'2'" - -# Command access -SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Access. Matched signature <%{TX.0}>',id:'950002',severity:'2'" - -# Command injection -SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS "(?:\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|c)|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|c)|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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection. Matched signature <%{TX.0}>',id:'950006',severity:'2'" -SecRule "ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:User-Agent" \ - "\bwget\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection. Matched signature <%{TX.0}>',id:'950907',severity:'2'" - -# Coldfusion injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "\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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags. Matched signature <%{TX.0}>',id:'950008',severity:'2'" - -# LDAP injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack. Matched signature <%{TX.0}>',id:'950010',severity:'2'" - -# SSI injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS " - \ No newline at end of file + diff --git a/doc/html.xsl b/doc/html.xsl index 5c774adc..41e58ed8 100644 --- a/doc/html.xsl +++ b/doc/html.xsl @@ -8,7 +8,7 @@ - + @@ -17,4 +17,4 @@ modsecurity-reference.css - \ No newline at end of file + From c8e5c7fcd5ba9ad3e376d8282bef21b8ddb001b8 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Sep 2007 21:00:56 +0000 Subject: [PATCH 109/372] Sync trunk from branches/2.1.x (merge in branch fixes). --- apache2/Makefile | 4 ++-- apache2/apache2_io.c | 4 ++++ apache2/mod_security2.c | 14 +++++++++----- apache2/msc_reqbody.c | 11 +++++------ apache2/msc_util.c | 8 ++++---- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/apache2/Makefile b/apache2/Makefile index 1a0a3b07..07f5e7bc 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -28,10 +28,10 @@ APXS = apxs APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 -#DEFS = -DWITH_LIBXML2 +DEFS = -DWITH_LIBXML2 +#DEFS = -DWITH_LIBXML2 -DNO_MODSEC_API #DEFS = -DWITH_LIBXML2 -DDEBUG_CONF #DEFS = -DWITH_LIBXML2 -DCACHE_DEBUG -DEFS = -DWITH_LIBXML2 -DNO_MODSEC_API #LIBS = -Lmy/lib/dir -lmylib CFLAGS = -O2 -g -Wuninitialized -Wall -Wmissing-prototypes -Wshadow -Wunused-variable -Wunused-value -Wchar-subscripts -Wsign-compare diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index b1ea0d27..ad54ca05 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -38,6 +38,8 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, return APR_EGENERAL; } + msr->r = f->r; + if ((msr->if_status == IF_STATUS_COMPLETE)||(msr->if_status == IF_STATUS_NONE)) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %x, r %x).", f, f->r); @@ -394,6 +396,8 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } + msr->r = r; + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Output filter: Receiving output (f %x, r %x).", f, f->r); } diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index eb10cce8..caa5da43 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -237,13 +237,14 @@ int perform_interception(modsec_rec *msr) { * Retrieves a previously stored transaction context by * looking at the main request, and the previous requests. */ -static modsec_rec *retrieve_tx_context(const request_rec *r) { +static modsec_rec *retrieve_tx_context(request_rec *r) { modsec_rec *msr = NULL; request_rec *rx = NULL; /* Look in the current request first. */ msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR); if (msr != NULL) { + msr->r = r; return msr; } @@ -251,6 +252,7 @@ static modsec_rec *retrieve_tx_context(const request_rec *r) { if (r->main != NULL) { msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); if (msr != NULL) { + msr->r = r; return msr; } } @@ -260,6 +262,7 @@ static modsec_rec *retrieve_tx_context(const request_rec *r) { while(rx != NULL) { msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); if (msr != NULL) { + msr->r = r; return msr; } rx = rx->prev; @@ -588,8 +591,6 @@ static int hook_request_late(request_rec *r) { */ return DECLINED; } - msr->r = r; - msr->remote_user = r->user; /* Has this phase been completed already? */ if (msr->phase_request_body_complete) { @@ -604,6 +605,8 @@ static int hook_request_late(request_rec *r) { } msr->phase_request_body_complete = 1; + msr->remote_user = r->user; + /* Get the second configuration context. */ msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config, &security2_module); @@ -703,7 +706,7 @@ static void hook_error_log(const char *file, int line, int level, apr_status_t s error_message *em = NULL; if (r == NULL) return; - msr = retrieve_tx_context(r); + msr = retrieve_tx_context((request_rec *)r); /* Create a context for requests we never had the chance to process */ if ((msr == NULL) @@ -719,7 +722,7 @@ static void hook_error_log(const char *file, int line, int level, apr_status_t s msr_log(msr, 9, "Context created after request failure."); } } - } + } if (msr == NULL) return; @@ -909,6 +912,7 @@ static void hook_insert_filter(request_rec *r) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping."); } + return; } diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 053dbccd..111aabdb 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -54,7 +54,7 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr) { * to allocate structures from (not data, which is allocated * via malloc). */ - apr_pool_create(&msr->msc_reqbody_mp, msr->mp); + apr_pool_create(&msr->msc_reqbody_mp, NULL); /* Initialise request body processors, if any. */ @@ -624,11 +624,10 @@ apr_status_t modsecurity_request_body_clear(modsec_rec *msr) { } } - /* NOTE No need to clear the pool as it has already been destroyed - * if (msr->msc_reqbody_mp != NULL) { - * apr_pool_clear(msr->msc_reqbody_mp); - * } - */ + if (msr->msc_reqbody_mp != NULL) { + apr_pool_destroy(msr->msc_reqbody_mp); + msr->msc_reqbody_mp = NULL; + } return 1; } diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 9c5aaece..d98d061c 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -597,7 +597,7 @@ int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) { - *d += 0x20; + (*d) += 0x20; } d++; @@ -786,7 +786,7 @@ int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input while((j < input_len)&&(isxdigit(input[j]))) j++; if (j > k) { /* Do we have at least one digit? */ /* Decode the entity. */ - char *x = apr_pstrmemdup(mp, (const char*)&input[k], j - k); + char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); *d++ = (unsigned char)strtol(x, NULL, 16); count++; @@ -804,7 +804,7 @@ int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input while((j < input_len)&&(isdigit(input[j]))) j++; if (j > k) { /* Do we have at least one digit? */ /* Decode the entity. */ - char *x = apr_pstrmemdup(mp, (const char*)&input[k], j - k); + char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); *d++ = (unsigned char)strtol(x, NULL, 10); count++; @@ -823,7 +823,7 @@ int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input k = j; while((j < input_len)&&(isalnum(input[j]))) j++; if (j > k) { /* Do we have at least one digit? */ - char *x = apr_pstrmemdup(mp, (const char*)&input[k], j - k); + char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); /* Decode the entity. */ if (strcasecmp(x, "quot") == 0) *d++ = '"'; From 53011819d470c22e73c34c274e6ee18c89c8f3b7 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Sep 2007 21:41:34 +0000 Subject: [PATCH 110/372] Cleanup some doc formatting. Prepare trunk for use as 2.5.0-devN tree. --- CHANGES | 29 +++++++++++------ apache2/modsecurity.h | 4 +-- doc/modsecurity2-apache-reference.xml | 47 ++++++++++++++------------- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index 47e31409..6114d98c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,14 +1,8 @@ -?? ??? 2007 - 2.5.0-trunk -------------------------- +?? ??? 2007 - 2.5.0-dev3 +------------------------ * Used new API calls to get the server version/banner when available. - * Allow mod_rpaf and mod_extract_forwarded2 to work before ModSecurity. - - * Quiet some compiler warnings. - - * Added ability to compile without an external API (-DNO_MODSEC_API). - * Added "logdata" meta action to allow safe logging of raw transaction data. * Added TX_SEVERITY that keeps track of the highest severity @@ -35,8 +29,23 @@ visibility attribute. -27 July 2007 - 2.1.2 --------------------- +11 Sep 2007 - 2.1.3 +------------------- + + * Updated multipart parsing code adding variables to allow checking + for various parsing issues (request body abnormalities). + + * Allow mod_rpaf and mod_extract_forwarded2 to work before ModSecurity. + + * Quiet some compiler warnings. + + * Do not block internal ErrorDocument requests after blocking request. + + * Added ability to compile without an external API (use -DNO_MODSEC_API). + + +27 Jul 2007 - 2.1.2 +------------------- * Cleaned up and clarified some documentation. diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 6afadc77..4daa9bca 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -58,8 +58,8 @@ 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 "trunk" -#define MODSEC_VERSION_RELEASE "99" +#define MODSEC_VERSION_TYPE "dev" +#define MODSEC_VERSION_RELEASE "3" #define MODULE_NAME "ModSecurity" #define MODULE_RELEASE \ diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 85dc4c75..3e078508 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-trunk / (September 7, 2007) + Version 2.5.0-dev3 / (September 14, 2007) 2004-2007 @@ -666,9 +666,9 @@ SecAuditLogStorageDir logs/audit I - This part is a replacement for part C. It will log the same data as C in all cases - except whenmultipart/form-data + except when multipart/form-data encoding in used. In this case it will log a fake application/x-www-form-urlencoded body + moreinfo="none">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. @@ -677,7 +677,7 @@ SecAuditLogStorageDir logs/audit J - RESERVED. This part, when implemented, will contain information about the files uploaded - using multipart/form-data encoding. + using multipart/form-data encoding. @@ -1910,16 +1910,17 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1 - application/x-www-form-urlencoded - used to transfer form - data - - - - multipart/form-data - used for file transfers - - - - text/xml - used for passing XML data + application/x-www-form-urlencoded - used to + transfer form data + + + + multipart/form-data - used for file + transfers + + + + text/xml - used for passing XML data @@ -3694,7 +3695,7 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ - 1 - 99,999; reserved for local (internal) use. Use as you see + 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. @@ -4730,10 +4731,11 @@ SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1} + 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 @@ -4804,8 +4806,9 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd 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. + multipart/form-data encoding (file upload) is used. + It is not necessary to do so because URL encoding is not used for this + encoding.
@@ -4970,4 +4973,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}
- \ No newline at end of file + From ad940d1ff981156d9c428e1431c58fd00bcd7048 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Sep 2007 23:01:58 +0000 Subject: [PATCH 111/372] Partially corrected the filter error code. See #3. --- CHANGES | 3 +++ apache2/apache2_io.c | 26 ++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 6114d98c..ca265d31 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * Return from the output filter with an error in addition to setting + up the HTTP error status in the output data. + * Used new API calls to get the server version/banner when available. * Added "logdata" meta action to allow safe logging of raw transaction data. diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index ad54ca05..9d232476 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -260,14 +260,31 @@ static apr_status_t send_error_bucket(ap_filter_t *f, int status) { brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); if (brigade == NULL) return APR_EGENERAL; + bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc); if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(brigade, bucket); - bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; + APR_BRIGADE_INSERT_TAIL(brigade, bucket); - return ap_pass_brigade(f->next, brigade); + bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); + if (bucket == NULL) return APR_EGENERAL; + + APR_BRIGADE_INSERT_TAIL(brigade, bucket); + + ap_pass_brigade(f->next, brigade); + + // TODO: Should return an error if this function failed, but currently + // coded to pass this on as the filter return value. The calling code + // needs changed to return an error after checking this return value + // and possibly generating a log entry. + // + // Also note that ap_pass_brigade will return APR_SUCCESS, so we should + // not pass this on to be returned by the filter on error. Although + // it may not matter what we return from the filter as it may be too + // late to even generate an error (already sent to client). Nick Kew + // recommends to return APR_EGENERAL in hopes that the handler in control + // will notice and do The Right Thing. So, that is what we do now. + return APR_EGENERAL; } /** @@ -596,6 +613,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } // TODO Why does the function below take pointer to length? Will it modify it? + // BR: Yes - The maximum length of the char array. On return, it is the actual length of the char array. rc = apr_brigade_flatten(msr->of_brigade, msr->resbody_data, &msr->resbody_length); if (rc != APR_SUCCESS) { msr_log(msr, 1, "Output filter: Failed to flatten brigade (%i): %s", rc, From b217e42624edb88d654cb5a73d6b06a2fc5ee135 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 17 Sep 2007 17:10:38 +0000 Subject: [PATCH 112/372] Merge in fix for ErrorDocument. --- apache2/mod_security2.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index caa5da43..02bd951e 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -594,13 +594,25 @@ static int hook_request_late(request_rec *r) { /* Has this phase been completed already? */ if (msr->phase_request_body_complete) { + /* If we are redirecting and there was no previous response it is + * an error page request and we ignore it. + */ + if ( (msr->r->prev != NULL) + && ((msr->r->prev->headers_out == NULL) || (apr_is_empty_table(msr->r->prev->headers_out))) ) + { + msr_log(msr, 9, "Allowing internally redirected error document: %s", msr->r->uri); + return DECLINED; + } + if (msr->was_intercepted) { - msr_log(msr, 4, "Phase REQUEST_BODY request already intercepted. Intercepting additional request."); + msr_log(msr, 4, "Phase REQUEST_BODY request already intercepted. Intercepting additional request."); return perform_interception(msr); } + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Phase REQUEST_BODY already complete, skipping."); } + return DECLINED; } msr->phase_request_body_complete = 1; From eb6b456f5be4f16e168deeeb7a9ba45a2a9cda18 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 21 Sep 2007 00:20:31 +0000 Subject: [PATCH 113/372] Fix potential buffer overrun by 1 byte in base64Decode caused by bad docs from APR-Util. See #255. --- CHANGES | 3 +++ apache2/re_tfns.c | 3 +-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index ca265d31..b7317995 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * Fixed potential corruption at end of strings after using base64Decode + (APR-Util issue). TODO make a better CHANGELOG entry ;) + * Return from the output filter with an error in addition to setting up the HTTP error status in the output data. diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 97a4664b..be0d5711 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -311,8 +311,7 @@ static int msre_fn_base64Decode_execute(apr_pool_t *mptmp, unsigned char *input, { *rval_len = apr_base64_decode_len((const char *)input); /* returns len with NULL byte included */ *rval = apr_palloc(mptmp, *rval_len); - apr_base64_decode(*rval, (const char *)input); - (*rval_len)--; + *rval_len = apr_base64_decode(*rval, (const char *)input); return 1; } From 2a707d43706d51545ef9d1051ab21776932c664c Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Sep 2007 19:06:54 +0000 Subject: [PATCH 114/372] Enable our output filters to intercept bodies of error responses (#65). --- apache2/mod_security2.c | 21 ++++++++++++--------- apache2/modsecurity.h | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 02bd951e..b87791ef 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -950,7 +950,6 @@ static void hook_insert_filter(request_rec *r) { } } -#if 0 /** * Invoked whenever Apache starts processing an error. A chance * to insert ourselves into the output filter chain. @@ -961,25 +960,30 @@ static void hook_insert_error_filter(request_rec *r) { /* Find the transaction context and make sure we are * supposed to proceed. */ - - /* TODO Insert filter but make a note that it's the error - * response the filter would be receiving. - */ - msr = retrieve_tx_context(r); if (msr == NULL) return; + /* Do not run if not enabled. */ if (msr->txcfg->is_enabled == 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Hook insert_error_filter: Processing disabled, skipping."); } return; } - + + /* Do not run if the output filter already completed. This will + * happen if we intercept in phase 4. + */ if (msr->of_status != OF_STATUS_COMPLETE) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %x).", r); } + + /* Make a note that the output we will be receiving is a + * result of error processing. + */ + msr->of_is_error = 1; + ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); } else { if (msr->txcfg->debuglog_level >= 4) { @@ -987,7 +991,6 @@ static void hook_insert_error_filter(request_rec *r) { } } } -#endif #if (!defined(NO_MODSEC_API)) /** @@ -1083,7 +1086,7 @@ static void register_hooks(apr_pool_t *mp) { /* Filter hooks */ ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST); - /* ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); */ + ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST); ap_register_input_filter("MODSECURITY_IN", input_filter, NULL, AP_FTYPE_CONTENT_SET); diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 4daa9bca..88a053fa 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -213,6 +213,7 @@ struct modsec_rec { unsigned int of_status; unsigned int of_done_reading; unsigned int of_skipping; + unsigned int of_is_error; unsigned int resbody_status; apr_size_t resbody_length; From dfe09ff1b029c200a53f62371e2edf6e03c37879 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Sep 2007 19:36:57 +0000 Subject: [PATCH 115/372] Fix content injection C++ style comments. --- apache2/apache2_io.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 9d232476..d1b0d48c 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -467,7 +467,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { break; } - // If injecting content unset headers now. + /* If injecting content unset headers now. */ if (msr->txcfg->content_injection_enabled == 0) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Content Injection: Not enabled."); @@ -489,7 +489,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } } - // Content injection (prepend & non-buffering). + /* Content injection (prepend & non-buffering). */ if (msr->txcfg->content_injection_enabled && msr->content_prepend && msr->of_skipping) { apr_bucket *bucket_ci = apr_bucket_heap_create(msr->content_prepend, msr->content_prepend_len, NULL, f->r->connection->bucket_alloc); @@ -548,7 +548,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (APR_BUCKET_IS_EOS(bucket)) { eos_bucket = bucket; - // Inject content (append & non-buffering). + /* Inject content (append & non-buffering). */ if (msr->txcfg->content_injection_enabled && msr->content_append && msr->of_skipping) { apr_bucket *bucket_ci = NULL; @@ -639,7 +639,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr->of_skipping == 0) { record_time_checkpoint(msr, 3); - // Inject content into response (prepend & buffering). + /* Inject content into response (prepend & buffering). */ if (msr->txcfg->content_injection_enabled && msr->content_prepend && (!msr->of_skipping)) { apr_bucket *bucket_ci = NULL; @@ -653,7 +653,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } } - // Inject content into response (append & buffering). + /* Inject content into response (append & buffering). */ if (msr->txcfg->content_injection_enabled && msr->content_append && (!msr->of_skipping)) { apr_bucket *bucket_ci = NULL; From 79ee3a6a7984f72557db121a25949e91658af6e1 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Sep 2007 19:46:53 +0000 Subject: [PATCH 116/372] Process debug log statements only if the debug log level is sufficiently high. --- apache2/msc_multipart.c | 112 +++++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index b935c13e..199e33f0 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -142,15 +142,21 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) if (strcmp(name, "name") == 0) { if (msr->mpd->mpp->name != NULL) return -14; msr->mpd->mpp->name = value; - msr_log(msr, 9, "Multipart: Content-Disposition name: %s", - log_escape_nq(msr->mp, 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) { if (msr->mpd->mpp->filename != NULL) return -15; msr->mpd->mpp->filename = value; - msr_log(msr, 9, "Multipart: Content-Disposition filename: %s", - log_escape_nq(msr->mp, value)); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Content-Disposition filename: %s", + log_escape_nq(msr->mp, value)); + } } else return -11; @@ -267,9 +273,11 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { new_value = apr_pstrcat(msr->mp, header_value, " ", new_value, NULL); apr_table_set(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name, new_value); - msr_log(msr, 9, "Multipart: Continued folder header \"%s\" with \"%s\"", - log_escape(msr->mp, msr->mpd->mpp->last_header_name), - log_escape(msr->mp, data)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Continued folder header \"%s\" with \"%s\"", + log_escape(msr->mp, msr->mpd->mpp->last_header_name), + log_escape(msr->mp, data)); + } if (strlen(new_value) > 4096) { *error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long."); @@ -306,9 +314,11 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { apr_table_setn(msr->mpd->mpp->headers, header_name, header_value); msr->mpd->mpp->last_header_name = header_name; - msr_log(msr, 9, "Multipart: Added part header \"%s\" \"%s\"", - log_escape(msr->mp, header_name), - log_escape(msr->mp, header_value)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added part header \"%s\" \"%s\"", + log_escape(msr->mp, header_name), + log_escape(msr->mp, header_value)); + } } } @@ -375,8 +385,10 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { return -1; } - msr_log(msr, 4, "Multipart: Created temporary file: %s", - log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Created temporary file: %s", + log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name)); + } } /* write the reserve first */ @@ -433,8 +445,10 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { *(value_part_t **)apr_array_push(msr->mpd->mpp->value_parts) = value_part; - msr_log(msr, 9, "Multipart: Added data to variable: %s", - log_escape_nq_ex(msr->mp, value_part->data, value_part->length)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added data to variable: %s", + log_escape_nq_ex(msr->mp, value_part->data, value_part->length)); + } } else { *error_msg = apr_psprintf(msr->mp, "Multipart: unknown part type %i", msr->mpd->mpp->type); @@ -505,17 +519,22 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err /* add the part to the list of parts */ *(multipart_part **)apr_array_push(msr->mpd->parts) = msr->mpd->mpp; if (msr->mpd->mpp->type == MULTIPART_FILE) { - msr_log(msr, 9, "Multipart: Added file part %x to the list: name \"%s\" " - "file name \"%s\" (offset %u, length %u)", - msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), - log_escape(msr->mp, msr->mpd->mpp->filename), - msr->mpd->mpp->offset, msr->mpd->mpp->length); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added file part %x to the list: name \"%s\" " + "file name \"%s\" (offset %u, length %u)", + msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), + log_escape(msr->mp, msr->mpd->mpp->filename), + msr->mpd->mpp->offset, msr->mpd->mpp->length); + } } else { - msr_log(msr, 9, "Multipart: Added part %x to the list: name \"%s\" " - "(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), - msr->mpd->mpp->offset, msr->mpd->mpp->length); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Added part %x to the list: name \"%s\" " + "(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), + msr->mpd->mpp->offset, msr->mpd->mpp->length); + } } + msr->mpd->mpp = NULL; } @@ -745,9 +764,11 @@ int multipart_init(modsec_rec *msr, char **error_msg) { return -1; } - msr_log(msr, 9, "Multipart: Boundary%s: %s", - (msr->mpd->flag_boundary_quoted ? " (quoted)" : ""), - log_escape_nq(msr->mp, msr->mpd->boundary)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Multipart: Boundary%s: %s", + (msr->mpd->flag_boundary_quoted ? " (quoted)" : ""), + log_escape_nq(msr->mp, msr->mpd->boundary)); + } if (strlen(msr->mpd->boundary) == 0) { msr->mpd->flag_error = 1; @@ -807,7 +828,11 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, if (msr->mpd->is_complete) { msr->mpd->flag_data_before = 1; - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %i bytes)", size); + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %i bytes)", size); + } + return 1; } @@ -955,7 +980,10 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, if (processed_as_boundary == 0) { if (msr->mpd->mpp == NULL) { msr->mpd->flag_data_before = 1; - msr_log(msr, 4, "Multipart: Ignoring data before first boundary."); + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Ignoring data before first boundary."); + } } else { if (msr->mpd->mpp_state == 0) { if ((msr->mpd->bufleft == 0)||(process_buffer)) { @@ -998,7 +1026,11 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, if ((msr->mpd->is_complete)&&(inleft != 0)) { msr->mpd->flag_data_after = 1; - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%i bytes left)", inleft); + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%i bytes left)", inleft); + } + return 1; } } @@ -1014,7 +1046,9 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { if (msr->mpd == NULL) return -1; - msr_log(msr, 4, "Multipart: Cleanup started (remove files %i).", msr->upload_remove_files); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Cleanup started (remove files %i).", msr->upload_remove_files); + } if (msr->upload_remove_files == 0) { if (msr->txcfg->upload_dir == NULL) { @@ -1043,8 +1077,10 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { 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)); } else { - msr_log(msr, 4, "Multipart: Deleted file (part) \"%s\"", - log_escape(msr->mp, parts[i]->tmp_file_name)); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Deleted file (part) \"%s\"", + log_escape(msr->mp, parts[i]->tmp_file_name)); + } } } } @@ -1063,8 +1099,10 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { 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)); } else { - msr_log(msr, 4, "Multipart: Deleted empty file (part) \"%s\"", - log_escape(msr->mp, parts[i]->tmp_file_name)); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Multipart: Deleted empty file (part) \"%s\"", + log_escape(msr->mp, parts[i]->tmp_file_name)); + } } } } else { @@ -1087,9 +1125,11 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { log_escape(msr->mp, new_filename)); return -1; } else { - msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".", - log_escape(msr->mp, parts[i]->tmp_file_name), - log_escape(msr->mp, new_filename)); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".", + log_escape(msr->mp, parts[i]->tmp_file_name), + log_escape(msr->mp, new_filename)); + } } } } From 59333a6a817ea4805d00a50e5f50ecb3c9003268 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Sep 2007 22:15:12 +0000 Subject: [PATCH 117/372] Update CHANGES. --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index b7317995..b5c9aebf 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,13 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * ModSecurity will now process phases 3 and 4 when request processing + is interrupted (either by Apache - e.g. by responding with 400, 401 + or 403, or by ModSecurity itself). + + * Fix the base64decode transformation function to not return extra + characters at the end. + * Fixed potential corruption at end of strings after using base64Decode (APR-Util issue). TODO make a better CHANGELOG entry ;) From 9ed3cf9e5ad14859df597a1be30dc55e00c55381 Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Sep 2007 23:23:11 +0000 Subject: [PATCH 118/372] Added support for partial response body processing. --- CHANGES | 9 +- apache2/apache2_config.c | 25 ++++ apache2/apache2_io.c | 297 +++++++++++++++++++++++++++------------ apache2/modsecurity.h | 5 + 4 files changed, 245 insertions(+), 91 deletions(-) diff --git a/CHANGES b/CHANGES index b5c9aebf..277ad203 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,14 @@ + ?? ??? 2007 - 2.5.0-dev3 ------------------------ - * ModSecurity will now process phases 3 and 4 when request processing + * Enable ModSecurity to look at partial response bodies. In previous + versions ModSecurity would respond with status code 500 when the + response body was too long. Now, if SecResponseBodyLimitAction is + set to "ProcessPartial", it will process the part of the response + body received up until that point but send the rest without buffering. + + * ModSecurity will now process phases 3 and 4 even when request processing is interrupted (either by Apache - e.g. by responding with 400, 401 or 403, or by ModSecurity itself). diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index d2d3d317..5b0eeee8 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -43,6 +43,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->debuglog_fd = NOT_SET_P; dcfg->of_limit = NOT_SET; + dcfg->of_limit_action = NOT_SET; dcfg->of_mime_types = NOT_SET_P; dcfg->of_mime_types_cleared = NOT_SET; @@ -216,6 +217,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { merged->of_limit = (child->of_limit == NOT_SET ? parent->of_limit : child->of_limit); + merged->of_limit_action = (child->of_limit_action == NOT_SET + ? parent->of_limit_action : child->of_limit_action); if (child->of_mime_types != NOT_SET_P) { /* Child added to the table */ @@ -423,6 +426,7 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0; if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; + if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; if (dcfg->of_mime_types == NOT_SET_P) { dcfg->of_mime_types = apr_table_make(dcfg->mp, 3); @@ -980,6 +984,19 @@ static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, const ch return NULL; } +static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, const char *p1) { + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL; + else + if (strcasecmp(p1, "Reject") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; + else + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimitAction: %s", p1); + + return NULL; +} + static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, const char *_p1) { directory_config *dcfg = (directory_config *)_dcfg; char *p1 = apr_pstrdup(cmd->pool, _p1); @@ -1502,6 +1519,14 @@ const command_rec module_directives[] = { "" // TODO ), + AP_INIT_TAKE1 ( + "SecResponseBodyLimitAction", + cmd_response_body_limit_action, + NULL, + CMD_SCOPE_ANY, + "" // TODO + ), + AP_INIT_ITERATE ( "SecResponseBodyMimeType", cmd_response_body_mime_type, diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index d1b0d48c..1b3c5a9e 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -300,13 +300,14 @@ static int output_filter_should_run(modsec_rec *msr, request_rec *r) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Output filter: Response body buffering is not enabled."); } + return 0; } /* Check MIME type. */ if ((msr->txcfg->of_mime_types == NULL)||(msr->txcfg->of_mime_types == NOT_SET_P)) { - msr_log(msr, 1, "Output filter: MIME type structures are corrupted (internal error)."); + msr_log(msr, 1, "Output filter: MIME type structures corrupted (internal error)."); return -1; } @@ -362,13 +363,21 @@ static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, if (rc < 0) return -1; if (rc == 0) return 0; + /* Do not check the output limit if we are willing to + * process partial response bodies. + */ + + if (msr->txcfg->of_limit_action == RESPONSE_BODY_LIMIT_ACTION_PARTIAL) { + return 1; + } + /* Look up the Content-Length header to see if we know * the amount of data coming our way. If we do and if * it's too much we might want to stop processing right here. */ s_content_length = apr_table_get(r->headers_out, "Content-Length"); if (s_content_length == NULL) { - /* Try this too, mod_cgi seems to put headers there */ + /* Try this too, mod_cgi seems to put headers there. */ s_content_length = apr_table_get(r->err_headers_out, "Content-Length"); } @@ -377,26 +386,111 @@ static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, len = strtol(s_content_length, NULL, 10); if ((len == LONG_MIN)||(len == LONG_MAX)||(len < 0)||(len >= 1073741824)) { - msr_log(msr, 1, "Output filter: Invalid Content-Length: %s", log_escape_nq(r->pool, (char *)s_content_length)); - return -1; + msr_log(msr, 1, "Output filter: Invalid Content-Length: %s", log_escape_nq(r->pool, + (char *)s_content_length)); + return -1; /* Invalid. */ } if (len == 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Output filter: Skipping response since Content-Length is zero."); } + return 0; } if (len > msr->txcfg->of_limit) { - msr_log(msr, 1, "Output filter: Content-Length (%s) over the limit (%lu).", log_escape_nq(r->pool, (char *)s_content_length), msr->txcfg->of_limit); - return -2; + msr_log(msr, 1, "Output filter: Content-Length (%s) over the limit (%lu).", + log_escape_nq(r->pool, (char *)s_content_length), msr->txcfg->of_limit); + return -2; /* Over the limit. */ } } return 1; } +/** + * Send the accumulated content down the filter stream + * and to the client. + */ +static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) { + apr_status_t rc; + + rc = ap_pass_brigade(f->next, msr->of_brigade); + if (rc != APR_SUCCESS) { + int log_level = 1; + + if (APR_STATUS_IS_ECONNRESET(rc)) { + /* Message "Connection reset by peer" is common and not a sign + * of something unusual. Hence we don't want to make a big deal + * about it, logging at NOTICE level. Everything else we log + * at ERROR level. + */ + log_level = 3; + } + + if (msr->txcfg->debuglog_level >= log_level) { + msr_log(msr, log_level, "Output filter: Error while forwarding response data (%i): %s", + rc, get_apr_error(msr->mp, rc)); + } + + return rc; + } + + return APR_SUCCESS; +} + +/** + * + */ +static void prepend_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) { + if ((msr->txcfg->content_injection_enabled) && (msr->content_prepend) && (!msr->of_skipping)) { + apr_bucket *bucket_ci = NULL; + + bucket_ci = apr_bucket_heap_create(msr->content_prepend, + msr->content_prepend_len, NULL, f->r->connection->bucket_alloc); + APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Content Injection (b): Added content to top: %s", + log_escape_nq_ex(msr->mp, msr->content_prepend, msr->content_prepend_len)); + } + } +} + +/** + * + */ +static int flatten_response_body(modsec_rec *msr) { + apr_status_t rc; + + msr->resbody_status = RESBODY_STATUS_READ_BRIGADE; + + if (msr->resbody_length + 1 <= 0) { + msr_log(msr, 1, "Output filter: Invalid response length: %lu", msr->resbody_length); + return -1; + } + + msr->resbody_data = apr_palloc(msr->mp, msr->resbody_length + 1); + if (msr->resbody_data == NULL) { + msr_log(msr, 1, "Output filter: Response body data memory allocation failed. Asked for: %li", + msr->resbody_length + 1); + return -1; + } + + rc = apr_brigade_flatten(msr->of_brigade, msr->resbody_data, &msr->resbody_length); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Output filter: Failed to flatten brigade (%i): %s", rc, + get_apr_error(msr->mp, rc)); + return -1; + } + + msr->resbody_data[msr->resbody_length] = '\0'; + msr->resbody_status = RESBODY_STATUS_READ; + + return 1; +} + /** * Output filter. */ @@ -405,7 +499,9 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { modsec_rec *msr = (modsec_rec *)f->ctx; apr_bucket *bucket = NULL, *eos_bucket = NULL; apr_status_t rc; + int start_skipping = 0; + /* Do we have the context? */ if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, "ModSecurity: Internal Error: msr is null in output filter."); @@ -490,7 +586,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } /* Content injection (prepend & non-buffering). */ - if (msr->txcfg->content_injection_enabled && msr->content_prepend && msr->of_skipping) { + if ((msr->txcfg->content_injection_enabled) && (msr->content_prepend) && (msr->of_skipping)) { apr_bucket *bucket_ci = apr_bucket_heap_create(msr->content_prepend, msr->content_prepend_len, NULL, f->r->connection->bucket_alloc); APR_BRIGADE_INSERT_HEAD(bb_in, bucket_ci); @@ -517,13 +613,19 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { const char *buf; apr_size_t buflen; - if (msr->of_skipping == 0) { + /* Look into response data if configured to do so, + * unless we've already processed a partial response. + */ + if ((msr->of_skipping == 0)&&(!msr->of_partial)) { /* Observe the response data. */ + /* Retrieve data from the bucket. */ rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ); if (rc != APR_SUCCESS) { msr->of_status = OF_STATUS_COMPLETE; msr->resbody_status = RESBODY_STATUS_ERROR; + msr_log(msr, 1, "Output filter: Failed to read bucket (rc %i): %s", rc, get_apr_error(r->pool, rc)); + ap_remove_output_filter(f); return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } @@ -533,23 +635,45 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { bucket->type->name, buflen); } + /* Check the response size. */ if (msr->resbody_length > (apr_size_t)msr->txcfg->of_limit) { - msr_log(msr, 1, "Output filter: Response body too large (over limit of %lu, total length not known).", - msr->txcfg->of_limit); - msr->of_status = OF_STATUS_COMPLETE; - msr->resbody_status = RESBODY_STATUS_PARTIAL; - ap_remove_output_filter(f); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); - } + /* The size of the response is larger than what we're + * ready to accept. We need to decide what we want to do + * about it. + */ + if (msr->txcfg->of_limit_action == RESPONSE_BODY_LIMIT_ACTION_REJECT) { + /* Reject response. */ + msr_log(msr, 1, "Output filter: Response body too large (over limit of %lu, " + "total length not known).", msr->txcfg->of_limit); - msr->resbody_length += buflen; + msr->of_status = OF_STATUS_COMPLETE; + msr->resbody_status = RESBODY_STATUS_PARTIAL; + + ap_remove_output_filter(f); + return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + } else { + /* Process partial response. */ + start_skipping = 1; + msr->resbody_length = msr->txcfg->of_limit; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Output filter: Processing partial response body (limit %lu)", + msr->txcfg->of_limit); + } + } + } else { + msr->resbody_length += buflen; + } } + /* Have we reached the end of the response? */ if (APR_BUCKET_IS_EOS(bucket)) { eos_bucket = bucket; /* Inject content (append & non-buffering). */ - if (msr->txcfg->content_injection_enabled && msr->content_append && msr->of_skipping) { + if ((msr->txcfg->content_injection_enabled) && (msr->content_append) + && (msr->of_skipping || msr->of_partial || start_skipping)) + { apr_bucket *bucket_ci = NULL; bucket_ci = apr_bucket_heap_create(msr->content_append, @@ -570,91 +694,94 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { * we have in the context, but only if we actually * want to keep the response body. */ - if (msr->of_skipping == 0) { + if ((msr->of_skipping == 0)&&(msr->of_partial == 0)) { ap_save_brigade(f, &msr->of_brigade, &bb_in, msr->mp); + /* Do we need to process a partial response? */ + if (start_skipping) { + if (flatten_response_body(msr) < 0) { + return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + } + + /* Process phase RESPONSE_BODY */ + rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); + if (rc < 0) { + return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + } + if (rc > 0) { + int status = perform_interception(msr); + if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ + return send_error_bucket(f, status); + } + } + + /* Prepend content as necessary. */ + prepend_content_to_of_brigade(msr, f); + + if ((rc = send_of_brigade(msr, f)) != APR_SUCCESS) { + return rc; + } + + msr->of_partial = 1; + } + if (msr->of_done_reading == 0) { /* We are done for now. We will be called again with more data. */ return APR_SUCCESS; } if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Completed receiving response (length %lu).", - msr->resbody_length); + msr_log(msr, 4, "Output filter: Completed receiving response body (buffered %s - %lu bytes).", + (msr->of_partial ? "partial" : "full"), msr->resbody_length); } - } else { + } else { /* Not looking at response data. */ if (msr->of_done_reading == 0) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Output filter: Sending input brigade directly."); + } + return ap_pass_brigade(f->next, bb_in); } if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Completed receiving response."); + msr_log(msr, 4, "Output filter: Completed receiving response body (non-buffering)."); } } - /* We're not coming back here. */ + /* We've done our thing; remove us from the filter list. */ msr->of_status = OF_STATUS_COMPLETE; ap_remove_output_filter(f); - if (msr->of_skipping == 0) { - /* We've done with reading, it's time to inspect the data. */ - msr->resbody_status = RESBODY_STATUS_READ_BRIGADE; - - if (msr->resbody_length + 1 <= 0) { - msr_log(msr, 1, "Output filter: Invalid response length: %lu", msr->resbody_length); + /* Process phase RESPONSE_BODY, but + * only if it hasn't been processed already. + */ + if (msr->phase < PHASE_RESPONSE_BODY) { + if (flatten_response_body(msr) < 0) { return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } - msr->resbody_data = apr_palloc(msr->mp, msr->resbody_length + 1); - if (msr->resbody_data == NULL) { - msr_log(msr, 1, "Output filter: Response body data memory allocation failed. Asked for: %li", - msr->resbody_length + 1); + rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); + if (rc < 0) { return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } - - // TODO Why does the function below take pointer to length? Will it modify it? - // BR: Yes - The maximum length of the char array. On return, it is the actual length of the char array. - rc = apr_brigade_flatten(msr->of_brigade, msr->resbody_data, &msr->resbody_length); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Output filter: Failed to flatten brigade (%i): %s", rc, - get_apr_error(r->pool, rc)); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); - } - msr->resbody_data[msr->resbody_length] = '\0'; - msr->resbody_status = RESBODY_STATUS_READ; - } - - /* Process phase RESPONSE_BODY */ - rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); - if (rc < 0) { - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); - } - if (rc > 0) { - int status = perform_interception(msr); - if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ - return send_error_bucket(f, status); - } - } - - if (msr->of_skipping == 0) { - record_time_checkpoint(msr, 3); - - /* Inject content into response (prepend & buffering). */ - if (msr->txcfg->content_injection_enabled && msr->content_prepend && (!msr->of_skipping)) { - apr_bucket *bucket_ci = NULL; - - bucket_ci = apr_bucket_heap_create(msr->content_prepend, - msr->content_prepend_len, NULL, f->r->connection->bucket_alloc); - APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Content Injection (b): Added content to top: %s", - log_escape_nq_ex(msr->mp, msr->content_prepend, msr->content_prepend_len)); + if (rc > 0) { + int status = perform_interception(msr); + if (status != DECLINED) { /* DECLINED means we allow-ed the request. */ + return send_error_bucket(f, status); } } + } + + /* Now send data down the filter stream + * (full-buffering only). + */ + if ((msr->of_skipping == 0)&&(!msr->of_partial)) { + record_time_checkpoint(msr, 3); + + prepend_content_to_of_brigade(msr, f); /* Inject content into response (append & buffering). */ - if (msr->txcfg->content_injection_enabled && msr->content_append && (!msr->of_skipping)) { + if ((msr->txcfg->content_injection_enabled) && (msr->content_append)) { apr_bucket *bucket_ci = NULL; bucket_ci = apr_bucket_heap_create(msr->content_append, @@ -667,22 +794,8 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } } - rc = ap_pass_brigade(f->next, msr->of_brigade); - if (rc != APR_SUCCESS) { - int log_level = 1; - - if (APR_STATUS_IS_ECONNRESET(rc)) { - /* Message "Connection reset by peer" is common and not a sign - * of something unusual. Hence we don't want to make a big deal - * about it, logging at NOTICE level. Everything else we log - * at ERROR level. - */ - log_level = 3; - } - - msr_log(msr, log_level, "Output filter: Error while forwarding response data (%i): %s", - rc, get_apr_error(msr->mp, rc)); - + /* Send data down the filter stream. */ + if ((rc = send_of_brigade(msr, f)) != APR_SUCCESS) { return rc; } } @@ -692,9 +805,13 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { msr_log(msr, 4, "Output filter: Output forwarding complete."); } - if (msr->of_skipping == 0) { + if ((msr->of_skipping == 0)&&(msr->of_partial == 0)) { return APR_SUCCESS; } else { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Output filter: Sending input brigade directly."); + } + return ap_pass_brigade(f->next, bb_in); } } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 88a053fa..b4905541 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -103,6 +103,9 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define RESPONSE_BODY_DEFAULT_LIMIT 524288 #define RESPONSE_BODY_HARD_LIMIT 1073741824L +#define RESPONSE_BODY_LIMIT_ACTION_REJECT 0 +#define RESPONSE_BODY_LIMIT_ACTION_PARTIAL 1 + #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) #include "unixd.h" #define __SET_MUTEX_PERMS @@ -213,6 +216,7 @@ struct modsec_rec { unsigned int of_status; unsigned int of_done_reading; unsigned int of_skipping; + unsigned int of_partial; unsigned int of_is_error; unsigned int resbody_status; @@ -358,6 +362,7 @@ struct directory_config { long int of_limit; apr_table_t *of_mime_types; int of_mime_types_cleared; + int of_limit_action; const char *debuglog_name; int debuglog_level; From 009c3b0fa1593f5e5c36797510c3597364e26e6a Mon Sep 17 00:00:00 2001 From: ivanr Date: Fri, 21 Sep 2007 23:37:56 +0000 Subject: [PATCH 119/372] Document SecResponseBodyLimitAction. --- doc/modsecurity2-apache-reference.xml | 60 +++++++++++++++++++++------ 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 3e078508..854391a7 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-dev3 / (September 14, 2007) + Version 2.5.0-dev3 / (September 21, 2007) 2004-2007 @@ -1302,6 +1302,40 @@ SecRequestBodyInMemoryLimit 131072 SecResponseBodyLimit 524288 +
+ <literal>SecResponseBodyLimitAction</literal> + + Description: Controls what + happens once a response body limit, configured with + SecResponseBodyLimit, is encountered. By default + ModSecurity wil 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 + + Processing Phase: N/A + + Scope: Any +
+
<literal>SecResponseBodyMimeType</literal> @@ -1910,17 +1944,17 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1 - application/x-www-form-urlencoded - used to - transfer form data - - - - multipart/form-data - used for file - transfers - - - - text/xml - used for passing XML data + application/x-www-form-urlencoded - used to + transfer form data + + + + multipart/form-data - used for file + transfers + + + + text/xml - used for passing XML data @@ -4973,4 +5007,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}
- + \ No newline at end of file From a1955d09e3664ccfc4ab859ce1d963309abebe3b Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 24 Sep 2007 23:59:42 +0000 Subject: [PATCH 120/372] Add crude performance measurement. --- apache2/Makefile | 1 + apache2/re.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++ apache2/re.h | 3 ++ 3 files changed, 78 insertions(+) diff --git a/apache2/Makefile b/apache2/Makefile index 07f5e7bc..7cd4c577 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -29,6 +29,7 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 DEFS = -DWITH_LIBXML2 +#DEFS = -DWITH_LIBXML2 -DPERFORMANCE_MEASUREMENT #DEFS = -DWITH_LIBXML2 -DNO_MODSEC_API #DEFS = -DWITH_LIBXML2 -DDEBUG_CONF #DEFS = -DWITH_LIBXML2 -DCACHE_DEBUG diff --git a/apache2/re.c b/apache2/re.c index 8424c34b..421df5cc 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -614,12 +614,18 @@ void msre_engine_destroy(msre_engine *engine) { #define NEXT_RULE 2 #define SKIP_RULES 3 + + /** * Default implementation of the ruleset phase processing; it processes * the rules in the ruleset attached to the currently active * transaction phase. */ +#if defined(PERFORMANCE_MEASUREMENT) +static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_rec *msr) { +#else apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { +#endif apr_array_header_t *arr = NULL; msre_rule **rules; apr_status_t rc; @@ -657,6 +663,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; + apr_time_t time1 = 0; /* NEXT_CHAIN is used when one of the rules in a chain * fails to match and then we need to skip the remaining @@ -712,8 +719,16 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : "")); } +#if defined(PERFORMANCE_MEASUREMENT) + time1 = apr_time_now(); +#endif + rc = msre_rule_process(rule, msr); +#if defined(PERFORMANCE_MEASUREMENT) + rule->execution_time += (apr_time_now() - time1); +#endif + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Rule returned %i.", rc); } @@ -796,6 +811,63 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) return 0; } +#if defined(PERFORMANCE_MEASUREMENT) +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) { + case PHASE_REQUEST_HEADERS : + arr = ruleset->phase_request_headers; + break; + case PHASE_REQUEST_BODY : + arr = ruleset->phase_request_body; + break; + case PHASE_RESPONSE_HEADERS : + arr = ruleset->phase_response_headers; + break; + case PHASE_RESPONSE_BODY : + arr = ruleset->phase_response_body; + break; + case PHASE_LOGGING : + arr = ruleset->phase_logging; + break; + default : + msr_log(msr, 1, "Internal Error: Invalid phase %d", msr->phase); + return -1; + } + + rules = (msre_rule **)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msre_rule *rule = rules[i]; + rule->execution_time = 0; + } + + time1 = apr_time_now(); + + for (i = 0; i < 10000; i++) { + rc = msre_ruleset_process_phase_(ruleset, msr); + } + + msr_log(msr, 1, "Phase %i: %" APR_TIME_T_FMT " usec", msr->phase, ((apr_time_now() - time1) / 10000)); + + rules = (msre_rule **)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msre_rule *rule = rules[i]; + msr_log(msr, 1, "Rule %x [id \"%s\"][file \"%s\"][line \"%d\"]: %lu usec", rule, + ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", + rule->filename != NULL ? rule->filename : "-", + rule->line_num, + (rule->execution_time / 10000)); + } + + return rc; +} +#endif + /** * Creates a ruleset that will be handled by the default * implementation. @@ -1241,7 +1313,9 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, if (msr->txcfg->debuglog_level >= 4) { time_before_regex = apr_time_now(); } + rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg); + if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", (apr_time_now() - time_before_regex)); diff --git a/apache2/re.h b/apache2/re.h index f214f117..ca05dbbe 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -123,6 +123,9 @@ struct msre_rule { msre_ruleset *ruleset; msre_rule *chain_starter; +#if defined(PERFORMANCE_MEASUREMENT) + unsigned int execution_time; +#endif }; msre_rule DSOLOCAL *msre_rule_create(msre_ruleset *ruleset, From 426ce1aea70c83adf53d44020873e4c76ef0f702 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 25 Sep 2007 21:40:04 +0000 Subject: [PATCH 121/372] Fixed deprecatevar. See #59. --- CHANGES | 3 +++ apache2/re_actions.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 277ad203..bbf49278 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds + as documented instead of decrementing by a rate. + * Enable ModSecurity to look at partial response bodies. In previous versions ModSecurity would respond with status code 500 when the response body was too long. Now, if SecResponseBodyLimitAction is diff --git a/apache2/re_actions.c b/apache2/re_actions.c index e182d48e..1c5f65ce 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -1147,7 +1147,7 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t * time elapsed since the last update. */ new_value = current_value - - ((current_time - last_update_time) * atoi(var_value) / atoi(s)); + (atoi(var_value) * ((current_time - last_update_time) / atoi(s))); if (new_value < 0) new_value = 0; /* Only change the value if it differs. */ From 72f81493380b476e7319b8b27acbd140adc88fe0 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 26 Sep 2007 18:03:08 +0000 Subject: [PATCH 122/372] Do not process subrequests in phase 2. See #135. --- CHANGES | 2 ++ apache2/mod_security2.c | 27 ++++++++------------------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index bbf49278..272a15de 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * Do not process subrequests in phase 2. + * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds as documented instead of decrementing by a rate. diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index b87791ef..e483215f 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -581,6 +581,13 @@ static int hook_request_late(request_rec *r) { modsec_rec *msr = NULL; int rc; + /* This function needs to run only once per transaction + * (i.e. subrequests and redirects are excluded). + */ + if ((r->main != NULL)||(r->prev != NULL)) { + return DECLINED; + } + /* Find the transaction context and make sure * we are supposed to proceed. */ @@ -594,25 +601,7 @@ static int hook_request_late(request_rec *r) { /* Has this phase been completed already? */ if (msr->phase_request_body_complete) { - /* If we are redirecting and there was no previous response it is - * an error page request and we ignore it. - */ - if ( (msr->r->prev != NULL) - && ((msr->r->prev->headers_out == NULL) || (apr_is_empty_table(msr->r->prev->headers_out))) ) - { - msr_log(msr, 9, "Allowing internally redirected error document: %s", msr->r->uri); - return DECLINED; - } - - if (msr->was_intercepted) { - msr_log(msr, 4, "Phase REQUEST_BODY request already intercepted. Intercepting additional request."); - return perform_interception(msr); - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Phase REQUEST_BODY already complete, skipping."); - } - + msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once."); return DECLINED; } msr->phase_request_body_complete = 1; From 7c393c48745e2047cbdb9f626804502017c179ad Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 26 Sep 2007 19:47:06 +0000 Subject: [PATCH 123/372] Fixed the wrong status being displayed in the error page. See #3. --- apache2/apache2_io.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 1b3c5a9e..9ab15cb2 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -258,6 +258,9 @@ static apr_status_t send_error_bucket(ap_filter_t *f, int status) { apr_bucket_brigade *brigade = NULL; apr_bucket *bucket = NULL; + /* Set the status line explicitly */ + f->r->status_line = ap_get_status_line(status); + brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); if (brigade == NULL) return APR_EGENERAL; @@ -273,17 +276,13 @@ static apr_status_t send_error_bucket(ap_filter_t *f, int status) { ap_pass_brigade(f->next, brigade); - // TODO: Should return an error if this function failed, but currently - // coded to pass this on as the filter return value. The calling code - // needs changed to return an error after checking this return value - // and possibly generating a log entry. - // - // Also note that ap_pass_brigade will return APR_SUCCESS, so we should - // not pass this on to be returned by the filter on error. Although - // it may not matter what we return from the filter as it may be too - // late to even generate an error (already sent to client). Nick Kew - // recommends to return APR_EGENERAL in hopes that the handler in control - // will notice and do The Right Thing. So, that is what we do now. + /* NOTE: + * It may not matter what we return from the filter as it may be too + * late to even generate an error (already sent to client). Nick Kew + * recommends to return APR_EGENERAL in hopes that the handler in control + * will notice and do The Right Thing. So, that is what we do now. + */ + return APR_EGENERAL; } From 9f898a0e0b2032ba509f9d0760607501f3871933 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 26 Sep 2007 19:49:48 +0000 Subject: [PATCH 124/372] Fixed comment. --- apache2/apache2_io.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 9ab15cb2..882ea875 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -258,7 +258,7 @@ static apr_status_t send_error_bucket(ap_filter_t *f, int status) { apr_bucket_brigade *brigade = NULL; apr_bucket *bucket = NULL; - /* Set the status line explicitly */ + /* Set the status line explicitly for the error document */ f->r->status_line = ap_get_status_line(status); brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); @@ -277,7 +277,7 @@ static apr_status_t send_error_bucket(ap_filter_t *f, int status) { ap_pass_brigade(f->next, brigade); /* NOTE: - * It may not matter what we return from the filter as it may be too + * It may not matter what we do from the filter as it may be too * late to even generate an error (already sent to client). Nick Kew * recommends to return APR_EGENERAL in hopes that the handler in control * will notice and do The Right Thing. So, that is what we do now. From 86c9a9bf1f1a5a14909d38ab4a44d55ac2498a40 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 26 Sep 2007 21:39:45 +0000 Subject: [PATCH 125/372] Cleanup CHANGES. --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 272a15de..8398ee52 100644 --- a/CHANGES +++ b/CHANGES @@ -17,11 +17,11 @@ is interrupted (either by Apache - e.g. by responding with 400, 401 or 403, or by ModSecurity itself). - * Fix the base64decode transformation function to not return extra + * Fixed the base64decode transformation function to not return extra characters at the end. - * Fixed potential corruption at end of strings after using base64Decode - (APR-Util issue). TODO make a better CHANGELOG entry ;) + * Removed potential for extra characters to be appended to the value when + using base64Decode. * Return from the output filter with an error in addition to setting up the HTTP error status in the output data. From 5022ddcadf796c69b9e5b7102fe8cb4fa59d954c Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 26 Sep 2007 21:46:06 +0000 Subject: [PATCH 126/372] Cleanup more subrequest code. Do not run with subrequests in phase 3-4. Still need to look at phase 5 to see what I can cleanup there. See #135. --- CHANGES | 2 +- apache2/mod_security2.c | 73 +++++++++++++++++------------------------ 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/CHANGES b/CHANGES index 8398ee52..8d71b81c 100644 --- a/CHANGES +++ b/CHANGES @@ -2,7 +2,7 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ - * Do not process subrequests in phase 2. + * Do not process subrequests in phase 2-4. * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds as documented instead of decrementing by a rate. diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index e483215f..61710c04 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -61,26 +61,9 @@ int perform_interception(modsec_rec *msr) { msre_actionset *actionset = NULL; const char *message = NULL; const char *phase_text = ""; - const char *intreq_text = ""; - int is_initial_req = ap_is_initial_req(msr->r); int status = DECLINED; int log_level = 1; - /* Check for an initial request */ - - if (is_initial_req != 1) { - if (msr->r->main != NULL) { - intreq_text = "Sub-Request: "; - } - else if (msr->r->prev != NULL) { - intreq_text = "Internal Redirect: "; - } - else { - intreq_text = "Internal Request: "; - } - } - - /* Sanity checks first. */ if (msr->was_intercepted == 0) { @@ -103,10 +86,10 @@ int perform_interception(modsec_rec *msr) { * if a nolog action was used or this is not the initial request * to hide the message. */ - log_level = ((actionset->log != 1) || (is_initial_req != 1)) ? 4 : 1; + log_level = (actionset->log != 1) ? 4 : 1; /* Pause the request first (if configured and the initial request). */ - if (actionset->intercept_pause && (is_initial_req == 1)) { + if (actionset->intercept_pause) { msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " "%i msec.", actionset->intercept_pause); /* apr_sleep accepts microseconds */ @@ -118,14 +101,14 @@ int perform_interception(modsec_rec *msr) { case ACTION_DENY : if (actionset->intercept_status != 0) { status = actionset->intercept_status; - message = apr_psprintf(msr->mp, "%sAccess denied with code %i%s.", - intreq_text, status, phase_text); + message = apr_psprintf(msr->mp, "Access denied with code %i%s.", + status, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Internal Error: Invalid status code requested %i).", - intreq_text, phase_text, actionset->intercept_status); + phase_text, actionset->intercept_status); } break; @@ -134,25 +117,25 @@ int perform_interception(modsec_rec *msr) { if (ap_find_linked_module("mod_proxy.c") == NULL) { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Configuration Error: Proxy action to %s requested but mod_proxy not found).", - intreq_text, phase_text, + phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); } else { msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri); msr->r->proxyreq = PROXYREQ_REVERSE; msr->r->handler = "proxy-server"; status = OK; - message = apr_psprintf(msr->mp, "%sAccess denied using proxy to%s %s.", - intreq_text, phase_text, + message = apr_psprintf(msr->mp, "Access denied using proxy to%s %s.", + phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Configuration Error: Proxy action requested but it does not work in output phases).", - intreq_text, phase_text); + phase_text); } break; @@ -169,30 +152,30 @@ int perform_interception(modsec_rec *msr) { if (csd) { if (apr_socket_close(csd) == APR_SUCCESS) { status = HTTP_FORBIDDEN; - message = apr_psprintf(msr->mp, "%sAccess denied with connection close%s.", - intreq_text, phase_text); + message = apr_psprintf(msr->mp, "Access denied with connection close%s.", + phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Error: Connection drop requested but failed to close the " " socket).", - intreq_text, phase_text); + phase_text); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Error: Connection drop requested but socket not found.", - intreq_text, phase_text); + phase_text); } } #else log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Error: Connection drop not implemented on this platform).", - intreq_text, phase_text); + phase_text); #endif break; @@ -205,25 +188,24 @@ int perform_interception(modsec_rec *msr) { } else { status = HTTP_MOVED_TEMPORARILY; } - message = apr_psprintf(msr->mp, "%sAccess denied with redirection to %s using " + message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " "status %i%s.", - intreq_text, log_escape_nq(msr->mp, actionset->intercept_uri), status, phase_text); break; case ACTION_ALLOW : status = DECLINED; - message = apr_psprintf(msr->mp, "%sAccess allowed%s.", intreq_text, phase_text); + message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text); msr->was_intercepted = 0; break; default : log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + message = apr_psprintf(msr->mp, "Access denied with code 500%s " "(Internal Error: invalid interception action %i).", - intreq_text, phase_text, actionset->intercept_action); + phase_text, actionset->intercept_action); break; } @@ -897,6 +879,13 @@ static int hook_log_transaction(request_rec *r) { static void hook_insert_filter(request_rec *r) { modsec_rec *msr = NULL; + /* This function needs to run only once per transaction + * (i.e. subrequests and redirects are excluded). + */ + if ((r->main != NULL)||(r->prev != NULL)) { + return; + } + /* Find the transaction context first. */ msr = retrieve_tx_context(r); if (msr == NULL) return; From f3a8854fe9754027386e6f84457c818358cd3807 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 27 Sep 2007 21:18:23 +0000 Subject: [PATCH 127/372] Mark any error conditions/alerts as 'relevant'. Clean up/add error messages where this can happen. --- CHANGES | 3 + apache2/apache2.h | 2 + apache2/apache2_io.c | 99 +++++++++------------------- apache2/apache2_util.c | 43 ++++++++++++ apache2/mod_security2.c | 10 ++- apache2/modsecurity.c | 9 ++- apache2/modsecurity.h | 12 ++-- apache2/msc_logging.c | 12 ++-- apache2/msc_reqbody.c | 141 +++++++++++++++++++++++++--------------- apache2/pdf_protect.c | 22 +------ 10 files changed, 200 insertions(+), 153 deletions(-) diff --git a/CHANGES b/CHANGES index 8d71b81c..9c9b0d2f 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * Situations where ModSecurity will intercept, generate an error or log + an alert to the debug log are now marked as 'relevant'. + * Do not process subrequests in phase 2-4. * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds diff --git a/apache2/apache2.h b/apache2/apache2.h index 4cd86c4c..e9157a81 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -69,6 +69,8 @@ apr_status_t DSOLOCAL read_request_body(modsec_rec *msr, char **error_msg); int DSOLOCAL perform_interception(modsec_rec *msr); +apr_status_t DSOLOCAL send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status); + int DSOLOCAL apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output); void DSOLOCAL record_time_checkpoint(modsec_rec *msr, int checkpoint_no); diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 882ea875..4d098277 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -30,6 +30,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, msc_data_chunk *chunk = NULL; apr_bucket *bucket; apr_status_t rc; + char *my_error_msg = NULL; if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, @@ -55,16 +56,20 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, if (msr->if_started_forwarding == 0) { msr->if_started_forwarding = 1; - rc = modsecurity_request_body_retrieve_start(msr); + rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg); if (rc == -1) { - // TODO err + if (my_error_msg != NULL) { + msr_log(msr, 1, "%s", my_error_msg); + } return APR_EGENERAL; } } - rc = modsecurity_request_body_retrieve(msr, &chunk, (unsigned int)nbytes); + rc = modsecurity_request_body_retrieve(msr, &chunk, (unsigned int)nbytes, &my_error_msg); if (rc == -1) { - // TODO err + if (my_error_msg != NULL) { + msr_log(msr, 1, "%s", my_error_msg); + } return APR_EGENERAL; } @@ -151,8 +156,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { msr_log(msr, 4, "Input filter: Reading request body."); } - if (modsecurity_request_body_start(msr) < 0) { - // TODO err + if (modsecurity_request_body_start(msr, error_msg) < 0) { return -1; } @@ -169,25 +173,18 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { */ switch(rc) { case APR_TIMEUP : + *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); return -4; - break; case -3 : *error_msg = apr_psprintf(msr->mp, "Error reading request body: HTTP Error 413 - Request entity too large. (Most likely.)"); - rc = -3; - break; + return -3; case APR_EGENERAL : *error_msg = apr_psprintf(msr->mp, "Error reading request body: Client went away."); - rc = -2; - break; + return -2; default : *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - rc = -1; - break; + return -1; } - - if (*error_msg) msr_log(msr, 1, "%s", *error_msg); - - return rc; } /* Loop through the buckets in the brigade in order @@ -202,8 +199,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ); if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Input filter: Failed reading input / bucket (%i): %s", - rc, get_apr_error(msr->mp, rc)); + *error_msg = apr_psprintf(msr->mp, "Failed reading input / bucket (%i): %s", rc, get_apr_error(msr->mp, rc)); return -1; } @@ -220,8 +216,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { } if (buflen != 0) { - if (modsecurity_request_body_store(msr, buf, buflen) < 0) { - // TODO err + if (modsecurity_request_body_store(msr, buf, buflen, error_msg) < 0) { return -1; } @@ -236,7 +231,8 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { apr_brigade_cleanup(bb_in); } while(!seen_eos); - modsecurity_request_body_end(msr); + // TODO: Why ignore the return code here? + modsecurity_request_body_end(msr, error_msg); if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Completed receiving request body (length %lu).", @@ -251,41 +247,6 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { /* -- Output filter -- */ -/** - * Sends a brigade with an error bucket down the filter chain. - */ -static apr_status_t send_error_bucket(ap_filter_t *f, int status) { - apr_bucket_brigade *brigade = NULL; - apr_bucket *bucket = NULL; - - /* Set the status line explicitly for the error document */ - f->r->status_line = ap_get_status_line(status); - - brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); - if (brigade == NULL) return APR_EGENERAL; - - bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; - - APR_BRIGADE_INSERT_TAIL(brigade, bucket); - - bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; - - APR_BRIGADE_INSERT_TAIL(brigade, bucket); - - ap_pass_brigade(f->next, brigade); - - /* NOTE: - * It may not matter what we do from the filter as it may be too - * late to even generate an error (already sent to client). Nick Kew - * recommends to return APR_EGENERAL in hopes that the handler in control - * will notice and do The Right Thing. So, that is what we do now. - */ - - return APR_EGENERAL; -} - /** * Examines the configuration and the response MIME type * in order to determine whether output buffering should @@ -505,7 +466,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, "ModSecurity: Internal Error: msr is null in output filter."); ap_remove_output_filter(f); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } msr->r = r; @@ -528,13 +489,13 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { rc = modsecurity_process_phase(msr, PHASE_RESPONSE_HEADERS); if (rc < 0) { /* error */ ap_remove_output_filter(f); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + 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); - return send_error_bucket(f, status); + return send_error_bucket(msr, f, status); } } @@ -547,7 +508,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { ap_remove_output_filter(f); msr->of_status = OF_STATUS_COMPLETE; msr->resbody_status = RESBODY_STATUS_ERROR; - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); case 0 : /* We do not want to observe this response body * but we need to remain attached to observe @@ -626,7 +587,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { rc, get_apr_error(r->pool, rc)); ap_remove_output_filter(f); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } if (msr->txcfg->debuglog_level >= 9) { @@ -649,7 +610,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { msr->resbody_status = RESBODY_STATUS_PARTIAL; ap_remove_output_filter(f); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } else { /* Process partial response. */ start_skipping = 1; @@ -699,18 +660,18 @@ 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) { - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + 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) { - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + 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. */ - return send_error_bucket(f, status); + return send_error_bucket(msr, f, status); } } @@ -756,17 +717,17 @@ 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) { - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY); if (rc < 0) { - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + 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. */ - return send_error_bucket(f, status); + return send_error_bucket(msr, f, status); } } } diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index f60d1ce8..0b4e9aca 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -13,6 +13,46 @@ #include "http_core.h" #include "util_script.h" +/** + * Sends a brigade with an error bucket down the filter chain. + */ +apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { + apr_bucket_brigade *brigade = NULL; + apr_bucket *bucket = NULL; + + /* Force relevancy for all errors */ + if (msr != NULL) { + msr->is_relevant++; + } + + /* Set the status line explicitly for the error document */ + f->r->status_line = ap_get_status_line(status); + + brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); + if (brigade == NULL) return APR_EGENERAL; + + bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc); + if (bucket == NULL) return APR_EGENERAL; + + APR_BRIGADE_INSERT_TAIL(brigade, bucket); + + bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); + if (bucket == NULL) return APR_EGENERAL; + + APR_BRIGADE_INSERT_TAIL(brigade, bucket); + + ap_pass_brigade(f->next, brigade); + + /* NOTE: + * It may not matter what we do from the filter as it may be too + * late to even generate an error (already sent to client). Nick Kew + * recommends to return APR_EGENERAL in hopes that the handler in control + * will notice and do The Right Thing. So, that is what we do now. + */ + + return APR_EGENERAL; +} + /** * Execute system command. First line of the output will be returned in * the "output" parameter. @@ -234,6 +274,9 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, /* Add this message to the list. */ if (msr != NULL) { + /* Force relevency if this is an alert */ + msr->is_relevant++; + *(const char **)apr_array_push(msr->alerts) = apr_pstrdup(msr->mp, str1); } } diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 61710c04..d1d69284 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -639,14 +639,22 @@ static int hook_request_late(request_rec *r) { if (rc < 0) { switch(rc) { case -1 : - msr_log(msr, 1, "%s", my_error_msg); + if ((my_error_msg != NULL)||(msr->is_relevant == 0)) { + msr_log(msr, 1, "%s", my_error_msg); + } return HTTP_INTERNAL_SERVER_ERROR; break; case -4 : /* Timeout. */ + if ((my_error_msg != NULL)||(msr->is_relevant == 0)) { + msr_log(msr, 4, "%s", my_error_msg); + } r->connection->keepalive = AP_CONN_CLOSE; return HTTP_REQUEST_TIME_OUT; break; case -5 : /* Request body limit reached. */ + if ((my_error_msg != NULL)||(msr->is_relevant == 0)) { + msr_log(msr, 1, "%s", my_error_msg); + } r->connection->keepalive = AP_CONN_CLOSE; return HTTP_REQUEST_ENTITY_TOO_LARGE; break; diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 9ec5685d..c6722398 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -136,6 +136,7 @@ static apr_status_t modsecurity_tx_cleanup(void *data) { apr_table_entry_t *te; int collect_garbage = 0; int i; + char *my_error_msg = NULL; if (msr == NULL) return APR_SUCCESS; @@ -167,7 +168,11 @@ static apr_status_t modsecurity_tx_cleanup(void *data) { if (msr->xml != NULL) xml_cleanup(msr); #endif - modsecurity_request_body_clear(msr); + // TODO: Why do we ignore return code here? + modsecurity_request_body_clear(msr, &my_error_msg); + if (my_error_msg != NULL) { + msr_log(msr, 1, "%s", my_error_msg); + } return APR_SUCCESS; } @@ -431,7 +436,7 @@ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { break; default : - return HTTP_INTERNAL_SERVER_ERROR; + msr_log(msr, 1, "Internal error: Could not determine if auditing is needed, so forcing auditing."); break; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index b4905541..a7470676 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -503,14 +503,14 @@ apr_status_t DSOLOCAL modsecurity_process_phase(modsec_rec *msr, int phase); /* Request body functions */ -apr_status_t DSOLOCAL modsecurity_request_body_start(modsec_rec *msr); +apr_status_t DSOLOCAL modsecurity_request_body_start(modsec_rec *msr, char **error_msg); apr_status_t DSOLOCAL modsecurity_request_body_store(modsec_rec *msr, - const char *data, apr_size_t length); + const char *data, apr_size_t length, char **error_msg); -apr_status_t DSOLOCAL modsecurity_request_body_end(modsec_rec *msr); +apr_status_t DSOLOCAL modsecurity_request_body_end(modsec_rec *msr, char **error_msg); -apr_status_t DSOLOCAL modsecurity_request_body_retrieve_start(modsec_rec *msr); +apr_status_t DSOLOCAL modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg); apr_status_t DSOLOCAL modsecurity_request_body_retrieve_end(modsec_rec *msr); @@ -519,7 +519,7 @@ apr_status_t DSOLOCAL modsecurity_request_body_retrieve_end(modsec_rec *msr); * nbytes will contain the number of bytes stored in the buffer. */ apr_status_t DSOLOCAL modsecurity_request_body_retrieve(modsec_rec *msr, msc_data_chunk **chunk, - long int nbytes); + long int nbytes, char **error_msg); void DSOLOCAL msc_add(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message, const char *rule_message); @@ -527,6 +527,6 @@ void DSOLOCAL msc_add(modsec_rec *msr, int level, msre_actionset *actionset, void DSOLOCAL msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message, const char *rule_message); -apr_status_t DSOLOCAL modsecurity_request_body_clear(modsec_rec *msr); +apr_status_t DSOLOCAL modsecurity_request_body_clear(modsec_rec *msr, char **error_msg); #endif diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 9935ef57..bcf1f1de 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -465,6 +465,7 @@ void sec_audit_logger(modsec_rec *msr) { unsigned int offset = 0, last_offset = 0; msc_arg *nextarg = NULL; int sanitise = 0; /* IMP1 Use constants for "sanitise" values. */ + char *my_error_msg = NULL; sorted_args = apr_array_make(msr->mp, 25, sizeof(const msc_arg *)); @@ -521,9 +522,9 @@ void sec_audit_logger(modsec_rec *msr) { * sanitise data in pieces. */ - rc = modsecurity_request_body_retrieve_start(msr); + rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg); if (rc < 0) { - msr_log(msr, 1, "Audit log: Failed retrieving request body."); + msr_log(msr, 1, "Audit log: %s", my_error_msg); } else { msc_data_chunk *chunk = NULL; unsigned int chunk_offset = 0; @@ -534,7 +535,7 @@ void sec_audit_logger(modsec_rec *msr) { sec_auditlog_write(msr, text, strlen(text)); for(;;) { - rc = modsecurity_request_body_retrieve(msr, &chunk, -1); + rc = modsecurity_request_body_retrieve(msr, &chunk, -1, &my_error_msg); if (chunk != NULL) { /* Anything greater than 1 means we have more data to sanitise. */ while (sanitise > 1) { @@ -594,7 +595,10 @@ void sec_audit_logger(modsec_rec *msr) { chunk_offset += chunk->length; } - if (rc <= 0) break; + if (rc <= 0) { + msr_log(msr, 1, "Audit log: %s", my_error_msg); + break; + } } modsecurity_request_body_retrieve_end(msr); diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 111aabdb..76603764 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -16,23 +16,30 @@ /** * Prepare to accept the request body (part 2). */ -static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr) { +static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **error_msg) { if(msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { /* Prepare to store request body in memory. */ msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 32, sizeof(msc_data_chunk *)); - if (msr->msc_reqbody_chunks == NULL) return -1; + if (msr->msc_reqbody_chunks == NULL) { + + *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage."); + return -1; + } } else { /* Prepare to store request body on disk. */ msr->msc_reqbody_filename = apr_psprintf(msr->mp, "%s/%s-%s-request_body-XXXXXX", msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid); - if (msr->msc_reqbody_filename == NULL) return -1; + if (msr->msc_reqbody_filename == NULL) { + *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to generate an on-disk filename."); + return -1; + } msr->msc_reqbody_fd = msc_mkstemp((char *)msr->msc_reqbody_filename); if (msr->msc_reqbody_fd < 0) { - msr_log(msr, 1, "Input filter: Failed to create temporary file: %s", + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to create temporary file: %s", msr->msc_reqbody_filename); return -1; } @@ -47,7 +54,7 @@ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr) { /** * Prepare to accept the request body (part 1). */ -apr_status_t modsecurity_request_body_start(modsec_rec *msr) { +apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { msr->msc_reqbody_length = 0; /* Create a separate memory pool that will be used @@ -63,7 +70,7 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr) { if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { if (multipart_init(msr, &my_error_msg) < 0) { - msr_log(msr, 1, "Multipart parser init failed: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "Multipart parser init failed: %s", my_error_msg); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; } @@ -72,7 +79,7 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr) { else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { if (xml_init(msr, &my_error_msg) < 0) { - msr_log(msr, 1, "XML parser init failed: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "XML parser init failed: %s", my_error_msg); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; } @@ -83,23 +90,23 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr) { /* Do nothing, URLENCODED processor does not support streaming yet. */ } else { - msr_log(msr, 1, "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; } } - return modsecurity_request_body_start_init(msr); + return modsecurity_request_body_start_init(msr, error_msg); } /** * Stores a chunk of request body data to disk. */ static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, - const char *data, apr_size_t length) + const char *data, apr_size_t length, char **error_msg) { apr_size_t i = write(msr->msc_reqbody_fd, data, length); if (i != length) { - msr_log(msr, 1, "Input filter: Failed writing %lu bytes to temporary file (rc %lu).", 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; } @@ -110,7 +117,7 @@ static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, * Stores one chunk of request body data in memory. */ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, - const char *data, apr_size_t length) + const char *data, apr_size_t length, char **error_msg) { /* Would storing this chunk mean going over the limit? */ if ((msr->msc_reqbody_spilltodisk) @@ -129,14 +136,14 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, /* Initialise disk storage */ msr->msc_reqbody_storage = MSC_REQBODY_DISK; - if (modsecurity_request_body_start_init(msr) < 0) return -1; + if (modsecurity_request_body_start_init(msr, error_msg) < 0) return -1; /* Write the data we keep in memory */ chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) { disklen += chunks[i]->length; - if (modsecurity_request_body_store_disk(msr, chunks[i]->data, chunks[i]->length) < 0) { + if (modsecurity_request_body_store_disk(msr, chunks[i]->data, chunks[i]->length, error_msg) < 0) { return -1; } @@ -152,10 +159,10 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, msr->msc_reqbody_chunks = NULL; apr_pool_clear(msr->msc_reqbody_mp); - msr_log(msr, 4, "Input filter: Wrote %lu bytes from memory to disk.", disklen); + msr_log(msr, 4, "Input filter: Wrote %" APR_SIZE_T_FMT " bytes from memory to disk.", disklen); /* Continue with disk storage from now on */ - return modsecurity_request_body_store_disk(msr, data, length); + return modsecurity_request_body_store_disk(msr, data, length, error_msg); } /* If we're here that means we are not over the @@ -180,10 +187,16 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, if (msr->msc_reqbody_chunk_current == NULL) { 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) return -1; + if (msr->msc_reqbody_chunk_current == NULL) { + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes for request body chunk.", sizeof(msc_data_chunk)); + return -1; + } msr->msc_reqbody_chunk_current->data = malloc(CHUNK_CAPACITY); - if (msr->msc_reqbody_chunk_current->data == NULL) return -1; + 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); + return -1; + } msr->msc_reqbody_chunk_current->length = 0; msr->msc_reqbody_chunk_current->is_permanent = 1; @@ -227,7 +240,7 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, * Stores one chunk of request body data. Returns -1 on error. */ apr_status_t modsecurity_request_body_store(modsec_rec *msr, - const char *data, apr_size_t length) + const char *data, apr_size_t length, char **error_msg) { /* If we have a processor for this request body send * data to it first (but only if it did not report an @@ -238,18 +251,18 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { 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); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 1, "Request body processor error: %s", my_error_msg); } } #ifdef WITH_LIBXML2 else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { 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); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 1, "Request body processor error: %s", my_error_msg); } } #endif @@ -258,22 +271,22 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /* Do nothing, URLENCODED processor does not support streaming. */ } else { - msr_log(msr, 1, "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; } } /* Store data. */ if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { - return modsecurity_request_body_store_memory(msr, data, length); + return modsecurity_request_body_store_memory(msr, data, length, error_msg); } else if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { - return modsecurity_request_body_store_disk(msr, data, length); + return modsecurity_request_body_store_disk(msr, data, length, error_msg); } /* Should never happen. */ - msr_log(msr, 1, "Internal Error: Unknown value for msc_reqbody_storage: %i", + *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %d", msr->msc_reqbody_storage); return -1; } @@ -281,7 +294,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /** * */ -static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr) { +static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) { msc_data_chunk **chunks, *one_chunk; char *d; int i, sofar; @@ -289,10 +302,13 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr) { /* Allocate a buffer large enough to hold the request body. */ - if (msr->msc_reqbody_length + 1 == 0) return -1; + if (msr->msc_reqbody_length + 1 == 0) { + *error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %d", msr->msc_reqbody_length); + return -1; + } msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1); if (msr->msc_reqbody_buffer == NULL) { - msr_log(msr, 1, "Unable to allocate memory to hold request body. Asked for %lu bytes.", + *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %" APR_SIZE_T_FMT " bytes.", msr->msc_reqbody_length + 1); return -1; } @@ -309,7 +325,7 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr) { d += chunks[i]->length; sofar += chunks[i]->length; } else { - msr_log(msr, 1, "Internal error, request body buffer overflow."); + *error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow."); return -1; } } @@ -325,7 +341,10 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr) { /* Create a new array with only one chunk in it. */ msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 2, sizeof(msc_data_chunk *)); - if (msr->msc_reqbody_chunks == NULL) return -1; + if (msr->msc_reqbody_chunks == NULL) { + *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; @@ -337,7 +356,7 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr) { if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length, msr->txcfg->argument_separator, "BODY", msr->arguments, &invalid_count) < 0) { - msr_log(msr, 1, "Initialisation: Error occurred while parsing BODY arguments."); + *error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments."); return -1; } @@ -347,7 +366,7 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr) { /** * Stops receiving the request body. */ -apr_status_t modsecurity_request_body_end(modsec_rec *msr) { +apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { /* Close open file descriptors, if any. */ if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { @@ -364,22 +383,22 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr) { 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); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = my_error_msg; - msr_log(msr, 1, "Multipart error: %s", my_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); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = "Error retrieving arguments."; - msr_log(msr, 1, "Multipart error: %s", my_error_msg); return -1; } } else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) { - return modsecurity_request_body_end_urlencoded(msr); + return modsecurity_request_body_end_urlencoded(msr, error_msg); } #ifdef WITH_LIBXML2 else @@ -400,27 +419,36 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr) { /** * Prepares to forward the request body. */ -apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr) { +apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) { if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { msr->msc_reqbody_chunk_position = 0; msr->msc_reqbody_chunk_offset = 0; msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk)); - if (msr->msc_reqbody_disk_chunk == NULL) return -1; + if (msr->msc_reqbody_disk_chunk == NULL) { + *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk.", sizeof(msc_data_chunk)); + return -1; + } msr->msc_reqbody_disk_chunk->is_permanent = 1; } else 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) return -1; + if (msr->msc_reqbody_disk_chunk == NULL) { + *error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk.", 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) return -1; + 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); + return -1; + } msr->msc_reqbody_fd = open(msr->msc_reqbody_filename, O_RDONLY | O_BINARY); if (msr->msc_reqbody_fd < 0) { - msr_log(msr, 1, "Input filter: Failed to open temporary file for reading: %s", + *error_msg = apr_psprintf(msr->mp, "Failed to open temporary file for reading: %s", msr->msc_reqbody_filename); return -1; } @@ -453,11 +481,14 @@ apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) { * a non-negative value in nbytes. */ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, - msc_data_chunk **chunk, long int nbytes) + msc_data_chunk **chunk, long int nbytes, char **error_msg) { msc_data_chunk **chunks; - if (chunk == NULL) return -1; + if (chunk == NULL) { + *error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk."); + return -1; + } *chunk = NULL; if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { @@ -527,7 +558,7 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, i = read(msr->msc_reqbody_fd, msr->msc_reqbody_disk_chunk->data, my_nbytes); if (i < 0) { - msr_log(msr, 1, "Input filter: Error reading from temporary file: %s", + *error_msg = apr_psprintf(msr->mp, "Input filter: Error reading from temporary file: %s", strerror(errno)); return -1; } @@ -540,7 +571,7 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, return 1; /* More data available. */ } - msr_log(msr, 1, "Internal error, invalid msc_reqbody_storage value: %i", + *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %d", msr->msc_reqbody_storage); return -1; @@ -549,7 +580,7 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, /** * */ -apr_status_t modsecurity_request_body_clear(modsec_rec *msr) { +apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) { /* Release memory we used to store request body data. */ if (msr->msc_reqbody_chunks != NULL) { msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; @@ -574,7 +605,7 @@ apr_status_t modsecurity_request_body_clear(modsec_rec *msr) { if (msr->txcfg->upload_dir != NULL) { keep_body = 1; } else { - msr_log(msr, 1, "Input filter: SecUploadDir is undefined, " + *error_msg = apr_psprintf(msr->mp, "Input filter: SecUploadDir is undefined, " "unable to store PUT file."); } } @@ -589,20 +620,26 @@ apr_status_t modsecurity_request_body_clear(modsec_rec *msr) { /* Construct the new filename. */ put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename); - if (put_basename == NULL) return -1; + if (put_basename == NULL) { + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate basename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename)); + return -1; + } put_filename = apr_psprintf(msr->msc_reqbody_mp, "%s/%s", msr->txcfg->upload_dir, put_basename); - if (put_filename == NULL) return -1; + if (put_filename == NULL) { + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate filename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename)); + return -1; + } if (apr_file_rename(msr->msc_reqbody_filename, put_filename, msr->msc_reqbody_mp) != APR_SUCCESS) { - msr_log(msr, 1, "Failed to rename file from \"%s\" to \"%s\".", + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to rename file from \"%s\" to \"%s\".", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename), log_escape(msr->msc_reqbody_mp, put_filename)); return -1; } else { - msr_log(msr, 4, "Moved file from \"%s\" to \"%s\".", + msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename), log_escape(msr->msc_reqbody_mp, put_filename)); } @@ -611,8 +648,8 @@ apr_status_t modsecurity_request_body_clear(modsec_rec *msr) { if (apr_file_remove(msr->msc_reqbody_filename, msr->msc_reqbody_mp) != APR_SUCCESS) { - msr_log(msr, 1, "Failed to delete temporary file: %s", - msr->msc_reqbody_filename); + *error_msg = apr_psprintf(msr->mp, "Input filter: Failed to delete temporary file: %s", + log_escape(msr->mp, msr->msc_reqbody_filename)); return -1; } diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index 15c2caaf..cc64f23c 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -9,6 +9,7 @@ * */ #include "modsecurity.h" +#include "apache2.h" #include "pdf_protect.h" #include @@ -197,23 +198,6 @@ static int verify_token(modsec_rec *msr, const char *token, char **error_msg) { return 1; } -/** - * - */ -static apr_status_t send_error_bucket(ap_filter_t *f, int status) { - apr_bucket_brigade *brigade = NULL; - apr_bucket *bucket = NULL; - - brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc); - if (brigade == NULL) return APR_EGENERAL; - bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc); - if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(brigade, bucket); - - f->r->connection->keepalive = AP_CONN_CLOSE; - return ap_pass_brigade(f->next, brigade); -} - /** * */ @@ -226,7 +210,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { ap_remove_output_filter(f); - return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); + return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR); } if (msr->txcfg->pdfp_enabled == 1) { @@ -328,7 +312,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { apr_table_set(r->headers_out, "Location", new_uri); - return send_error_bucket(f, REDIRECT_STATUS); + return send_error_bucket(msr, f, REDIRECT_STATUS); } } else { /* Token found. */ char *my_error_msg = NULL; From 63a47c370e55fa6fefde4fe56ac31aec2b50ebfc Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 27 Sep 2007 21:34:29 +0000 Subject: [PATCH 128/372] Prefer %d string formatter to %i so we do not get warnings on some platforms. --- apache2/apache2_config.c | 2 +- apache2/apache2_io.c | 14 +++++++------- apache2/apache2_util.c | 16 ++++++++-------- apache2/mod_security2.c | 18 +++++++++--------- apache2/modsecurity.c | 6 +++--- apache2/msc_logging.c | 24 ++++++++++++------------ apache2/msc_multipart.c | 12 ++++++------ apache2/msc_xml.c | 4 ++-- apache2/pdf_protect.c | 4 ++-- apache2/persist_dbm.c | 14 +++++++------- apache2/re.c | 26 +++++++++++++------------- apache2/re_actions.c | 24 ++++++++++++------------ apache2/re_operators.c | 36 ++++++++++++++++++------------------ apache2/re_variables.c | 14 +++++++------- 14 files changed, 107 insertions(+), 107 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 5b0eeee8..88401165 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1114,7 +1114,7 @@ static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, const cha msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); #ifdef DEBUG_CONF - fprintf(stderr, "Added exception %x (%i %s) to dcfg %x.\n", re, re->type, re->param, dcfg); + fprintf(stderr, "Added exception %x (%d %s) to dcfg %x.\n", re, re->type, re->param, dcfg); #endif return NULL; diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 4d098277..0e21d3f1 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -50,7 +50,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, } if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Forwarding input: mode=%i, block=%i, nbytes=%" APR_OFF_T_FMT + msr_log(msr, 4, "Input filter: Forwarding input: mode=%d, block=%d, nbytes=%" APR_OFF_T_FMT " (f %x, r %x).", mode, block, nbytes, f, f->r); } @@ -199,12 +199,12 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ); if (rc != APR_SUCCESS) { - *error_msg = apr_psprintf(msr->mp, "Failed reading input / bucket (%i): %s", rc, get_apr_error(msr->mp, rc)); + *error_msg = apr_psprintf(msr->mp, "Failed reading input / bucket (%d): %s", rc, get_apr_error(msr->mp, rc)); return -1; } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Input filter: Bucket type %s contains %i bytes.", + msr_log(msr, 9, "Input filter: Bucket type %s contains %d bytes.", bucket->type->name, buflen); } @@ -390,7 +390,7 @@ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) { } if (msr->txcfg->debuglog_level >= log_level) { - msr_log(msr, log_level, "Output filter: Error while forwarding response data (%i): %s", + msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): %s", rc, get_apr_error(msr->mp, rc)); } @@ -440,7 +440,7 @@ static int flatten_response_body(modsec_rec *msr) { rc = apr_brigade_flatten(msr->of_brigade, msr->resbody_data, &msr->resbody_length); if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Output filter: Failed to flatten brigade (%i): %s", rc, + msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, get_apr_error(msr->mp, rc)); return -1; } @@ -583,7 +583,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { msr->of_status = OF_STATUS_COMPLETE; msr->resbody_status = RESBODY_STATUS_ERROR; - msr_log(msr, 1, "Output filter: Failed to read bucket (rc %i): %s", + msr_log(msr, 1, "Output filter: Failed to read bucket (rc %d): %s", rc, get_apr_error(r->pool, rc)); ap_remove_output_filter(f); @@ -591,7 +591,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Output filter: Bucket type %s contains %i bytes.", + msr_log(msr, 9, "Output filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.", bucket->type->name, buflen); } diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 0b4e9aca..f890683f 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -86,7 +86,7 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * procnew = apr_pcalloc(r->pool, sizeof(*procnew)); if (procnew == NULL) { - msr_log(msr, 1, "Exec: Unable to allocate %i bytes.", sizeof(*procnew)); + msr_log(msr, 1, "Exec: Unable to allocate %d bytes.", sizeof(*procnew)); return -1; } @@ -171,17 +171,17 @@ void record_time_checkpoint(modsec_rec *msr, int checkpoint_no) { msr->time_checkpoint_3 = now; break; default : - msr_log(msr, 1, "Internal Error: Unknown checkpoint: %i", checkpoint_no); + msr_log(msr, 1, "Internal Error: Unknown checkpoint: %d", checkpoint_no); return; break; } /* Apache-specific stuff. */ apr_snprintf(note, 99, "%" APR_TIME_T_FMT, (now - msr->request_time)); - apr_snprintf(note_name, 99, "mod_security-time%i", 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 #%i: %s", checkpoint_no, note); + msr_log(msr, 4, "Time #%d: %s", checkpoint_no, note); } /** @@ -240,7 +240,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, /* Construct the message. */ apr_vsnprintf(str1, sizeof(str1), text, ap); - apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%lx][rid#%lx][%s][%i] %s\n", + apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%lx][rid#%lx][%s][%d] %s\n", current_logtime(msr->mp), ap_get_server_name(r), (unsigned long)(r->server), (unsigned long)r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), level, str1); @@ -314,15 +314,15 @@ char *format_error_log_message(apr_pool_t *mp, error_message *em) { } if (em->line > 0) { - s_line = apr_psprintf(mp, "[line %i] ", em->line); + s_line = apr_psprintf(mp, "[line %d] ", em->line); if (s_line == NULL) return NULL; } - s_level = apr_psprintf(mp, "[level %i] ", em->level); + s_level = apr_psprintf(mp, "[level %d] ", em->level); if (s_level == NULL) return NULL; if (em->status != 0) { - s_status = apr_psprintf(mp, "[status %i] ", em->status); + s_status = apr_psprintf(mp, "[status %d] ", em->status); if (s_status == NULL) return NULL; } diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index d1d69284..4f6eeea9 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -72,7 +72,7 @@ int perform_interception(modsec_rec *msr) { } if (msr->phase > 4) { - msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %i.", msr->phase); + msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %d.", msr->phase); msr->was_intercepted = 0; return DECLINED; } @@ -80,7 +80,7 @@ int perform_interception(modsec_rec *msr) { /* OK, we're good to go. */ actionset = msr->intercept_actionset; - phase_text = apr_psprintf(msr->mp, " (phase %i)", msr->phase); + phase_text = apr_psprintf(msr->mp, " (phase %d)", msr->phase); /* By default we log at level 1 but we switch to 4 * if a nolog action was used or this is not the initial request @@ -91,7 +91,7 @@ int perform_interception(modsec_rec *msr) { /* Pause the request first (if configured and the initial request). */ if (actionset->intercept_pause) { msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " - "%i msec.", actionset->intercept_pause); + "%d msec.", actionset->intercept_pause); /* apr_sleep accepts microseconds */ apr_sleep((apr_interval_time_t)(actionset->intercept_pause * 1000)); } @@ -101,13 +101,13 @@ int perform_interception(modsec_rec *msr) { case ACTION_DENY : if (actionset->intercept_status != 0) { status = actionset->intercept_status; - message = apr_psprintf(msr->mp, "Access denied with code %i%s.", + message = apr_psprintf(msr->mp, "Access denied with code %d%s.", status, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Internal Error: Invalid status code requested %i).", + "(Internal Error: Invalid status code requested %d).", phase_text, actionset->intercept_status); } break; @@ -189,7 +189,7 @@ int perform_interception(modsec_rec *msr) { status = HTTP_MOVED_TEMPORARILY; } message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " - "status %i%s.", + "status %d%s.", log_escape_nq(msr->mp, actionset->intercept_uri), status, phase_text); break; @@ -204,7 +204,7 @@ int perform_interception(modsec_rec *msr) { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Internal Error: invalid interception action %i).", + "(Internal Error: invalid interception action %d).", phase_text, actionset->intercept_action); break; } @@ -780,7 +780,7 @@ static void sec_guardian_logger(request_rec *r, request_rec *origr, modsec_rec * * The fields SESSION_ID, MODSEC_MESSAGE, and MODSEC_RATING are not used at the moment. */ - str2 = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT " %" APR_TIME_T_FMT " \"%s\" %i", + str2 = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT " %" APR_TIME_T_FMT " \"%s\" %d", duration, apr_time_sec(duration), log_escape(msr->mp, modsec_message), modsec_rating); if (str2 == NULL) return; @@ -801,7 +801,7 @@ static void sec_guardian_logger(request_rec *r, request_rec *origr, modsec_rec * limit = limit - strlen(str2) - 5; if (limit <= 0) { - msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %i", PIPE_BUF); + msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF); return; } diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index c6722398..bd927be9 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -323,13 +323,13 @@ static int is_response_status_relevant(modsec_rec *msr, int status) { return 0; } - apr_snprintf(buf, sizeof(buf), "%i", status); + apr_snprintf(buf, sizeof(buf), "%d", status); rc = msc_regexec(msr->txcfg->auditlog_relevant_regex, buf, strlen(buf), &my_error_msg); if (rc >= 0) return 1; if (rc == PCRE_ERROR_NOMATCH) return 0; - msr_log(msr, 1, "Regex processing failed (rc %i): %s", rc, my_error_msg); + msr_log(msr, 1, "Regex processing failed (rc %d): %s", rc, my_error_msg); return 0; } @@ -473,7 +473,7 @@ apr_status_t modsecurity_process_phase(modsec_rec *msr, int phase) { return modsecurity_process_phase_logging(msr); break; default : - msr_log(msr, 1, "Invalid processing phase: %i", msr->phase); + msr_log(msr, 1, "Invalid processing phase: %d", msr->phase); return -1; break; } diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index bcf1f1de..2b4ac634 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -78,7 +78,7 @@ char *construct_log_vcombinedus(modsec_rec *msr) { /* sessionid */ sessionid = (msr->sessionid == NULL ? "-" : msr->sessionid); - return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %i %" APR_OFF_T_FMT " \"%s\" \"%s\" %s \"%s\"", + return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %d %" APR_OFF_T_FMT " \"%s\" \"%s\" %s \"%s\"", log_escape_nq(msr->mp, msr->hostname), msr->remote_addr, log_escape_nq(msr->mp, remote_user), log_escape_nq(msr->mp, local_user), current_logtime(msr->mp), ((msr->request_line == NULL) ? "" : log_escape(msr->mp, msr->request_line)), @@ -149,7 +149,7 @@ char *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_li limit -= strlen(sessionid); /* session id */ if (limit <= 0) { - msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %i", PIPE_BUF); + msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %d", PIPE_BUF); return NULL; } @@ -189,21 +189,21 @@ char *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_li limit -= strlen(user_agent); if (limit <= 0) { - msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %i.", PIPE_BUF); + msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %d.", PIPE_BUF); return NULL; } /* use what's left for the request line */ if ((int)strlen(the_request) > limit) { the_request[limit] = '\0'; - msr_log(msr, 9, "GuardianLog: Reduced the_request to %i bytes.", limit); + msr_log(msr, 9, "GuardianLog: Reduced the_request to %d bytes.", limit); } } else { /* Yay! We have enough space! */ *was_limited = 0; } - return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %i %s \"%s\" \"%s\" %s \"%s\"", + return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %d %s \"%s\" \"%s\" %s \"%s\"", hostname, msr->remote_addr, remote_user, local_user, current_logtime(msr->mp), the_request, msr->response_status, bytes_sent, referer, user_agent, @@ -284,7 +284,7 @@ static void sanitise_request_line(modsec_rec *msr) { j = arg->value_origin_offset; while((*p != '\0')&&(j--)) p++; if (*p == '\0') { - msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %i of QUERY_STRING" + msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %d of QUERY_STRING" "because the request line is too short.", log_escape_ex(msr->mp, arg->name, arg->name_len), arg->value_origin_offset); @@ -297,7 +297,7 @@ static void sanitise_request_line(modsec_rec *msr) { *p++ = '*'; } if (*p == '\0') { - msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %i (size %i) " + msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %d (size %d) " "of QUERY_STRING because the request line is too short.", log_escape_ex(msr->mp, arg->name, arg->name_len), arg->value_origin_offset, arg->value_origin_len); @@ -418,7 +418,7 @@ void sec_audit_logger(modsec_rec *msr) { /* Format: time transaction_id remote_addr remote_port local_addr local_port */ - text = apr_psprintf(msr->mp, "[%s] %s %s %i %s %i", + text = apr_psprintf(msr->mp, "[%s] %s %s %d %s %d", current_logtime(msr->mp), msr->txid, msr->remote_addr, msr->remote_port, msr->local_addr, msr->local_port); sec_auditlog_write(msr, text, strlen(text)); @@ -635,7 +635,7 @@ void sec_audit_logger(modsec_rec *msr) { text = apr_psprintf(msr->mp, "%s %s\n", msr->response_protocol, msr->status_line); } else { - text = apr_psprintf(msr->mp, "%s %i\n", msr->response_protocol, + text = apr_psprintf(msr->mp, "%s %d\n", msr->response_protocol, msr->response_status); } sec_auditlog_write(msr, text, strlen(text)); @@ -691,7 +691,7 @@ void sec_audit_logger(modsec_rec *msr) { /* Action */ if (msr->was_intercepted) { - text = apr_psprintf(msr->mp, "Action: Intercepted (phase %i)\n", msr->intercept_phase); + text = apr_psprintf(msr->mp, "Action: Intercepted (phase %d)\n", msr->intercept_phase); sec_auditlog_write(msr, text, strlen(text)); } @@ -853,7 +853,7 @@ void sec_audit_logger(modsec_rec *msr) { /* Calculate hash of the entry. */ apr_md5_final(md5hash, &msr->new_auditlog_md5ctx); - str2 = apr_psprintf(msr->mp, "%s %i %i md5:%s", msr->new_auditlog_filename, 0, + str2 = apr_psprintf(msr->mp, "%s %d %d md5:%s", msr->new_auditlog_filename, 0, msr->new_auditlog_size, bytes2hex(msr->mp, md5hash, 16)); if (str2 == NULL) return; @@ -873,7 +873,7 @@ void sec_audit_logger(modsec_rec *msr) { limit = limit - strlen(str2) - 5; if (limit <= 0) { - msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %i", PIPE_BUF); + msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF); return; } diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 199e33f0..dccc4325 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -219,7 +219,7 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { rc = multipart_parse_content_disposition(msr, header_value); if (rc < 0) { - *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%i): %s.", + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%d): %s.", rc, log_escape_nq(msr->mp, header_value)); return -1; } @@ -451,7 +451,7 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { } } else { - *error_msg = apr_psprintf(msr->mp, "Multipart: unknown part type %i", msr->mpd->mpp->type); + *error_msg = apr_psprintf(msr->mp, "Multipart: unknown part type %d", msr->mpd->mpp->type); return -1; } @@ -830,7 +830,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, msr->mpd->flag_data_before = 1; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %i bytes)", size); + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %d bytes)", size); } return 1; @@ -992,7 +992,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, */ msr->mpd->flag_error = 1; *error_msg = apr_psprintf(msr->mp, - "Multipart: Part header line over %i bytes long", + "Multipart: Part header line over %d bytes long", MULTIPART_BUF_SIZE); return -1; } @@ -1028,7 +1028,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, msr->mpd->flag_data_after = 1; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%i bytes left)", inleft); + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%d bytes left)", inleft); } return 1; @@ -1047,7 +1047,7 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { if (msr->mpd == NULL) return -1; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Cleanup started (remove files %i).", msr->upload_remove_files); + msr_log(msr, 4, "Multipart: Cleanup started (remove files %d).", msr->upload_remove_files); } if (msr->upload_remove_files == 0) { diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index df1e62d5..4e31544d 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -33,7 +33,7 @@ static void xml_receive_sax_error(void *data, const char *msg, ...) { if (msr == NULL) return; - apr_snprintf(message, sizeof(message), "%s (line %i pos %i)", + apr_snprintf(message, sizeof(message), "%s (line %d pos %d)", log_escape_nq(msr->mp, msr->xml->parsing_ctx->lastError.message), msr->xml->parsing_ctx->lastError.line, msr->xml->parsing_ctx->lastError.int2); @@ -109,7 +109,7 @@ int xml_complete(modsec_rec *msr, char **error_msg) { /* Clean up everything else. */ xmlFreeParserCtxt(msr->xml->parsing_ctx); msr->xml->parsing_ctx = NULL; - msr_log(msr, 4, "XML: Parsing complete (well_formed %i).", msr->xml->well_formed); + msr_log(msr, 4, "XML: Parsing complete (well_formed %d).", msr->xml->well_formed); if (msr->xml->well_formed != 1) { *error_msg = apr_psprintf(msr->mp, "XML: Failed parsing document."); diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index cc64f23c..4ba611dd 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -74,7 +74,7 @@ static char *create_token(modsec_rec *msr) { } current_time = apr_time_sec(apr_time_now()); - time_string = apr_psprintf(msr->mp, "%i", current_time + timeout); + time_string = apr_psprintf(msr->mp, "%d", current_time + timeout); if (time_string == NULL) return NULL; hash = create_hash(msr, time_string); @@ -420,7 +420,7 @@ int pdfp_check(modsec_rec *msr) { if ((msr->r->method_number != M_GET)&&(cfg->pdfp_only_get != 0)) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Not intercepting a GET/HEAD request " - "(method=%s/%i).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); + "(method=%s/%d).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); } return 0; diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index f5c31cef..7cacfe67 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -163,10 +163,10 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, /* NOTE: No rate if there has been no time elapsed */ td = (apr_time_sec(apr_time_now()) - create_time); if (td == 0) { - var->value = apr_psprintf(msr->mp, "%i", 0); + var->value = apr_psprintf(msr->mp, "%d", 0); } else { - var->value = apr_psprintf(msr->mp, "%i", + var->value = apr_psprintf(msr->mp, "%d", (int)((60 * counter)/td)); } var->value_len = strlen(var->value); @@ -279,7 +279,7 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { int timeout = atoi(var->value); var = (msc_string *)apr_table_get(col, "__expire_KEY"); if (var != NULL) { - var->value = apr_psprintf(msr->mp, "%i", (int)(apr_time_sec(apr_time_now()) + timeout)); + var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(apr_time_now()) + timeout)); var->value_len = strlen(var->value); } } @@ -294,7 +294,7 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { var->name_len = strlen(var->name); apr_table_setn(col, var->name, (void *)var); } - var->value = apr_psprintf(msr->mp, "%i", (int)(apr_time_sec(apr_time_now()))); + var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(apr_time_now()))); var->value_len = strlen(var->value); } @@ -310,7 +310,7 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { } else { counter = atoi(var->value); } - var->value = apr_psprintf(msr->mp, "%i", counter + 1); + var->value = apr_psprintf(msr->mp, "%d", counter + 1); var->value_len = strlen(var->value); } @@ -459,7 +459,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { } apr_sdbm_unlock(dbm); - msr_log(msr, 9, "Found %i record(s) in file \"%s\".", keys_arr->nelts, + msr_log(msr, 9, "Found %d record(s) in file \"%s\".", keys_arr->nelts, log_escape(msr->mp, dbm_filename)); /* Now retrieve the entires one by one. */ @@ -493,7 +493,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { } else { unsigned int expiry_time = atoi(var->value); - msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %i seconds.", + msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %d seconds.", log_escape(msr->mp, col_name), log_escape(msr->mp, key.dptr), expiry_time - now); diff --git a/apache2/re.c b/apache2/re.c index 421df5cc..87f2bd88 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -316,7 +316,7 @@ int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, continue; } - *error_msg = apr_psprintf(mp, "Unexpected character at position %i: %s", + *error_msg = apr_psprintf(mp, "Unexpected character at position %d: %s", (int)(p - text), text); return -1; } @@ -351,14 +351,14 @@ int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, for(;;) { if (*p == '\0') { - *error_msg = apr_psprintf(mp, "Missing closing quote at position %i: %s", + *error_msg = apr_psprintf(mp, "Missing closing quote at position %d: %s", (int)(p - text), text); free(value); return -1; } else if (*p == '\\') { if ( (*(p + 1) == '\0') || ((*(p + 1) != '\'')&&(*(p + 1) != '\\')) ) { - *error_msg = apr_psprintf(mp, "Invalid quoted pair at position %i: %s", + *error_msg = apr_psprintf(mp, "Invalid quoted pair at position %d: %s", (int)(p - text), text); free(value); return -1; @@ -654,7 +654,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "This phase consists of %i rule(s).", arr->nelts); + msr_log(msr, 9, "This phase consists of %d rule(s).", arr->nelts); } /* Loop through the rules in the selected set. */ @@ -730,7 +730,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) #endif if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Rule returned %i.", rc); + msr_log(msr, 4, "Rule returned %d.", rc); } if (rc == RULE_NO_MATCH) { @@ -784,14 +784,14 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) if (rule->chain_starter->actionset->skip_count > 0) { skip = rule->chain_starter->actionset->skip_count; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping %i rules/chains (from a chain).", skip); + msr_log(msr, 4, "Skipping %d rules/chains (from a chain).", skip); } } } else if (rule->actionset->skip_count > 0) { skip = rule->actionset->skip_count; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Skipping %i rules/chains.", skip); + msr_log(msr, 4, "Skipping %d rules/chains.", skip); } } } @@ -801,7 +801,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) return -1; } else { - msr_log(msr, 1, "Rule processing failed with unknown return code: %i.", rc); + msr_log(msr, 1, "Rule processing failed with unknown return code: %d.", rc); return -1; } } @@ -852,7 +852,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 %i: %" APR_TIME_T_FMT " usec", msr->phase, ((apr_time_now() - time1) / 10000)); + msr_log(msr, 1, "Phase %d: %" APR_TIME_T_FMT " usec", msr->phase, ((apr_time_now() - time1) / 10000)); rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { @@ -1425,7 +1425,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (targets[i]->is_counting) { /* Count how many there are and just add the score to the target list. */ msre_var *newvar = (msre_var *)apr_pmemdup(mptmp, targets[i], sizeof(msre_var)); - newvar->value = apr_psprintf(mptmp, "%i", list_count); + newvar->value = apr_psprintf(mptmp, "%d", list_count); newvar->value_len = strlen(newvar->value); apr_table_addn(tartab, newvar->name, (void *)newvar); } else { @@ -1583,7 +1583,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { var->value_len = crec->val_len; } - msr_log(msr, 9, "T (%i) %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\" [cached hits=%d]", crec->changed, crec->path, log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); rc = execute_operator(var, rule, msr, acting_actionset, mptmp); @@ -1682,7 +1682,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { var->value_len = crec->val_len; } - msr_log(msr, 9, "T (%i) %s: \"%s\" [cached hits=%i]", crec->changed, metadata->name, log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); + 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; } } @@ -1715,7 +1715,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "T (%i) %s: \"%s\"", rc, metadata->name, + msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, metadata->name, log_escape_nq_ex(mptmp, var->value, var->value_len)); } } diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 1c5f65ce..3530779c 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -605,7 +605,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (pv == -1) return -1; msr->txcfg->reqbody_access = pv; msr->usercfg->reqbody_access = pv; - msr_log(msr, 4, "Ctl: Set requestBodyAccess to %i.", pv); + msr_log(msr, 4, "Ctl: Set requestBodyAccess to %d.", pv); return 1; } else @@ -621,7 +621,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (pv == -1) return -1; msr->txcfg->resbody_access = pv; msr->usercfg->resbody_access = pv; - msr_log(msr, 4, "Ctl: Set responseBodyAccess to %i.", pv); + msr_log(msr, 4, "Ctl: Set responseBodyAccess to %d.", pv); return 1; } else @@ -641,7 +641,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, msr->usercfg->auditlog_flag = AUDITLOG_RELEVANT; } - msr_log(msr, 4, "Ctl: Set auditEngine to %i.", msr->txcfg->auditlog_flag); // TODO + msr_log(msr, 4, "Ctl: Set auditEngine to %d.", msr->txcfg->auditlog_flag); // TODO return 1; } else @@ -684,7 +684,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (strcmp(name, "debugLogLevel") == 0) { msr->txcfg->debuglog_level = atoi(value); msr->usercfg->debuglog_level = atoi(value); - msr_log(msr, 4, "Ctl: Set debugLogLevel to %i.", msr->txcfg->debuglog_level); + msr_log(msr, 4, "Ctl: Set debugLogLevel to %d.", msr->txcfg->debuglog_level); return 1; } else @@ -969,7 +969,7 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm if (value < 0) value = 0; /* Counters never go below zero. */ /* Put the variable back. */ - var->value = apr_psprintf(msr->mp, "%i", value); + var->value = apr_psprintf(msr->mp, "%d", value); var->value_len = strlen(var->value); apr_table_setn(target_col, var->name, (void *)var); @@ -1056,7 +1056,7 @@ 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); - var->value = apr_psprintf(msr->mp, "%i", (int)(apr_time_sec(msr->request_time) + var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(msr->request_time) + atoi(var_value))); var->value_len = strlen(var->value); apr_table_setn(target_col, var->name, (void *)var); @@ -1152,17 +1152,17 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t /* Only change the value if it differs. */ if (new_value != current_value) { - var->value = apr_psprintf(msr->mp, "%i", (int)new_value); + var->value = apr_psprintf(msr->mp, "%d", (int)new_value); var->value_len = strlen(var->value); - msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %li to %li (%i seconds since " + msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %li to %li (%d seconds since " "last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), current_value, new_value, current_time - last_update_time); apr_table_set(msr->collections_dirty, col_name, "1"); } else { msr_log(msr, 9, "Not deprecating variable \"%s.%s\" because the new value (%li) is " - "the same as the old one (%li) (%i seconds since last update).", + "the same as the old one (%li) (%d seconds since last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), current_value, new_value, current_time - last_update_time); } @@ -1201,7 +1201,7 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = "__expire_KEY"; var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%i", (int)(apr_time_sec(msr->request_time) + 3600)); + var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(msr->request_time) + 3600)); var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); @@ -1217,7 +1217,7 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var = apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = "TIMEOUT"; var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%i", 3600); + var->value = apr_psprintf(msr->mp, "%d", 3600); var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); @@ -1249,7 +1249,7 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var = apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = "CREATE_TIME"; var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%i", (int)apr_time_sec(msr->request_time)); + var->value = apr_psprintf(msr->mp, "%d", (int)apr_time_sec(msr->request_time)); var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 1a10d7a6..5515172b 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -67,7 +67,7 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { /* Compile pattern */ regex = msc_pregcomp(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); if (regex == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %i): %s", + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %d): %s", erroffset, errptr); return 0; } @@ -135,14 +135,14 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c for(i = 0; i < rc; i++) { msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; - s->name = apr_psprintf(msr->mp, "%i", i); + s->name = apr_psprintf(msr->mp, "%d", i); s->value = apr_pstrmemdup(msr->mp, target + ovector[2*i], ovector[2*i + 1] - ovector[2*i]); s->value_len = (ovector[2*i + 1] - ovector[2*i]); if ((s->name == NULL)||(s->value == NULL)) return -1; apr_table_setn(msr->tx_vars, s->name, (void *)s); if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Adding regex subexpression to TXVARS (%i): %s", i, + msr_log(msr, 9, "Adding regex subexpression to TXVARS (%d): %s", i, log_escape_nq_ex(msr->mp, s->value, s->value_len)); } } @@ -150,7 +150,7 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* Unset the remaining ones (from previous invocations). */ for(i = rc; i <= 9; i++) { char buf[24]; - apr_snprintf(buf, sizeof(buf), "%i", i); + apr_snprintf(buf, sizeof(buf), "%d", i); apr_table_unset(msr->tx_vars, buf); } } @@ -352,7 +352,7 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* Unset the remaining ones (from previous invocations). */ for(i = rc; i <= 9; i++) { char buf[2]; - apr_snprintf(buf, sizeof(buf), "%i", i); + apr_snprintf(buf, sizeof(buf), "%d", i); apr_table_unset(msr->tx_vars, buf); } } @@ -941,7 +941,7 @@ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, /* Construct the host name we want to resolve. */ if (sscanf(target, "%d.%d.%d.%d", &h0, &h1, &h2, &h3) == 4) { /* IPv4 address */ - name_to_check = apr_psprintf(msr->mp, "%i.%i.%i.%i.%s", h3, h2, h1, h0, rule->op_param); + name_to_check = apr_psprintf(msr->mp, "%d.%d.%d.%d.%s", h3, h2, h1, h0, rule->op_param); } else { /* Assume the input is a domain name. */ name_to_check = apr_psprintf(msr->mp, "%s.%s", target, rule->op_param); @@ -1032,7 +1032,7 @@ static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) { /* Single value. */ int x = atoi(p); if ((x < 0)||(x > 255)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range value: %i", x); + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range value: %d", x); return 0; } table[x>>3] = (table[x>>3] | (1 << (x & 0x7))); @@ -1042,16 +1042,16 @@ static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) { int end = atoi(s + 1); if ((start < 0)||(start > 255)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range start value: %i", + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range start value: %d", start); return 0; } if ((end < 0)||(end > 255)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range end value: %i", end); + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range end value: %d", end); return 0; } if (start > end) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range: %i-%i", start, end); + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range: %d-%d", start, end); return 0; } @@ -1089,7 +1089,7 @@ static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, m int x = ((unsigned char *)var->value)[i]; if (!(table[x >> 3] & (1 << (x & 0x7)))) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Value %i in %s outside range: %s", x, var->name, rule->op_param); + msr_log(msr, 9, "Value %d in %s outside range: %s", x, var->name, rule->op_param); } count++; } @@ -1097,7 +1097,7 @@ static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, m if (count == 0) return 0; /* Valid - no match. */ - *error_msg = apr_psprintf(msr->mp, "Found %i byte(s) in %s outside range: %s.", + *error_msg = apr_psprintf(msr->mp, "Found %d byte(s) in %s outside range: %s.", count, var->name, rule->op_param); return 1; /* Invalid - match.*/ @@ -1161,7 +1161,7 @@ static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, break; case -1 : default : - *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Internal Error (rc = %i)", rc); + *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Internal Error (rc = %d)", rc); return -1; break; @@ -1330,7 +1330,7 @@ static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } else { - *error_msg = apr_psprintf(msr->mp, "Operator EQ match: %i.", right); + *error_msg = apr_psprintf(msr->mp, "Operator EQ match: %d.", right); /* Match. */ return 1; } @@ -1359,7 +1359,7 @@ static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } else { - *error_msg = apr_psprintf(msr->mp, "Operator GT match: %i.", right); + *error_msg = apr_psprintf(msr->mp, "Operator GT match: %d.", right); /* Match. */ return 1; } @@ -1388,7 +1388,7 @@ static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } else { - *error_msg = apr_psprintf(msr->mp, "Operator LT match: %i.", right); + *error_msg = apr_psprintf(msr->mp, "Operator LT match: %d.", right); /* Match. */ return 1; } @@ -1417,7 +1417,7 @@ static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } else { - *error_msg = apr_psprintf(msr->mp, "Operator GE match: %i.", right); + *error_msg = apr_psprintf(msr->mp, "Operator GE match: %d.", right); /* Match. */ return 1; } @@ -1446,7 +1446,7 @@ static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } else { - *error_msg = apr_psprintf(msr->mp, "Operator LE match: %i.", right); + *error_msg = apr_psprintf(msr->mp, "Operator LE match: %d.", right); /* Match. */ return 1; } diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 6ba6cbe8..319ce69b 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -69,7 +69,7 @@ static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { regex = msc_pregcomp(ruleset->mp, pattern, PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); if (regex == NULL) { - return apr_psprintf(ruleset->mp, "Error compiling pattern (pos %i): %s", + return apr_psprintf(ruleset->mp, "Error compiling pattern (pos %d): %s", erroffset, errptr); } @@ -404,7 +404,7 @@ static int var_rule_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, return var_simple_generate(var, vartab, mptmp, actionset->rev); } else if ((strcasecmp(var->param, "severity") == 0)&&(actionset->severity != -1)) { - char *value = apr_psprintf(mptmp, "%i", actionset->severity); + char *value = apr_psprintf(mptmp, "%d", actionset->severity); return var_simple_generate(var, vartab, mptmp, value); } else if ((strcasecmp(var->param, "msg") == 0)&&(actionset->msg != NULL)) { @@ -486,7 +486,7 @@ static int var_reqbody_processor_error_generate(modsec_rec *msr, msre_var *var, { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%i", msr->msc_reqbody_error); + rvar->value = apr_psprintf(mptmp, "%d", msr->msc_reqbody_error); rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -681,7 +681,7 @@ static int var_remote_host_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_remote_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - char *value = apr_psprintf(mptmp, "%i", msr->remote_port); + char *value = apr_psprintf(mptmp, "%d", msr->remote_port); return var_simple_generate(var, vartab, mptmp, value); } @@ -1539,7 +1539,7 @@ static int var_time_epoch_generate(modsec_rec *msr, msre_var *var, msre_rule *ru tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%i", (int)tc); + rvar->value = apr_psprintf(mptmp, "%d", (int)tc); rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1800,7 +1800,7 @@ static int var_server_name_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_server_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - char *value = apr_psprintf(mptmp, "%i", msr->local_port); + char *value = apr_psprintf(mptmp, "%d", msr->local_port); return var_simple_generate(var, vartab, mptmp, value); } @@ -2015,7 +2015,7 @@ static int var_response_protocol_generate(modsec_rec *msr, msre_var *var, msre_r static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - const char *value = apr_psprintf(mptmp, "%i", msr->response_status); + const char *value = apr_psprintf(mptmp, "%d", msr->response_status); return var_simple_generate(var, vartab, mptmp, value); } From 8b6f0e72a7046db84533be97a99ec911125c2c5f Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 27 Sep 2007 21:38:33 +0000 Subject: [PATCH 129/372] Wrap PERFORMANCE_MEASUREMENT variable as conditional compile. --- apache2/re.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apache2/re.c b/apache2/re.c index 87f2bd88..467091b5 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -663,7 +663,9 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; +#if defined(PERFORMANCE_MEASUREMENT) apr_time_t time1 = 0; +#endif /* NEXT_CHAIN is used when one of the rules in a chain * fails to match and then we need to skip the remaining From fe1021e36949a5132e8989eaf5f546e49fd1a00b Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 28 Sep 2007 20:02:02 +0000 Subject: [PATCH 130/372] More cleanup of error messages and marking as relevant. See #4. --- CHANGES | 3 ++- apache2/apache2_io.c | 12 +++++++++--- apache2/apache2_util.c | 16 +++++++++++----- apache2/mod_security2.c | 17 +++++++++-------- apache2/msc_logging.c | 5 ++++- apache2/msc_reqbody.c | 22 +++++++++++++++++++++- apache2/pdf_protect.c | 2 ++ apache2/re_operators.c | 9 +++++---- 8 files changed, 63 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index 9c9b0d2f..83c5194c 100644 --- a/CHANGES +++ b/CHANGES @@ -3,7 +3,8 @@ ------------------------ * Situations where ModSecurity will intercept, generate an error or log - an alert to the debug log are now marked as 'relevant'. + a level 1-3 message to the debug log are now marked as 'relevant' and may + generate an audit log entry. * Do not process subrequests in phase 2-4. diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 0e21d3f1..ea73f71f 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -275,7 +275,10 @@ static int output_filter_should_run(modsec_rec *msr, request_rec *r) { char *p = NULL; content_type = apr_pstrdup(msr->mp, r->content_type); - if (content_type == NULL) return -1; + if (content_type == NULL) { + msr_log(msr, 1, "Output filter: Failed to allocate memory for content type."); + return -1; + } /* Hide the character encoding information * if present. Sometimes the content type header @@ -316,11 +319,14 @@ static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, apr_status_t rc; msr->of_brigade = apr_brigade_create(msr->mp, f->c->bucket_alloc); - if (msr->of_brigade == NULL) return -1; + if (msr->of_brigade == NULL) { + msr_log(msr, 1, "Output filter: Failed to create brigade."); + return -1; + } msr->of_status = OF_STATUS_IN_PROGRESS; rc = output_filter_should_run(msr, r); - if (rc < 0) return -1; + if (rc < 0) return -1; /* output_filter_should_run() generates error msg */ if (rc == 0) return 0; /* Do not check the output limit if we are willing to diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index f890683f..e9c75b73 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -20,14 +20,20 @@ apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { apr_bucket_brigade *brigade = NULL; apr_bucket *bucket = NULL; - /* Force relevancy for all errors */ - if (msr != NULL) { - msr->is_relevant++; - } - /* 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/mod_security2.c b/apache2/mod_security2.c index 4f6eeea9..53faa31d 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -338,7 +338,10 @@ static modsec_rec *create_tx_context(request_rec *r) { msr->hostname = ap_get_server_name(r); /* Invoke the engine to continue with initialisation */ - if (modsecurity_tx_init(msr) < 0) return NULL; + if (modsecurity_tx_init(msr) < 0) { + msr_log(msr, 1, "Failed to initialising transaction (txid %s).", msr->txid); + return NULL; + } store_tx_context(msr, r); @@ -400,10 +403,8 @@ static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_te /* Initialise ModSecurity engine */ modsecurity = modsecurity_create(mp, MODSEC_ONLINE); if (modsecurity == NULL) { - /* ENH Since s not available, how do we log from here? stderr? - * ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - * "ModSecurity: Failed to initialise engine."); - */ + ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, + "ModSecurity: Failed to initialise engine."); return HTTP_INTERNAL_SERVER_ERROR; } @@ -639,20 +640,20 @@ static int hook_request_late(request_rec *r) { if (rc < 0) { switch(rc) { case -1 : - if ((my_error_msg != NULL)||(msr->is_relevant == 0)) { + if (my_error_msg != NULL) { msr_log(msr, 1, "%s", my_error_msg); } return HTTP_INTERNAL_SERVER_ERROR; break; case -4 : /* Timeout. */ - if ((my_error_msg != NULL)||(msr->is_relevant == 0)) { + if (my_error_msg != NULL) { msr_log(msr, 4, "%s", my_error_msg); } r->connection->keepalive = AP_CONN_CLOSE; return HTTP_REQUEST_TIME_OUT; break; case -5 : /* Request body limit reached. */ - if ((my_error_msg != NULL)||(msr->is_relevant == 0)) { + if (my_error_msg != NULL) { msr_log(msr, 1, "%s", my_error_msg); } r->connection->keepalive = AP_CONN_CLOSE; diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 2b4ac634..334761f5 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -596,11 +596,14 @@ void sec_audit_logger(modsec_rec *msr) { } if (rc <= 0) { - msr_log(msr, 1, "Audit log: %s", my_error_msg); break; } } + if (rc < 0) { + msr_log(msr, 1, "Audit log: %s", my_error_msg); + } + modsecurity_request_body_retrieve_end(msr); } } diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 76603764..8f96505d 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -17,6 +17,8 @@ * Prepare to accept the request body (part 2). */ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **error_msg) { + *error_msg = NULL; + if(msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { /* Prepare to store request body in memory. */ @@ -55,6 +57,7 @@ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char ** * Prepare to accept the request body (part 1). */ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { + *error_msg = NULL; msr->msc_reqbody_length = 0; /* Create a separate memory pool that will be used @@ -104,7 +107,11 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, const char *data, apr_size_t length, char **error_msg) { - apr_size_t i = write(msr->msc_reqbody_fd, data, length); + apr_size_t i; + + *error_msg = NULL; + + 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); return -1; @@ -119,6 +126,8 @@ static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, const char *data, apr_size_t length, char **error_msg) { + *error_msg = NULL; + /* Would storing this chunk mean going over the limit? */ if ((msr->msc_reqbody_spilltodisk) && (msr->msc_reqbody_length + length > (apr_size_t)msr->txcfg->reqbody_inmemory_limit)) @@ -242,6 +251,8 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, apr_status_t modsecurity_request_body_store(modsec_rec *msr, const char *data, apr_size_t length, char **error_msg) { + *error_msg = NULL; + /* If we have a processor for this request body send * data to it first (but only if it did not report an * error on previous invocations). @@ -300,6 +311,8 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha int i, sofar; int invalid_count = 0; + *error_msg = NULL; + /* Allocate a buffer large enough to hold the request body. */ if (msr->msc_reqbody_length + 1 == 0) { @@ -367,6 +380,7 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha * Stops receiving the request body. */ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { + *error_msg = NULL; /* Close open file descriptors, if any. */ if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { @@ -420,6 +434,8 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { * Prepares to forward the request body. */ apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) { + *error_msg = NULL; + if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { msr->msc_reqbody_chunk_position = 0; msr->msc_reqbody_chunk_offset = 0; @@ -485,6 +501,8 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, { msc_data_chunk **chunks; + *error_msg = NULL; + if (chunk == NULL) { *error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk."); return -1; @@ -581,6 +599,8 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, * */ apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) { + *error_msg = NULL; + /* Release memory we used to store request body data. */ if (msr->msc_reqbody_chunks != NULL) { msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts; diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index 4ba611dd..fe26837d 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -312,6 +312,8 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { apr_table_set(r->headers_out, "Location", new_uri); + ap_remove_output_filter(f); + return send_error_bucket(msr, f, REDIRECT_STATUS); } } else { /* Token found. */ diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 5515172b..663593fd 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -161,15 +161,16 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c { */ if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ - char *pattern_escaped = log_escape(msr->mp, regex->pattern); + /* We no longer escape the pattern here as it is done when logging */ + char *pattern = apr_pstrdup(msr->mp, regex->pattern); /* This message will be logged. */ - if (strlen(pattern_escaped) > 252) { + if (strlen(pattern) > 252) { *error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.", - pattern_escaped, var->name); + pattern, var->name); } else { *error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.", - pattern_escaped, var->name); + pattern, var->name); } return 1; From 27ba3027b7586aae116fe77d3a5e00f3b491488d Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 28 Sep 2007 21:06:57 +0000 Subject: [PATCH 131/372] Move init of msr->msc_rule_mptmp before msr storage. --- apache2/mod_security2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 53faa31d..d834b438 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -337,6 +337,8 @@ static modsec_rec *create_tx_context(request_rec *r) { msr->request_headers = apr_table_copy(msr->mp, r->headers_in); msr->hostname = ap_get_server_name(r); + msr->msc_rule_mptmp = NULL; + /* Invoke the engine to continue with initialisation */ if (modsecurity_tx_init(msr) < 0) { msr_log(msr, 1, "Failed to initialising transaction (txid %s).", msr->txid); @@ -349,8 +351,6 @@ static modsec_rec *create_tx_context(request_rec *r) { msr_log(msr, 4, "Transaction context created (dcfg %x).", msr->dcfg1); } - msr->msc_rule_mptmp = NULL; - return msr; } From b6615749730a3e98056cf00e99696d06be2a48b9 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 28 Sep 2007 22:16:37 +0000 Subject: [PATCH 132/372] Document the 'tag' action. See #276. --- doc/modsecurity2-apache-reference.xml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 854391a7..5d35a3c6 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-dev3 / (September 21, 2007) + Version 2.5.0-dev3 / (September 28, 2007) 2004-2007 @@ -4458,6 +4458,27 @@ SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \ rule. +
+ <literal>tag</literal> + + Description: Assigns custom text + to a rule or chain. + + Action Group: Metadata + + 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'" + + 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. +
+
<literal>xmlns</literal> From 2d526f1434d23d60073696f8e3c3838f120866f6 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 1 Oct 2007 17:23:38 +0000 Subject: [PATCH 133/372] Fix typo in a comment. --- apache2/re.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index 467091b5..2d9eb4df 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1113,7 +1113,7 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { /** * Assembles a new rule using the strings that contain a list - * of targets (variables), argumments, and actions. + * of targets (variables), arguments, and actions. */ msre_rule *msre_rule_create(msre_ruleset *ruleset, const char *fn, int line, const char *targets, From 9d4965b29e30f2b47ed60a214420af5d1fadc3f0 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 1 Oct 2007 17:24:10 +0000 Subject: [PATCH 134/372] Fix macro expansion in setvar. See #126. --- CHANGES | 3 ++ apache2/re_actions.c | 90 +++++++++++++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/CHANGES b/CHANGES index 83c5194c..04f5a602 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ ?? ??? 2007 - 2.5.0-dev3 ------------------------ + * Fixed expansion of macros when using relative changes with setvar. In + addition, added support for expanding macros in the variable name. + * Situations where ModSecurity will intercept, generate an error or log a level 1-3 message to the debug log are now marked as 'relevant' and may generate an audit log entry. diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 3530779c..fb9220ba 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -890,6 +890,7 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm char *s = NULL; apr_table_t *target_col = NULL; int is_negated = 0; + msc_string *var = NULL; /* Extract the name and the value. */ /* IMP1 We have a function for this now, parse_name_eq_value? */ @@ -905,6 +906,22 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm while ((*var_value != '\0')&&(isspace(*var_value))) var_value++; } + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Setting 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); + /* Handle the exclamation mark. */ if (var_name[0] == '!') { var_name = var_name + 1; @@ -943,25 +960,45 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm /* ENH Refuse to remove certain variables, e.g. TIMEOUT, internal variables, etc... */ apr_table_unset(target_col, var_name); - msr_log(msr, 9, "Unset variable \"%s.%s\".", log_escape(mptmp, col_name), - log_escape(mptmp, var_name)); - } else { + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Unset variable \"%s.%s\".", col_name, var_name); + } + } + else { /* Set or change variable. */ if ((var_value[0] == '+')||(var_value[0] == '-')) { /* Relative change. */ - msc_string *var = NULL; + msc_string *rec = NULL; + msc_string *val = apr_palloc(msr->mp, sizeof(msc_string)); int value = 0; + if (val == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand value macros"); + return -1; + } + /* Retrieve variable or generate (if it does not exist). */ - var = (msc_string *)apr_table_get(target_col, var_name); - if (var == NULL) { - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = apr_pstrdup(msr->mp, var_name); - var->name_len = strlen(var->name); + rec = (msc_string *)apr_table_get(target_col, var_name); + if (rec == NULL) { + rec = var; /* use the already allocated space for var */ + rec->name = apr_pstrdup(msr->mp, var_name); + rec->name_len = strlen(rec->name); value = 0; - } else { - value = atoi(var->value); + } + else { + value = atoi(rec->value); + } + + /* Expand values in value */ + val->value = var_value; + val->value_len = strlen(val->value); + expand_macros(msr, val, rule, mptmp); + var_value = val->value; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Relative change: %s=%d%s", var_name, value, var_value); } /* Change value. */ @@ -969,20 +1006,19 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm if (value < 0) value = 0; /* Counters never go below zero. */ /* Put the variable back. */ - var->value = apr_psprintf(msr->mp, "%d", value); - var->value_len = strlen(var->value); - apr_table_setn(target_col, var->name, (void *)var); + rec->value = apr_psprintf(msr->mp, "%d", value); + rec->value_len = strlen(rec->value); + apr_table_setn(target_col, rec->name, (void *)rec); - msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", - log_escape(mptmp, col_name), - log_escape_ex(mptmp, var->name, var->name_len), - log_escape_ex(mptmp, var->value, var->value_len)); - } else { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", + col_name, rec->name, + log_escape_ex(mptmp, rec->value, rec->value_len)); + } + } + else { /* Absolute change. */ - msc_string *var = NULL; - - var = apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = apr_pstrdup(msr->mp, var_name); var->name_len = strlen(var->name); var->value = apr_pstrdup(msr->mp, var_value); @@ -990,10 +1026,12 @@ static apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptm expand_macros(msr, var, rule, mptmp); apr_table_setn(target_col, var->name, (void *)var); - msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", - log_escape(mptmp, col_name), - log_escape_ex(mptmp, var->name, var->name_len), - log_escape_ex(mptmp, var->value, var->value_len)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Set variable \"%s.%s\" to \"%s\".", + log_escape(mptmp, col_name), + log_escape_ex(mptmp, var->name, var->name_len), + log_escape_ex(mptmp, var->value, var->value_len)); + } } } From dc71842cee7908c34e64a99087433333e4a85a9c Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 1 Oct 2007 18:45:06 +0000 Subject: [PATCH 135/372] Revert to apr_psprintf (vs apr_pstrcat) to get around what appears to be an apr bug with FC4. --- apache2/re.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index 2d9eb4df..aa4d7eb9 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1295,7 +1295,9 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, var->name, var->param); } else if ((var->name != NULL) && var->is_counting && (*var->name != '&')) { - full_varname = apr_pstrcat(mptmp, "&", var->name); + // TODO: This fails (sigsegv) on FC4, but apr_psprintf works + //full_varname = apr_pstrcat(mptmp, "&", var->name); + full_varname = apr_psprintf(mptmp, "&%s", var->name); } else { full_varname = var->name; From e0e031d163e221bca80eb1b377b908f5e5be9891 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 1 Oct 2007 19:05:34 +0000 Subject: [PATCH 136/372] Oops, too fast to blame apr :) This bug was a forgotten NULL in the apr_pstrcat function. Apparently newer APRs can handle this. --- apache2/re.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index aa4d7eb9..e043a002 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1295,9 +1295,7 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, var->name, var->param); } else if ((var->name != NULL) && var->is_counting && (*var->name != '&')) { - // TODO: This fails (sigsegv) on FC4, but apr_psprintf works - //full_varname = apr_pstrcat(mptmp, "&", var->name); - full_varname = apr_psprintf(mptmp, "&%s", var->name); + full_varname = apr_pstrcat(mptmp, "&", var->name, NULL); } else { full_varname = var->name; From da1399f0b8c718213a6cd1d50f709b94cccd59a8 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 1 Oct 2007 22:35:52 +0000 Subject: [PATCH 137/372] Added TX:LAST_MATCHED_VAR_NAME. See #123. --- CHANGES | 5 ++++- apache2/re.c | 17 ++++++++++++++++ apache2/re_operators.c | 4 ++-- doc/modsecurity2-apache-reference.xml | 28 +++++++++++++++++++++++++-- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 04f5a602..3be55b24 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,10 @@ -?? ??? 2007 - 2.5.0-dev3 +01 Oct 2007 - 2.5.0-dev3 ------------------------ + * Used new TX:LAST_MATCHED_VAR_NAME to store the last matched variable name + so that it can be more easily used by rules. + * Fixed expansion of macros when using relative changes with setvar. In addition, added support for expanding macros in the variable name. diff --git a/apache2/re.c b/apache2/re.c index e043a002..150ae9b0 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1335,6 +1335,8 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, else { /* Match. */ + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (rc == 0) { /* Operator did not match so we need to provide a message. */ my_error_msg = apr_psprintf(msr->mp, "Match of \"%s %s\" against \"%s\" required.", @@ -1344,6 +1346,21 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, msr->matched_var = apr_pstrdup(msr->mp, var->name); + if (s == NULL) { + msr_log(msr, 3, "Internal error: Failed to allocate space for TX.last_matched_var_name."); + } + else { + s->name = "last_matched_var_name"; + s->value = apr_pstrdup(msr->mp, var->name); + s->value_len = strlen(var->name); + if ((s->name == NULL)||(s->value == NULL)) return -1; + apr_table_setn(msr->tx_vars, s->name, (void *)s); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Added matched variable name to TX.%s: %s", s->name, var->name); + } + } + + /* Keep track of the highest severity matched so far */ if ((acting_actionset->severity > 0) && (acting_actionset->severity < msr->highest_severity)) { diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 663593fd..2cdf6f4a 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -142,7 +142,7 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c if ((s->name == NULL)||(s->value == NULL)) return -1; apr_table_setn(msr->tx_vars, s->name, (void *)s); if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Adding regex subexpression to TXVARS (%d): %s", i, + msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, log_escape_nq_ex(msr->mp, s->value, s->value_len)); } } @@ -346,7 +346,7 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c apr_table_setn(msr->tx_vars, s->name, (void *)s); if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Adding phrase match to TXVARS (0): %s", + msr_log(msr, 9, "Added phrase match to TX.0: %s", log_escape_nq_ex(msr->mp, s->value, s->value_len)); } diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 5d35a3c6..321ef877 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-dev3 / (September 28, 2007) + Version 2.5.0-dev3 / (October 1, 2007) 2004-2007 @@ -2977,6 +2977,30 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} this 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:1-TX:9 - The captured + subexpression value when using the @rx operator with capturing parens and the + capture action. + + + + TX:LAST_MATCHED_VAR_NAME - + The full name of the variable that was matched against. + + + SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" SecRule TX:SCORE "@gt 20" deny,log @@ -5028,4 +5052,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}
- \ No newline at end of file + From a6cf7957be4f134c3b03f180377c3eb76e421c64 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 1 Oct 2007 22:38:19 +0000 Subject: [PATCH 138/372] Update ModSecurity chroot documentation. --- doc/modsecurity2-apache-reference.xml | 43 ++++++++++++++++++++------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 321ef877..5f75673f 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -855,17 +855,38 @@ SecAuditLogStorageDir logs/audit 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. For more complex setups you - should consider building a jail the old-fashioned way. The internal - chroot feature should be treated as somewhat experimental. 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. You are advised to think about your option - and make your own decision. 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. + only, or running scripts using modules. 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). + + + + You cannot send email from PHP because it uses sendmail and + sendmail is outside the jail. + + + + In some cases Apache graceful 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.
From 83a788607186ea31906872c3527cab61fd7db182 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 2 Oct 2007 18:50:35 +0000 Subject: [PATCH 139/372] Now use memcmp() vs strncmp() in string comparison operators since we already short-circuit when the match will not fit in the target. Added @containsWord. See #182. --- CHANGES | 3 ++ apache2/re_operators.c | 109 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 3be55b24..4efae210 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ 01 Oct 2007 - 2.5.0-dev3 ------------------------ + * Added a @containsWord operator that will match a given string anywhere in + the target value, but only on word boundaries. + * Used new TX:LAST_MATCHED_VAR_NAME to store the last matched variable name so that it can be more easily used by rules. diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 2cdf6f4a..a91cf90d 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -411,7 +411,7 @@ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *va i_max = match_length - target_length; for (i = 0; i <= i_max; i++) { if (match[i] == target[0]) { - if (strncmp(target, (match + i), target_length) == 0) { + if (memcmp((target + 1), (match + i + 1), (target_length - 1)) == 0) { /* match. */ *error_msg = apr_psprintf(msr->mp, "String match %s=\"%s\" within \"%s\".", var->name, @@ -476,7 +476,7 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * i_max = target_length - match_length; for (i = 0; i <= i_max; i++) { if (target[i] == match[0]) { - if (strncmp(match, (target + i), match_length) == 0) { + if (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -490,6 +490,98 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * return 0; } +/* containsWord */ + +static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length = 0; + unsigned int i, i_max; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* These are impossible to match */ + if ((match_length == 0) || (match_length > target_length)) { + /* No match. */ + return 0; + } + + /* scan for first character, then compare from there until we + * have a match or there is no room left in the target + */ + i_max = target_length - match_length; + for (i = 0; i <= i_max; i++) { + + /* Previous char must have been a start or non-word */ + if ((i > 0) && (isalnum(target[i-1])||(target[i-1] == '_'))) + continue; + + + /* First character matched */ + if (target[i] == match[0]) { + + /* Maybe a match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + + /* only one character */ + if (match_length == 1) return 1; + + /* remaining matched */ + if (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0) { + + + /* check boundaries */ + if (i == i_max) { + /* exact/end word match */ + return 1; + } + else if (!(isalnum(target[i+match_length])||(target[i+match_length] == '_'))) { + /* start/mid word match */ + return 1; + } + + /* No word match */ + *error_msg = NULL; + return 0; + } + } + } + + /* No match. */ + *error_msg = NULL; + return 0; +} + /* streq */ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { @@ -533,7 +625,7 @@ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var return 0; } - if (strncmp(match, target, target_length) == 0) { + if (memcmp(match, target, target_length) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -588,7 +680,7 @@ static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var return 0; } - if (strncmp(match, target, match_length) == 0) { + if (memcmp(match, target, match_length) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -643,7 +735,7 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * return 0; } - if (strncmp(match, (target + (target_length - match_length)), match_length) == 0) { + if (memcmp(match, (target + (target_length - match_length)), match_length) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -1501,6 +1593,13 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_contains_execute ); + /* containsWord */ + msre_engine_op_register(engine, + "containsWord", + NULL, /* ENH init function to flag var substitution */ + msre_op_containsWord_execute + ); + /* is */ msre_engine_op_register(engine, "streq", From b784e6cb732387e08fff33deee9c1db7426c9595 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 3 Oct 2007 00:23:46 +0000 Subject: [PATCH 140/372] Change from TX:LAST_MATCHED_VAR_NAME to MATCHED_VAR. See #123. --- CHANGES | 2 +- apache2/re.c | 17 -- apache2/re_variables.c | 20 ++ doc/modsecurity2-apache-reference.xml | 397 +++++++++++++++++++------- 4 files changed, 315 insertions(+), 121 deletions(-) diff --git a/CHANGES b/CHANGES index 4efae210..862ada9c 100644 --- a/CHANGES +++ b/CHANGES @@ -5,7 +5,7 @@ * Added a @containsWord operator that will match a given string anywhere in the target value, but only on word boundaries. - * Used new TX:LAST_MATCHED_VAR_NAME to store the last matched variable name + * New MATCHED_VAR variable to store the last matched variable name so that it can be more easily used by rules. * Fixed expansion of macros when using relative changes with setvar. In diff --git a/apache2/re.c b/apache2/re.c index 150ae9b0..e043a002 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1335,8 +1335,6 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, else { /* Match. */ - msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - if (rc == 0) { /* Operator did not match so we need to provide a message. */ my_error_msg = apr_psprintf(msr->mp, "Match of \"%s %s\" against \"%s\" required.", @@ -1346,21 +1344,6 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, msr->matched_var = apr_pstrdup(msr->mp, var->name); - if (s == NULL) { - msr_log(msr, 3, "Internal error: Failed to allocate space for TX.last_matched_var_name."); - } - else { - s->name = "last_matched_var_name"; - s->value = apr_pstrdup(msr->mp, var->name); - s->value_len = strlen(var->name); - if ((s->name == NULL)||(s->value == NULL)) return -1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Added matched variable name to TX.%s: %s", s->name, var->name); - } - } - - /* Keep track of the highest severity matched so far */ if ((acting_actionset->severity > 0) && (acting_actionset->severity < msr->highest_severity)) { diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 319ce69b..2b41c57b 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -838,6 +838,15 @@ static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, return count; } +/* MATCHED_VAR */ + +static int var_matched_var_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + return var_simple_generate(var, vartab, mptmp, + apr_pstrdup(mptmp, msr->matched_var)); +} + /* SESSION */ static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, @@ -2292,6 +2301,17 @@ void msre_engine_register_default_variables(msre_engine *engine) { PHASE_REQUEST_HEADERS ); + /* MATCHED_VAR */ + msre_engine_variable_register(engine, + "MATCHED_VAR", + VAR_SIMPLE, + 0, 0, + NULL, + var_matched_var_generate, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + /* MODSEC_BUILD */ msre_engine_variable_register(engine, "MODSEC_BUILD", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 5f75673f..4302ee2a 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -2025,7 +2025,9 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1The following variables are supported in ModSecurity 2.x:
- <literal moreinfo="none">ARGS</literal> + + <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 @@ -2070,7 +2072,9 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1
- <literal moreinfo="none">ARGS_COMBINED_SIZE</literal> + + <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 @@ -2084,7 +2088,9 @@ SecRule ARGS_COMBINED_SIZE "@gt 25"
- <literal moreinfo="none">ARGS_NAMES</literal> + + <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, @@ -2098,14 +2104,18 @@ SecRule ARGS_NAMES "!^(p|a)$"
- <literal moreinfo="none">ARGS_GET</literal> + + <literal moreinfo="none">ARGS_GET</literal> + ARGS_GET is similar to ARGS, but only contains arguments from the query string.
- <literal moreinfo="none">ARGS_GET_NAMES</literal> + + <literal moreinfo="none">ARGS_GET_NAMES</literal> + ARGS_GET_NAMES is similar to ARGS_NAMES, but only contains argument names from the @@ -2113,7 +2123,9 @@ SecRule ARGS_NAMES "!^(p|a)$"
- <literal moreinfo="none">ARGS_POST</literal> + + <literal moreinfo="none">ARGS_POST</literal> + ARGS_POST is similar to ARGS, but only contains arguments from the POST @@ -2121,7 +2133,9 @@ SecRule ARGS_NAMES "!^(p|a)$"
- <literal moreinfo="none">ARGS_POST_NAMES</literal> + + <literal moreinfo="none">ARGS_POST_NAMES</literal> + ARGS_POST_NAMES is similar to ARGS_NAMES, but only contains argument names from the @@ -2129,14 +2143,18 @@ SecRule ARGS_NAMES "!^(p|a)$"
- <literal moreinfo="none">AUTH_TYPE</literal> + + <literal moreinfo="none">AUTH_TYPE</literal> + 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 + + 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 @@ -2145,7 +2163,9 @@ SecRule ARGS_NAMES "!^(p|a)$"
- <literal moreinfo="none">ENV</literal> + + <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 @@ -2157,7 +2177,9 @@ SecRule ENV:tag "suspicious"
- <literal moreinfo="none">FILES</literal> + + <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 @@ -2167,7 +2189,9 @@ SecRule ENV:tag "suspicious"
- <literal moreinfo="none">FILES_COMBINED_SIZE</literal> + + <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: @@ -2176,7 +2200,9 @@ SecRule ENV:tag "suspicious"
- <literal moreinfo="none">FILES_NAMES</literal> + + <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 @@ -2186,7 +2212,9 @@ SecRule ENV:tag "suspicious"
- <literal moreinfo="none">FILES_SIZES</literal> + + <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 @@ -2196,7 +2224,9 @@ SecRule ENV:tag "suspicious"
- <literal moreinfo="none">FILES_TMPNAMES</literal> + + <literal moreinfo="none">FILES_TMPNAMES</literal> + Collection. Contains a collection of temporary files' names on the disk. Useful when used together with ENV:tag "suspicious"
- <literal moreinfo="none">GEO</literal> + + <literal moreinfo="none">GEO</literal> + GEO is a collection populated by the @geoLookups operator. It can be used to match @@ -2281,7 +2313,9 @@ SecRule GEO:COUNTRY_CODE "!@streq UK"
- <literal moreinfo="none">HIGHEST_SEVERITY</literal> + + <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 @@ -2298,7 +2332,22 @@ SecRule GEO:COUNTRY_CODE "!@streq UK"
- <literal moreinfo="none">MODSEC_BUILD</literal> + + <literal moreinfo="none">MATCHED_VAR</literal> + + + This variable holds the full name of the variable that was matched + against. + + SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR} +... +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 @@ -2309,7 +2358,9 @@ SecRule ARGS "@pm some key words" deny,status:500
- <literal>MULTIPART_STRICT_ERROR</literal> + + <literal>MULTIPART_STRICT_ERROR</literal> + MULTIPART_STRICT_ERROR will be set to 1 when any of the following variables is also set to @@ -2356,7 +2407,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal>MULTIPART_UNMATCHED_BOUNDARY</literal> + + <literal>MULTIPART_UNMATCHED_BOUNDARY</literal> + Set to 1 when, during the parsing phase of a multipart/request-body, ModSecurity encounters what @@ -2374,7 +2427,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal moreinfo="none">PATH_INFO</literal> + + <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 @@ -2384,7 +2439,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal moreinfo="none">QUERY_STRING</literal> + + <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. @@ -2394,7 +2451,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal moreinfo="none">REMOTE_ADDR</literal> + + <literal moreinfo="none">REMOTE_ADDR</literal> + This variable holds the IP address of the remote client. Example: @@ -2403,7 +2462,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal moreinfo="none">REMOTE_HOST</literal> + + <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 @@ -2415,7 +2476,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal moreinfo="none">REMOTE_PORT</literal> + + <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 @@ -2427,7 +2490,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- <literal moreinfo="none">REMOTE_USER</literal> + + <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 @@ -2435,14 +2500,18 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" SecRule REMOTE_USER "admin" - Note + + Note + This data will not be available in a proxy-mode deployment as the authentication is not local.
- <literal moreinfo="none">REQBODY_PROCESSOR</literal> + + <literal moreinfo="none">REQBODY_PROCESSOR</literal> + Built-in processors are URLENCODED, @@ -2454,8 +2523,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal - moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> + + <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 @@ -2480,8 +2550,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal - moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> + + <literal moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> + Empty, or contains the error message from the processor. Example: @@ -2490,7 +2561,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_BASENAME</literal> + + <literal moreinfo="none">REQUEST_BASENAME</literal> + This variable holds just the filename part of REQUEST_FILENAME (e.g. index.php). Warning: not @@ -2500,7 +2573,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_BODY</literal> + + <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 @@ -2509,14 +2584,18 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" - Note + + Note + This variable is only available if the content type is application/x-www-form-urlencoded.
- <literal moreinfo="none">REQUEST_COOKIES</literal> + + <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 @@ -2527,7 +2606,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_COOKIES_NAMES</literal> + + <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 @@ -2537,7 +2618,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_FILENAME</literal> + + <literal moreinfo="none">REQUEST_FILENAME</literal> + This variable holds the relative REQUEST_URI minus the QUERY_STRING part (e.g. /index.php). Example: @@ -2546,7 +2629,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_HEADERS</literal> + + <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 indivudual headers (by using @@ -2564,7 +2649,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> + + <literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> + This variable is a collection of the names of all of the Request Headers. Example: @@ -2574,7 +2661,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_LINE</literal> + + <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 @@ -2584,7 +2673,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" - Note + + Note + Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2592,7 +2683,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_METHOD</literal> + + <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 @@ -2600,7 +2693,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_METHOD "^((?:connect|trace))$" - Note + + Note + Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2608,14 +2703,18 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_PROTOCOL</literal> + + <literal moreinfo="none">REQUEST_PROTOCOL</literal> + This variable holds the Request Protocol Version information. Example: SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" - Note + + Note + Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2623,7 +2722,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_URI</literal> + + <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 @@ -2635,7 +2736,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">REQUEST_URI_RAW</literal> + + <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. @@ -2646,7 +2749,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">RESPONSE_BODY</literal> + + <literal moreinfo="none">RESPONSE_BODY</literal> + This variable holds the data for the response payload. Example: @@ -2655,7 +2760,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal>RESPONSE_CONTENT_LENGTH</literal> + + <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 @@ -2671,14 +2778,18 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal>RESPONSE_CONTENT_TYPE</literal> + + <literal>RESPONSE_CONTENT_TYPE</literal> + Response content type. Only available starting with phase 3.
- <literal moreinfo="none">RESPONSE_HEADERS</literal> + + <literal moreinfo="none">RESPONSE_HEADERS</literal> + This variable is similar to the REQUEST_HEADERS variable and can be used in the same manner. Example: @@ -2686,7 +2797,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule RESPONSE_HEADERS:X-Cache "MISS" - Note + + Note + This variable may not have access to some headers when running in embedded-mode. Headers such as Server, Date, Connection and Content-Type @@ -2696,21 +2809,27 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> + + <literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> + This variable is a collection of the response header names. Example: SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" - Note + + Note + Same limitations as RESPONSE_HEADERS with regards to access to some headers in embedded-mode.
- <literal moreinfo="none">RESPONSE_PROTOCOL</literal> + + <literal moreinfo="none">RESPONSE_PROTOCOL</literal> + This variable holds the HTTP Response Protocol information. Example: @@ -2719,14 +2838,18 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">RESPONSE_STATUS</literal> + + <literal moreinfo="none">RESPONSE_STATUS</literal> + This variable holds the HTTP Response Status Code generated by Apache. Example: SecRule RESPONSE_STATUS "^[45]" - Note + + 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 @@ -2735,7 +2858,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">RULE</literal> + + <literal moreinfo="none">RULE</literal> + This variable provides access to the id, rev, @@ -2750,59 +2875,77 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">SCRIPT_BASENAME</literal> + + <literal moreinfo="none">SCRIPT_BASENAME</literal> + This variable holds just the local filename part of SCRIPT_FILENAME. Example: SecRule SCRIPT_BASENAME "^login\.php$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SCRIPT_FILENAME</literal> + + <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: SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SCRIPT_GID</literal> + + <literal moreinfo="none">SCRIPT_GID</literal> + This variable holds the groupid (numerical value) of the group owner of the script. Example: SecRule SCRIPT_GID "!^46$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SCRIPT_GROUPNAME</literal> + + <literal moreinfo="none">SCRIPT_GROUPNAME</literal> + This variable holds the group name of the group owner of the script. Example: SecRule SCRIPT_GROUPNAME "!^apache$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SCRIPT_MODE</literal> + + <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 @@ -2810,13 +2953,17 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule SCRIPT_MODE "^(2|3|6|7)$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SCRIPT_UID</literal> + + <literal moreinfo="none">SCRIPT_UID</literal> + This variable holds the userid (numerical value) of the owner of the script. Example: the example rule below will trigger if the UID is @@ -2824,26 +2971,34 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule SCRIPT_UID "!^46$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SCRIPT_USERNAME</literal> + + <literal moreinfo="none">SCRIPT_USERNAME</literal> + This variable holds the username of the owner of the script. Example: SecRule SCRIPT_USERNAME "!^apache$" - Note + + Note + This variable is not available in proxy mode.
- <literal moreinfo="none">SERVER_ADDR</literal> + + <literal moreinfo="none">SERVER_ADDR</literal> + This variable contains the IP address of the server. Example: @@ -2852,21 +3007,27 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">SERVER_NAME</literal> + + <literal moreinfo="none">SERVER_NAME</literal> + This variable contains the server's hostname or IP address. Example: SecRule SERVER_NAME "hostname\.com$" - Note + + Note + This data is taken from the Host header submitted in the client request.
- <literal moreinfo="none">SERVER_PORT</literal> + + <literal moreinfo="none">SERVER_PORT</literal> + This variable contains the local port that the web server is listening on. Example: @@ -2875,7 +3036,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal moreinfo="none">SESSION</literal> + + <literal moreinfo="none">SESSION</literal> + This variable is a collection, available only after setsid is executed. Example: the following @@ -2893,7 +3056,9 @@ SecRule SESSION:BLOCKED "@eq 1" "log,deny,statu
- <literal moreinfo="none">SESSIONID</literal> + + <literal moreinfo="none">SESSIONID</literal> + This variable is the value set with setsid. Example: @@ -2904,7 +3069,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME</literal> + + <literal moreinfo="none">TIME</literal> + This variable holds a formatted string representing the time (hour:minute:second). Example: @@ -2913,7 +3080,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_DAY</literal> + + <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 @@ -2923,7 +3092,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_EPOCH</literal> + + <literal moreinfo="none">TIME_EPOCH</literal> + This variable holds the time in seconds since 1970. Example: @@ -2932,7 +3103,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_HOUR</literal> + + <literal moreinfo="none">TIME_HOUR</literal> + This variable holds the current hour (0-23). Example: this rule would trigger during "off hours". @@ -2941,7 +3114,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_MIN</literal> + + <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. @@ -2950,7 +3125,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_MON</literal> + + <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 @@ -2960,7 +3137,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_SEC</literal> + + <literal moreinfo="none">TIME_SEC</literal> + This variable holds the current second count (0-59). Example: @@ -2969,7 +3148,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_WDAY</literal> + + <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). @@ -2978,7 +3159,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TIME_YEAR</literal> + + <literal moreinfo="none">TIME_YEAR</literal> + This variable holds the current four-digit year data. Example: @@ -2987,7 +3170,9 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- <literal moreinfo="none">TX</literal> + + <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 @@ -3015,11 +3200,6 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} moreinfo="none">@rx operator with capturing parens and the capture action. - - - TX:LAST_MATCHED_VAR_NAME - - The full name of the variable that was matched against. - SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass, TX:SCORE "@gt 20" deny,log
- <literal moreinfo="none">USERID</literal> + + <literal moreinfo="none">USERID</literal> + This variable is the value set with setuid. Example: @@ -3038,7 +3220,9 @@ SecRule USERID "Admin"
- <literal moreinfo="none">WEBAPPID</literal> + + <literal moreinfo="none">WEBAPPID</literal> + This variable is the value set with SecWebAppId. Example: @@ -3049,7 +3233,9 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
- <literal moreinfo="none">WEBSERVER_ERROR_LOG</literal> + + <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: @@ -3058,7 +3244,9 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
- <literal moreinfo="none">XML</literal> + + <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 @@ -3129,14 +3317,17 @@ SecRule XML:/xq:employees/employee/name/text() - XPath - Standard + + XPath Standard + - XPath - Tutorial + + XPath + Tutorial +
From 793b57670105682a894e1d55fdcfdfcbeec03447 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 15 Oct 2007 16:50:36 +0000 Subject: [PATCH 141/372] Added support for MATCHED_VAR and MATCHED_VAR_NAME. See #123. --- apache2/modsecurity.c | 3 + apache2/modsecurity.h | 2 +- apache2/re.c | 6 +- apache2/re_actions.c | 29 +- apache2/re_variables.c | 30 +- doc/modsecurity2-apache-reference.xml | 400 ++++++++------------------ 6 files changed, 166 insertions(+), 304 deletions(-) diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index bd927be9..3e718cd1 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -304,6 +304,9 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { msr->tcache = apr_hash_make(msr->mp); if (msr->tcache == 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 */ return 1; diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index a7470676..17662b12 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -324,7 +324,7 @@ struct modsec_rec { apr_time_t time_checkpoint_2; apr_time_t time_checkpoint_3; - const char *matched_var; + msc_string *matched_var; int highest_severity; /* upload */ diff --git a/apache2/re.c b/apache2/re.c index e043a002..6b60e406 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1342,7 +1342,11 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, log_escape(msr->mp, full_varname)); } - msr->matched_var = apr_pstrdup(msr->mp, var->name); + /* Save the last matched var data */ + msr->matched_var->name = apr_pstrdup(msr->mp, var->name); + msr->matched_var->name_len = strlen(msr->matched_var->name); + msr->matched_var->value = apr_pmemdup(msr->mp, var->value, var->value_len); + msr->matched_var->value_len = var->value_len; /* Keep track of the highest severity matched so far */ if ((acting_actionset->severity > 0) && (acting_actionset->severity < msr->highest_severity)) diff --git a/apache2/re_actions.c b/apache2/re_actions.c index fb9220ba..8bd034c9 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -769,39 +769,40 @@ static apr_status_t msre_action_sanitiseMatched_execute(modsec_rec *msr, apr_poo const apr_array_header_t *tarr; const apr_table_entry_t *telts; int i, type = 0; + msc_string *mvar = msr->matched_var; - if (msr->matched_var == NULL) return 0; + if (mvar->name_len == 0) return 0; /* IMP1 We need to extract the variable name properly here, * taking into account it may have been escaped. */ - if (strncmp(msr->matched_var, "ARGS:", 5) == 0) { - sargname = apr_pstrdup(msr->mp, msr->matched_var + 5); + if ((mvar->name_len > 5) && (strncmp(mvar->name, "ARGS:", 5) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 5); type = SANITISE_ARG; } else - if (strncmp(msr->matched_var, "ARGS_NAMES:", 11) == 0) { - sargname = apr_pstrdup(msr->mp, msr->matched_var + 11); + if ((mvar->name_len > 11) && (strncmp(mvar->name, "ARGS_NAMES:", 11) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 11); type = SANITISE_ARG; } else - if (strncmp(msr->matched_var, "REQUEST_HEADERS:", 16) == 0) { - sargname = apr_pstrdup(msr->mp, msr->matched_var + 16); + if ((mvar->name_len > 16) && (strncmp(mvar->name, "REQUEST_HEADERS:", 16) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 16); type = SANITISE_REQUEST_HEADER; } else - if (strncmp(msr->matched_var, "REQUEST_HEADERS_NAMES:", 22) == 0) { - sargname = apr_pstrdup(msr->mp, msr->matched_var + 22); + if ((mvar->name_len > 22) && (strncmp(mvar->name, "REQUEST_HEADERS_NAMES:", 22) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 22); type = SANITISE_REQUEST_HEADER; } else - if (strncmp(msr->matched_var, "RESPONSE_HEADERS:", 17) == 0) { - sargname = apr_pstrdup(msr->mp, msr->matched_var + 17); + if ((mvar->name_len > 17) && (strncmp(mvar->name, "RESPONSE_HEADERS:", 17) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 17); type = SANITISE_RESPONSE_HEADER; } else - if (strncmp(msr->matched_var, "RESPONSE_HEADERS_NAMES:", 23) == 0) { - sargname = apr_pstrdup(msr->mp, msr->matched_var + 23); + if ((mvar->name_len > 23) && (strncmp(mvar->name, "RESPONSE_HEADERS_NAMES:", 23) == 0)) { + sargname = apr_pstrdup(msr->mp, mvar->name + 23); type = SANITISE_RESPONSE_HEADER; } else { msr_log(msr, 3, "sanitiseMatched: Don't know how to handle variable: %s", - msr->matched_var); + mvar->name); return 0; } diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 2b41c57b..d75afafa 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -843,8 +843,23 @@ static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_matched_var_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - return var_simple_generate(var, vartab, mptmp, - apr_pstrdup(mptmp, msr->matched_var)); + 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); +} + +/* MATCHED_VAR_NAME */ + +static int var_matched_var_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + 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); } /* SESSION */ @@ -2312,6 +2327,17 @@ void msre_engine_register_default_variables(msre_engine *engine) { PHASE_REQUEST_HEADERS ); + /* MATCHED_VAR_NAME */ + msre_engine_variable_register(engine, + "MATCHED_VAR_NAME", + VAR_SIMPLE, + 0, 0, + NULL, + var_matched_var_name_generate, + VAR_DONT_CACHE, + PHASE_REQUEST_HEADERS + ); + /* MODSEC_BUILD */ msre_engine_variable_register(engine, "MODSEC_BUILD", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 4302ee2a..40c19d5d 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -2025,9 +2025,7 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1The following variables are supported in ModSecurity 2.x:
- - <literal moreinfo="none">ARGS</literal> - + <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 @@ -2072,9 +2070,7 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1
- - <literal moreinfo="none">ARGS_COMBINED_SIZE</literal> - + <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 @@ -2088,9 +2084,7 @@ SecRule ARGS_COMBINED_SIZE "@gt 25"
- - <literal moreinfo="none">ARGS_NAMES</literal> - + <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, @@ -2104,18 +2098,14 @@ SecRule ARGS_NAMES "!^(p|a)$"
- - <literal moreinfo="none">ARGS_GET</literal> - + <literal moreinfo="none">ARGS_GET</literal> ARGS_GET is similar to ARGS, but only contains arguments from the query string.
- - <literal moreinfo="none">ARGS_GET_NAMES</literal> - + <literal moreinfo="none">ARGS_GET_NAMES</literal> ARGS_GET_NAMES is similar to ARGS_NAMES, but only contains argument names from the @@ -2123,9 +2113,7 @@ SecRule ARGS_NAMES "!^(p|a)$"
- - <literal moreinfo="none">ARGS_POST</literal> - + <literal moreinfo="none">ARGS_POST</literal> ARGS_POST is similar to ARGS, but only contains arguments from the POST @@ -2133,9 +2121,7 @@ SecRule ARGS_NAMES "!^(p|a)$"
- - <literal moreinfo="none">ARGS_POST_NAMES</literal> - + <literal moreinfo="none">ARGS_POST_NAMES</literal> ARGS_POST_NAMES is similar to ARGS_NAMES, but only contains argument names from the @@ -2143,18 +2129,14 @@ SecRule ARGS_NAMES "!^(p|a)$"
- - <literal moreinfo="none">AUTH_TYPE</literal> - + <literal moreinfo="none">AUTH_TYPE</literal> 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 - + 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 @@ -2163,9 +2145,7 @@ SecRule ARGS_NAMES "!^(p|a)$"
- - <literal moreinfo="none">ENV</literal> - + <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 @@ -2177,9 +2157,7 @@ SecRule ENV:tag "suspicious"
- - <literal moreinfo="none">FILES</literal> - + <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 @@ -2189,9 +2167,7 @@ SecRule ENV:tag "suspicious"
- - <literal moreinfo="none">FILES_COMBINED_SIZE</literal> - + <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: @@ -2200,9 +2176,7 @@ SecRule ENV:tag "suspicious"
- - <literal moreinfo="none">FILES_NAMES</literal> - + <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 @@ -2212,9 +2186,7 @@ SecRule ENV:tag "suspicious"
- - <literal moreinfo="none">FILES_SIZES</literal> - + <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 @@ -2224,9 +2196,7 @@ SecRule ENV:tag "suspicious"
- - <literal moreinfo="none">FILES_TMPNAMES</literal> - + <literal moreinfo="none">FILES_TMPNAMES</literal> Collection. Contains a collection of temporary files' names on the disk. Useful when used together with ENV:tag "suspicious"
- - <literal moreinfo="none">GEO</literal> - + <literal moreinfo="none">GEO</literal> GEO is a collection populated by the @geoLookups operator. It can be used to match @@ -2313,9 +2281,7 @@ SecRule GEO:COUNTRY_CODE "!@streq UK"
- - <literal moreinfo="none">HIGHEST_SEVERITY</literal> - + <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 @@ -2332,22 +2298,31 @@ SecRule GEO:COUNTRY_CODE "!@streq UK"
- - <literal moreinfo="none">MATCHED_VAR</literal> - + <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. + + 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. - SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR} + SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR_NAME} ... SecRule TX:MYMATCH "@eq ARGS:param" deny
- - <literal moreinfo="none">MODSEC_BUILD</literal> - + <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 @@ -2358,9 +2333,7 @@ SecRule ARGS "@pm some key words" deny,status:500
- - <literal>MULTIPART_STRICT_ERROR</literal> - + <literal>MULTIPART_STRICT_ERROR</literal> MULTIPART_STRICT_ERROR will be set to 1 when any of the following variables is also set to @@ -2407,9 +2380,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal>MULTIPART_UNMATCHED_BOUNDARY</literal> - + <literal>MULTIPART_UNMATCHED_BOUNDARY</literal> Set to 1 when, during the parsing phase of a multipart/request-body, ModSecurity encounters what @@ -2427,9 +2398,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal moreinfo="none">PATH_INFO</literal> - + <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 @@ -2439,9 +2408,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal moreinfo="none">QUERY_STRING</literal> - + <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. @@ -2451,9 +2418,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal moreinfo="none">REMOTE_ADDR</literal> - + <literal moreinfo="none">REMOTE_ADDR</literal> This variable holds the IP address of the remote client. Example: @@ -2462,9 +2427,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal moreinfo="none">REMOTE_HOST</literal> - + <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 @@ -2476,9 +2439,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal moreinfo="none">REMOTE_PORT</literal> - + <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 @@ -2490,9 +2451,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'"
- - <literal moreinfo="none">REMOTE_USER</literal> - + <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 @@ -2500,18 +2459,14 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" SecRule REMOTE_USER "admin" - - Note - + Note This data will not be available in a proxy-mode deployment as the authentication is not local.
- - <literal moreinfo="none">REQBODY_PROCESSOR</literal> - + <literal moreinfo="none">REQBODY_PROCESSOR</literal> Built-in processors are URLENCODED, @@ -2523,9 +2478,8 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQBODY_PROCESSOR_ERROR</literal> - + <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 @@ -2550,9 +2504,8 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> - + <literal + moreinfo="none">REQBODY_PROCESSOR_ERROR_MSG</literal> Empty, or contains the error message from the processor. Example: @@ -2561,9 +2514,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_BASENAME</literal> - + <literal moreinfo="none">REQUEST_BASENAME</literal> This variable holds just the filename part of REQUEST_FILENAME (e.g. index.php). Warning: not @@ -2573,9 +2524,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_BODY</literal> - + <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 @@ -2584,18 +2533,14 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" - - Note - + Note This variable is only available if the content type is application/x-www-form-urlencoded.
- - <literal moreinfo="none">REQUEST_COOKIES</literal> - + <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 @@ -2606,9 +2551,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_COOKIES_NAMES</literal> - + <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 @@ -2618,9 +2561,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_FILENAME</literal> - + <literal moreinfo="none">REQUEST_FILENAME</literal> This variable holds the relative REQUEST_URI minus the QUERY_STRING part (e.g. /index.php). Example: @@ -2629,9 +2570,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_HEADERS</literal> - + <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 indivudual headers (by using @@ -2649,9 +2588,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> - + <literal moreinfo="none">REQUEST_HEADERS_NAMES</literal> This variable is a collection of the names of all of the Request Headers. Example: @@ -2661,9 +2598,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_LINE</literal> - + <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 @@ -2673,9 +2608,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" - - Note - + Note Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2683,9 +2616,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_METHOD</literal> - + <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 @@ -2693,9 +2624,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule REQUEST_METHOD "^((?:connect|trace))$" - - Note - + Note Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2703,18 +2632,14 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_PROTOCOL</literal> - + <literal moreinfo="none">REQUEST_PROTOCOL</literal> This variable holds the Request Protocol Version information. Example: SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" - - Note - + Note Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2722,9 +2647,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_URI</literal> - + <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 @@ -2736,9 +2659,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">REQUEST_URI_RAW</literal> - + <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. @@ -2749,9 +2670,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">RESPONSE_BODY</literal> - + <literal moreinfo="none">RESPONSE_BODY</literal> This variable holds the data for the response payload. Example: @@ -2760,9 +2679,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal>RESPONSE_CONTENT_LENGTH</literal> - + <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 @@ -2778,18 +2695,14 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal>RESPONSE_CONTENT_TYPE</literal> - + <literal>RESPONSE_CONTENT_TYPE</literal> Response content type. Only available starting with phase 3.
- - <literal moreinfo="none">RESPONSE_HEADERS</literal> - + <literal moreinfo="none">RESPONSE_HEADERS</literal> This variable is similar to the REQUEST_HEADERS variable and can be used in the same manner. Example: @@ -2797,9 +2710,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule RESPONSE_HEADERS:X-Cache "MISS" - - Note - + Note This variable may not have access to some headers when running in embedded-mode. Headers such as Server, Date, Connection and Content-Type @@ -2809,27 +2720,21 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> - + <literal moreinfo="none">RESPONSE_HEADERS_NAMES</literal> This variable is a collection of the response header names. Example: SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" - - Note - + Note Same limitations as RESPONSE_HEADERS with regards to access to some headers in embedded-mode.
- - <literal moreinfo="none">RESPONSE_PROTOCOL</literal> - + <literal moreinfo="none">RESPONSE_PROTOCOL</literal> This variable holds the HTTP Response Protocol information. Example: @@ -2838,18 +2743,14 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">RESPONSE_STATUS</literal> - + <literal moreinfo="none">RESPONSE_STATUS</literal> This variable holds the HTTP Response Status Code generated by Apache. Example: SecRule RESPONSE_STATUS "^[45]" - - Note - + 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 @@ -2858,9 +2759,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">RULE</literal> - + <literal moreinfo="none">RULE</literal> This variable provides access to the id, rev, @@ -2875,77 +2774,59 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">SCRIPT_BASENAME</literal> - + <literal moreinfo="none">SCRIPT_BASENAME</literal> This variable holds just the local filename part of SCRIPT_FILENAME. Example: SecRule SCRIPT_BASENAME "^login\.php$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SCRIPT_FILENAME</literal> - + <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: SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SCRIPT_GID</literal> - + <literal moreinfo="none">SCRIPT_GID</literal> This variable holds the groupid (numerical value) of the group owner of the script. Example: SecRule SCRIPT_GID "!^46$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SCRIPT_GROUPNAME</literal> - + <literal moreinfo="none">SCRIPT_GROUPNAME</literal> This variable holds the group name of the group owner of the script. Example: SecRule SCRIPT_GROUPNAME "!^apache$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SCRIPT_MODE</literal> - + <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 @@ -2953,17 +2834,13 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule SCRIPT_MODE "^(2|3|6|7)$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SCRIPT_UID</literal> - + <literal moreinfo="none">SCRIPT_UID</literal> This variable holds the userid (numerical value) of the owner of the script. Example: the example rule below will trigger if the UID is @@ -2971,34 +2848,26 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" SecRule SCRIPT_UID "!^46$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SCRIPT_USERNAME</literal> - + <literal moreinfo="none">SCRIPT_USERNAME</literal> This variable holds the username of the owner of the script. Example: SecRule SCRIPT_USERNAME "!^apache$" - - Note - + Note This variable is not available in proxy mode.
- - <literal moreinfo="none">SERVER_ADDR</literal> - + <literal moreinfo="none">SERVER_ADDR</literal> This variable contains the IP address of the server. Example: @@ -3007,27 +2876,21 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">SERVER_NAME</literal> - + <literal moreinfo="none">SERVER_NAME</literal> This variable contains the server's hostname or IP address. Example: SecRule SERVER_NAME "hostname\.com$" - - Note - + Note This data is taken from the Host header submitted in the client request.
- - <literal moreinfo="none">SERVER_PORT</literal> - + <literal moreinfo="none">SERVER_PORT</literal> This variable contains the local port that the web server is listening on. Example: @@ -3036,9 +2899,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- - <literal moreinfo="none">SESSION</literal> - + <literal moreinfo="none">SESSION</literal> This variable is a collection, available only after setsid is executed. Example: the following @@ -3056,9 +2917,7 @@ SecRule SESSION:BLOCKED "@eq 1" "log,deny,statu
- - <literal moreinfo="none">SESSIONID</literal> - + <literal moreinfo="none">SESSIONID</literal> This variable is the value set with setsid. Example: @@ -3069,9 +2928,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME</literal> - + <literal moreinfo="none">TIME</literal> This variable holds a formatted string representing the time (hour:minute:second). Example: @@ -3080,9 +2937,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_DAY</literal> - + <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 @@ -3092,9 +2947,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_EPOCH</literal> - + <literal moreinfo="none">TIME_EPOCH</literal> This variable holds the time in seconds since 1970. Example: @@ -3103,9 +2956,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_HOUR</literal> - + <literal moreinfo="none">TIME_HOUR</literal> This variable holds the current hour (0-23). Example: this rule would trigger during "off hours". @@ -3114,9 +2965,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_MIN</literal> - + <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. @@ -3125,9 +2974,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_MON</literal> - + <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 @@ -3137,9 +2984,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_SEC</literal> - + <literal moreinfo="none">TIME_SEC</literal> This variable holds the current second count (0-59). Example: @@ -3148,9 +2993,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_WDAY</literal> - + <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). @@ -3159,9 +3002,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TIME_YEAR</literal> - + <literal moreinfo="none">TIME_YEAR</literal> This variable holds the current four-digit year data. Example: @@ -3170,9 +3011,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
- - <literal moreinfo="none">TX</literal> - + <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 @@ -3208,9 +3047,7 @@ SecRule TX:SCORE "@gt 20" deny,log
- - <literal moreinfo="none">USERID</literal> - + <literal moreinfo="none">USERID</literal> This variable is the value set with setuid. Example: @@ -3220,9 +3057,7 @@ SecRule USERID "Admin"
- - <literal moreinfo="none">WEBAPPID</literal> - + <literal moreinfo="none">WEBAPPID</literal> This variable is the value set with SecWebAppId. Example: @@ -3233,9 +3068,7 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
- - <literal moreinfo="none">WEBSERVER_ERROR_LOG</literal> - + <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: @@ -3244,9 +3077,7 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
- - <literal moreinfo="none">XML</literal> - + <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 @@ -3317,17 +3148,14 @@ SecRule XML:/xq:employees/employee/name/text() - - XPath Standard - + XPath + Standard - - XPath - Tutorial - + XPath + Tutorial
@@ -5264,4 +5092,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}
- + \ No newline at end of file From d5f3b9ce529cd0459fdbfdf56d3436af6b79e3e2 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 15 Oct 2007 17:27:51 +0000 Subject: [PATCH 142/372] Fix multipart parser blocking FP with Safari ( (#317). --- apache2/msc_multipart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index dccc4325..8a1f431d 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -622,8 +622,11 @@ static int multipart_count_boundary_params(apr_pool_t *mp, const char *header_va s = duplicate; while((s = strstr(s, "boundary")) != NULL) { - count++; s += 8; + + if (strchr(s, "=") != NULL) { + count++; + } } return count; From b0d514478f3c2cee2889bd9d76b0b6055f2c7d66 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 15 Oct 2007 18:05:12 +0000 Subject: [PATCH 143/372] Fix blocking multipart FP, which affected Safari. --- apache2/msc_multipart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 8a1f431d..64f5264e 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -624,7 +624,7 @@ static int multipart_count_boundary_params(apr_pool_t *mp, const char *header_va while((s = strstr(s, "boundary")) != NULL) { s += 8; - if (strchr(s, "=") != NULL) { + if (strchr(s, '=') != NULL) { count++; } } From f66e8c5b38b24fabf493aea63d4f1aeb030187a4 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 15 Oct 2007 18:27:42 +0000 Subject: [PATCH 144/372] Document MULTIPART_CRLF_LF_LINES. --- doc/modsecurity2-apache-reference.xml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 40c19d5d..c4d6edd8 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-dev3 / (October 1, 2007) + Version 2.5.0-dev?/ (October 15, 2007) 2004-2007 @@ -2332,6 +2332,24 @@ SecRule TX:MYMATCH "@eq ARGS:param" deny
+
+ <literal>MULTIPART_CRLF_LF_LINES</literal> + + This flag variable will be set to 1 whenever a + multipart 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 multipart flag + variable individually, avoding 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> From 9efa02f4231881171f3620e2561e7f693e1a5922 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 16 Oct 2007 00:14:42 +0000 Subject: [PATCH 145/372] Change ctl parameters to be case insensitive. Initial implementation of ctl:removeRuleById. See #259. --- apache2/modsecurity.c | 3 +++ apache2/modsecurity.h | 3 +++ apache2/re.c | 19 +++++++++++++++++++ apache2/re_actions.c | 44 +++++++++++++++++++++++++------------------ 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 3e718cd1..6448ca0b 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -309,6 +309,9 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { 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; + return 1; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 17662b12..0c867b20 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -346,6 +346,9 @@ struct modsec_rec { /* data cache */ apr_hash_t *tcache; + + /* removed rules */ + apr_array_header_t *removed_rules; }; struct directory_config { diff --git a/apache2/re.c b/apache2/re.c index 6b60e406..eeb98936 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1384,6 +1384,25 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { int invocations = 0; int multi_match = 0; + /* Check if this rule was excluded at runtime */ + if ((rule->actionset->id !=NULL) && (! apr_is_empty_array(msr->removed_rules))) { + const char *range; + + for(i = 0; i < msr->removed_rules->nelts; i++) { + range = ((const char**)msr->removed_rules->elts)[i]; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Checking rule id=\"%s\" against exclusion: %s", rule->actionset->id, range); + } + + if (rule_id_in_range(atoi(rule->actionset->id), range)) { + msr_log(msr, 5, "Rule id=\"%s\" excluded.", rule->actionset->id); + return RULE_NO_MATCH; + } + } + + } + /* Choose the correct metadata/disruptive action actionset. */ acting_actionset = rule->actionset; if (rule->chain_starter != NULL) { diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 8bd034c9..eb8a7e4c 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -476,40 +476,44 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) } /* Validate value. */ - if (strcmp(name, "ruleEngine") == 0) { + if (strcasecmp(name, "ruleEngine") == 0) { if (strcasecmp(value, "on") == 0) return NULL; if (strcasecmp(value, "off") == 0) return NULL; if (strcasecmp(value, "detectiononly") == 0) return NULL; return apr_psprintf(engine->mp, "Invalid setting for ctl name ruleEngine: %s", value); } else - if (strcmp(name, "requestBodyAccess") == 0) { + if (strcasecmp(name, "removeRuleById") == 0) { + /* ENH nothing yet */ + return NULL; + } else + if (strcasecmp(name, "requestBodyAccess") == 0) { if (parse_boolean(value) == -1) { return apr_psprintf(engine->mp, "Invalid setting for ctl name " " requestBodyAccess: %s", value); } return NULL; } else - if (strcmp(name, "requestBodyProcessor") == 0) { + if (strcasecmp(name, "requestBodyProcessor") == 0) { /* ENH We will accept anything for now but it'd be nice * to add a check here that the processor name is a valid one. */ return NULL; } else - if (strcmp(name, "responseBodyAccess") == 0) { + if (strcasecmp(name, "responseBodyAccess") == 0) { if (parse_boolean(value) == -1) { return apr_psprintf(engine->mp, "Invalid setting for ctl name " " responseBodyAccess: %s", value); } return NULL; } else - if (strcmp(name, "auditEngine") == 0) { + if (strcasecmp(name, "auditEngine") == 0) { if (strcasecmp(value, "on") == 0) return NULL; if (strcasecmp(value, "off") == 0) return NULL; if (strcasecmp(value, "relevantonly") == 0) return NULL; return apr_psprintf(engine->mp, "Invalid setting for ctl name " " auditEngine: %s", value); } else - if (strcmp(name, "auditLogParts") == 0) { + if (strcasecmp(name, "auditLogParts") == 0) { if ((value[0] == '+')||(value[0] == '-')) { if (is_valid_parts_specification(value + 1) != 1) { return apr_psprintf(engine->mp, "Invalid setting for ctl name " @@ -523,12 +527,12 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) } return NULL; } else - if (strcmp(name, "debugLogLevel") == 0) { + if (strcasecmp(name, "debugLogLevel") == 0) { if ((atoi(value) >= 0)&&(atoi(value) <= 9)) return NULL; return apr_psprintf(engine->mp, "Invalid setting for ctl name " "debugLogLevel: %s", value); } else - if (strcmp(name, "requestBodyLimit") == 0) { + if (strcasecmp(name, "requestBodyLimit") == 0) { long int limit = strtol(value, NULL, 10); if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { @@ -543,7 +547,7 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) return NULL; } else - if (strcmp(name, "responseBodyLimit") == 0) { + if (strcasecmp(name, "responseBodyLimit") == 0) { long int limit = strtol(value, NULL, 10); if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { @@ -581,7 +585,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (value == NULL) return -1; /* Validate value. */ - if (strcmp(name, "ruleEngine") == 0) { + if (strcasecmp(name, "ruleEngine") == 0) { if (strcasecmp(value, "on") == 0) { msr->txcfg->is_enabled = MODSEC_ENABLED; msr->usercfg->is_enabled = MODSEC_ENABLED; @@ -599,7 +603,11 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcmp(name, "requestBodyAccess") == 0) { + if (strcasecmp(name, "removeRuleById") == 0) { + *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value); + return 1; + } else + if (strcasecmp(name, "requestBodyAccess") == 0) { int pv = parse_boolean(value); if (pv == -1) return -1; @@ -609,13 +617,13 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcmp(name, "requestBodyProcessor") == 0) { + if (strcasecmp(name, "requestBodyProcessor") == 0) { msr->msc_reqbody_processor = value; msr_log(msr, 4, "Ctl: Set requestBodyProcessor to %s.", value); return 1; } else - if (strcmp(name, "responseBodyAccess") == 0) { + if (strcasecmp(name, "responseBodyAccess") == 0) { int pv = parse_boolean(value); if (pv == -1) return -1; @@ -625,7 +633,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcmp(name, "auditEngine") == 0) { + if (strcasecmp(name, "auditEngine") == 0) { if (strcasecmp(value, "on") == 0) { msr->txcfg->auditlog_flag = AUDITLOG_ON; msr->usercfg->auditlog_flag = AUDITLOG_ON; @@ -645,7 +653,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcmp(name, "auditLogParts") == 0) { + if (strcasecmp(name, "auditLogParts") == 0) { char *new_value = value; if (value[0] == '+') { @@ -681,14 +689,14 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcmp(name, "debugLogLevel") == 0) { + if (strcasecmp(name, "debugLogLevel") == 0) { msr->txcfg->debuglog_level = atoi(value); msr->usercfg->debuglog_level = atoi(value); msr_log(msr, 4, "Ctl: Set debugLogLevel to %d.", msr->txcfg->debuglog_level); return 1; } else - if (strcmp(name, "requestBodyLimit") == 0) { + if (strcasecmp(name, "requestBodyLimit") == 0) { long int limit = strtol(value, NULL, 10); /* ENH Accept only in correct phase warn otherwise. */ @@ -697,7 +705,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcmp(name, "responseBodyLimit") == 0) { + if (strcasecmp(name, "responseBodyLimit") == 0) { long int limit = strtol(value, NULL, 10); /* ENH Accept only in correct phase warn otherwise. */ From 974298a76c67bcf664e2f9790c2ec47c7fb911a3 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 17 Oct 2007 19:11:47 +0000 Subject: [PATCH 146/372] Added ctl:ruleRemoveById action. See #259. --- CHANGES | 4 ++- apache2/re.c | 47 ++++++++++++++++----------- apache2/re_actions.c | 4 +-- doc/modsecurity2-apache-reference.xml | 6 +++- 4 files changed, 38 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index 862ada9c..a321b3f8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,9 @@ -01 Oct 2007 - 2.5.0-dev3 +17 Oct 2007 - 2.5.0-dev3 ------------------------ + * Added ctl:ruleRemoveById action to allow rule removal on a match. + * Added a @containsWord operator that will match a given string anywhere in the target value, but only on word boundaries. diff --git a/apache2/re.c b/apache2/re.c index eeb98936..a350c49d 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -703,6 +703,34 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) continue; } + /* Check if this rule was removed at runtime */ + if ((rule->actionset->id !=NULL) && (! apr_is_empty_array(msr->removed_rules))) { + int j; + int do_process = 1; + const char *range; + + for(j = 0; j < msr->removed_rules->nelts; j++) { + range = ((const char**)msr->removed_rules->elts)[j]; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Checking removal of rule id=\"%s\" against: %s", rule->actionset->id, range); + } + + if (rule_id_in_range(atoi(rule->actionset->id), range)) { + do_process = 0; + break; + } + } + + /* Go to the next rule if this one has been removed. */ + if (do_process == 0) { + if (msr->txcfg->debuglog_level >= 5) { + msr_log(msr, 5, "Not processing rule id=\"%s\": removed by ctl action", rule->actionset->id); + } + continue; + } + } + if (msr->txcfg->debuglog_level >= 4) { apr_pool_t *p = msr->mp; const char *fn = NULL; @@ -1384,25 +1412,6 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { int invocations = 0; int multi_match = 0; - /* Check if this rule was excluded at runtime */ - if ((rule->actionset->id !=NULL) && (! apr_is_empty_array(msr->removed_rules))) { - const char *range; - - for(i = 0; i < msr->removed_rules->nelts; i++) { - range = ((const char**)msr->removed_rules->elts)[i]; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Checking rule id=\"%s\" against exclusion: %s", rule->actionset->id, range); - } - - if (rule_id_in_range(atoi(rule->actionset->id), range)) { - msr_log(msr, 5, "Rule id=\"%s\" excluded.", rule->actionset->id); - return RULE_NO_MATCH; - } - } - - } - /* Choose the correct metadata/disruptive action actionset. */ acting_actionset = rule->actionset; if (rule->chain_starter != NULL) { diff --git a/apache2/re_actions.c b/apache2/re_actions.c index eb8a7e4c..f3db7aae 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -482,7 +482,7 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) if (strcasecmp(value, "detectiononly") == 0) return NULL; return apr_psprintf(engine->mp, "Invalid setting for ctl name ruleEngine: %s", value); } else - if (strcasecmp(name, "removeRuleById") == 0) { + if (strcasecmp(name, "ruleRemoveById") == 0) { /* ENH nothing yet */ return NULL; } else @@ -603,7 +603,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, return 1; } else - if (strcasecmp(name, "removeRuleById") == 0) { + if (strcasecmp(name, "ruleRemoveById") == 0) { *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value); return 1; } else diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index c4d6edd8..b31370f1 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3624,6 +3624,10 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requ debugLogLevel + + ruleRemoveById + + requestBodyAccess @@ -5110,4 +5114,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}
- \ No newline at end of file + From 9d49adf028f675839d2f84b849ccff4f4c1308a1 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 17 Oct 2007 19:59:28 +0000 Subject: [PATCH 147/372] Basic implementation of skipAfter (still need to implement placeholders so it works with removed rules). See #258. --- CHANGES | 4 ++++ apache2/re.c | 39 +++++++++++++++++++++++++++++++++++++++ apache2/re.h | 1 + apache2/re_actions.c | 27 +++++++++++++++++++++++++++ 4 files changed, 71 insertions(+) diff --git a/CHANGES b/CHANGES index a321b3f8..93a140f7 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ 17 Oct 2007 - 2.5.0-dev3 ------------------------ + * Added skipAfter: action to allow skipping all rules until a rule + with a specified ID is reached. Rule execution then continues after + the specified rule. + * Added ctl:ruleRemoveById action to allow rule removal on a match. * Added a @containsWord operator that will match a given string anywhere in diff --git a/apache2/re.c b/apache2/re.c index a350c49d..4fe98bb6 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -425,6 +425,7 @@ msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, /* Flow */ actionset->is_chained = NOT_SET; actionset->skip_count = NOT_SET; + actionset->skip_after = NOT_SET_P; /* Disruptive */ actionset->intercept_action = NOT_SET; @@ -501,6 +502,7 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent /* Flow */ merged->is_chained = child->is_chained; if (child->skip_count != NOT_SET) merged->skip_count = child->skip_count; + if (child->skip_after != NOT_SET_P) merged->skip_after = child->skip_after; /* Disruptive */ if (child->intercept_action != NOT_SET) { @@ -558,6 +560,7 @@ static void msre_actionset_set_defaults(msre_actionset *actionset) { /* Flow */ if (actionset->is_chained == NOT_SET) actionset->is_chained = 0; if (actionset->skip_count == NOT_SET) actionset->skip_count = 0; + if (actionset->skip_after == NOT_SET_P) actionset->skip_after = NULL; /* Disruptive */ if (actionset->intercept_action == NOT_SET) actionset->intercept_action = ACTION_NONE; @@ -629,6 +632,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) apr_array_header_t *arr = NULL; msre_rule **rules; apr_status_t rc; + const char *skip_after = NULL; int i, mode, skip; /* First determine which set of rules we need to use. */ @@ -667,6 +671,30 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) apr_time_t time1 = 0; #endif + // TODO: Still need to skip over placeholders + + /* SKIP_RULES is used to skip all rules until we hit a placeholder + * with the specified rule ID and then resume execution after that. + */ + if (mode == SKIP_RULES) { + /* Go to the next rule if we have not yet hit the skip_after ID */ + // TODO: must be a placeholder as well + if ((rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Skipping rule id=\"%s\" while looking for id=\"%s\"", (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); + } + continue; + } + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Continuing execution after rule id=\"%s\"", skip_after); + } + skip_after = NULL; + mode = NEXT_RULE; + + /* Go to the rule *after* this one to continue execution. */ + continue; + } + /* NEXT_CHAIN is used when one of the rules in a chain * fails to match and then we need to skip the remaining * rules in that chain in order to get to the next @@ -796,6 +824,17 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) return 1; } + if (rule->actionset->skip_after != NULL) { + skip_after = rule->actionset->skip_after; + mode = SKIP_RULES; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Skipping after rule id=\"%s\" -> mode SKIP_RULES.", skip_after); + } + + continue; + } + /* We had a match but the transaction was not * intercepted. In that case we proceed with the * next rule... diff --git a/apache2/re.h b/apache2/re.h index ca05dbbe..66ba0bcf 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -235,6 +235,7 @@ struct msre_actionset { /* Flow */ int is_chained; int skip_count; + const char *skip_after; /* Disruptive */ int intercept_action; diff --git a/apache2/re_actions.c b/apache2/re_actions.c index f3db7aae..97481827 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -420,6 +420,21 @@ static apr_status_t msre_action_skip_init(msre_engine *engine, msre_actionset *a return 1; } +/* skipAfter */ + +static char *msre_action_skipAfter_validate(msre_engine *engine, msre_action *action) { + /* ENH Add validation. */ + return NULL; +} + +static apr_status_t msre_action_skipAfter_init(msre_engine *engine, msre_actionset *actionset, + msre_action *action) +{ + // TODO: Need to keep track of skipAfter IDs so we can insert placeholders after we get to the real rule with that ID. + actionset->skip_after = action->param; + return 1; +} + /* allow */ static apr_status_t msre_action_allow_init(msre_engine *engine, msre_actionset *actionset, @@ -1660,6 +1675,18 @@ void msre_engine_register_default_actions(msre_engine *engine) { NULL ); + /* skipAfter */ + msre_engine_action_register(engine, + "skipAfter", + ACTION_DISRUPTIVE, + 1, 1, + NO_PLUS_MINUS, + ACTION_CARDINALITY_ONE, + msre_action_skipAfter_validate, + msre_action_skipAfter_init, + NULL + ); + /* allow */ msre_engine_action_register(engine, "allow", From 8e990900678654f145716046bb2cef8546d20fec Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 17 Oct 2007 22:41:37 +0000 Subject: [PATCH 148/372] Add the input filter if we have read the body (even if a sub-request). See #335. --- CHANGES | 2 +- apache2/apache2_io.c | 5 +++++ apache2/mod_security2.c | 27 ++++++++++++++------------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 93a140f7..8e261a3b 100644 --- a/CHANGES +++ b/CHANGES @@ -21,7 +21,7 @@ a level 1-3 message to the debug log are now marked as 'relevant' and may generate an audit log entry. - * Do not process subrequests in phase 2-4. + * Do not process subrequests in phase 2-4, but do hand off the request data. * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds as documented instead of decrementing by a rate. diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index ea73f71f..a0c0632c 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -41,6 +41,11 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, msr->r = f->r; + if (msr->phase > PHASE_REQUEST_BODY) { + msr_log(msr, 1, "Internal error: Still in input filter in phase %d", msr->phase); + return APR_EGENERAL; + } + if ((msr->if_status == IF_STATUS_COMPLETE)||(msr->if_status == IF_STATUS_NONE)) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %x, r %x).", f, f->r); diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index d834b438..b95bc1a5 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -888,6 +888,19 @@ static int hook_log_transaction(request_rec *r) { static void hook_insert_filter(request_rec *r) { modsec_rec *msr = NULL; + /* Find the transaction context first. */ + msr = retrieve_tx_context(r); + if (msr == NULL) return; + + /* 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 %x).", (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); + } + + ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); + } + /* This function needs to run only once per transaction * (i.e. subrequests and redirects are excluded). */ @@ -895,10 +908,6 @@ static void hook_insert_filter(request_rec *r) { return; } - /* Find the transaction context first. */ - msr = retrieve_tx_context(r); - if (msr == NULL) return; - /* We always add the PDF XSS protection filter. */ if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %x).", r); @@ -907,6 +916,7 @@ static void hook_insert_filter(request_rec *r) { ap_add_output_filter("PDFP_OUT", msr, r, r->connection); /* Only proceed to add the second filter if the engine is enabled. */ + // TODO: Do we need this anymore? if (msr->txcfg->is_enabled == 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping."); @@ -915,15 +925,6 @@ static void hook_insert_filter(request_rec *r) { return; } - /* 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 (r %x).", r); - } - - ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); - } - /* We always add the output filter because that's where we need to * initiate our 3rd and 4th processing phases from. The filter is * smart enough not to buffer the data if it is not supposed to. From 2b346dd0864a0047fcb43134c88599c4116d7571 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 17 Oct 2007 23:07:00 +0000 Subject: [PATCH 149/372] Updated input filter insertion code for sub-requests. --- apache2/apache2_io.c | 4 ++-- apache2/mod_security2.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index a0c0632c..411105e1 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -41,8 +41,8 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, msr->r = f->r; - if (msr->phase > PHASE_REQUEST_BODY) { - msr_log(msr, 1, "Internal error: Still in input filter in phase %d", msr->phase); + if (msr->phase < PHASE_REQUEST_BODY) { + msr_log(msr, 1, "Internal error: REQUEST_BODY phase incomplete for input filter in phase %d", msr->phase); return APR_EGENERAL; } diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index b95bc1a5..04dcebb2 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -901,7 +901,7 @@ static void hook_insert_filter(request_rec *r) { ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); } - /* This function needs to run only once per transaction + /* The output filters only need to be added only once per transaction * (i.e. subrequests and redirects are excluded). */ if ((r->main != NULL)||(r->prev != NULL)) { @@ -916,7 +916,6 @@ static void hook_insert_filter(request_rec *r) { ap_add_output_filter("PDFP_OUT", msr, r, r->connection); /* Only proceed to add the second filter if the engine is enabled. */ - // TODO: Do we need this anymore? if (msr->txcfg->is_enabled == 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping."); From faec5b8e9dea6aac4ac56af92dab9121a2ba383b Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 23 Oct 2007 22:16:39 +0000 Subject: [PATCH 150/372] Fix a possible loss of data warning when compiling 64bit reported by Marc Stern. --- apache2/persist_dbm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 7cacfe67..218eaff8 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -161,7 +161,7 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, } /* NOTE: No rate if there has been no time elapsed */ - td = (apr_time_sec(apr_time_now()) - create_time); + td = (int)(apr_time_sec(apr_time_now()) - create_time); if (td == 0) { var->value = apr_psprintf(msr->mp, "%d", 0); } From e45ea12fc8b63db7dd181800fd4db1df91e07edc Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 2 Nov 2007 22:31:47 +0000 Subject: [PATCH 151/372] Fix warnings on Solaris and/or 64bit builds. --- CHANGES | 5 +++-- apache2/Makefile | 13 ++++++++++++- apache2/apache2_config.c | 26 ++++++++++++++++++++------ apache2/mod_security2.c | 8 ++++---- apache2/modsecurity.c | 3 +-- apache2/modsecurity.h | 4 ++-- apache2/re.h | 39 +++++++++++++++++++-------------------- apache2/re_actions.c | 4 ++-- apache2/re_operators.c | 8 ++++---- apache2/re_tfns.c | 2 +- apache2/re_variables.c | 2 +- 11 files changed, 69 insertions(+), 45 deletions(-) diff --git a/CHANGES b/CHANGES index 8e261a3b..46b66c40 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,8 @@ - -17 Oct 2007 - 2.5.0-dev3 +02 Nov 2007 - 2.5.0-dev3 ------------------------ + * Fix warnings on Solaris and/or 64bit builds. + * Added skipAfter: action to allow skipping all rules until a rule with a specified ID is reached. Rule execution then continues after the specified rule. diff --git a/apache2/Makefile b/apache2/Makefile index 7cd4c577..a725f3b7 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -17,7 +17,17 @@ builddir = . # Debian - /usr/share/apache2 (apache2-prefork-dev or apache2-threaded-dev # needed, depending on your installation type) # -top_dir = /apps/apache22 +# NOTE: On Solaris the top_builddir is /var/apache2. This version of Apache +# httpd uses the buildin PCRE, but you must still install the PCRE headers +# to compile ModSecurity as they are not installed with Apache httpd. Because +# of this, it is recommended to install your own version of Apache httpd. +# +# Additionally, if you do not have PCRE installed, you can use the +# headers included with Apache httpd. To do this, add the PCRE +# include directory to the INCLUDES variable below +# Something like: -I /path/to/httpd-x.y/srclib/pcre +# +top_dir = /usr/local/apache2 top_srcdir = ${top_dir} top_builddir = ${top_dir} @@ -28,6 +38,7 @@ APXS = apxs APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 +#INCLUDES = -I /usr/include/libxml2 -I /path/to/httpd-x.y/srclib/pcre DEFS = -DWITH_LIBXML2 #DEFS = -DWITH_LIBXML2 -DPERFORMANCE_MEASUREMENT #DEFS = -DWITH_LIBXML2 -DNO_MODSEC_API diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 88401165..eec00ce7 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1304,11 +1304,18 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const charval = apr_table_get(vartable, "minlen"); if (charval != NULL) { 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); } - if (intval >= (apr_size_t)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %u", (apr_size_t)NOT_SET); + + /* 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: %u", (unsigned long)NOT_SET); } dcfg->cache_trans_min = (apr_size_t)intval; } @@ -1317,14 +1324,21 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const charval = apr_table_get(vartable, "maxlen"); if (charval != NULL) { 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); } - if (intval >= (apr_size_t)NOT_SET) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %u", (apr_size_t)NOT_SET); + + /* 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: %u", (unsigned long)NOT_SET); } - if ((intval != 0) && (intval < dcfg->cache_trans_min)) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %u < %u", (apr_size_t)intval, dcfg->cache_trans_min); + if ((intval != 0) && ((apr_size_t)intval < dcfg->cache_trans_min)) { + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %u < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min); } dcfg->cache_trans_max = (apr_size_t)intval; diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 04dcebb2..a936a95d 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -449,7 +449,7 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t if (first_time == 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot checkpoint #2 (pid=%d ppid=%d)", getpid(), getppid()); + "ModSecurity: chroot checkpoint #2 (pid=%d ppid=%d)", (int)getpid(), (int)getppid()); if (chdir(chroot_dir) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, @@ -478,7 +478,7 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t "ModSecurity: chroot successful, path=%s", chroot_dir); } else { ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot checkpoint #1 (pid=%d ppid=%d)", getpid(), getppid()); + "ModSecurity: chroot checkpoint #1 (pid=%d ppid=%d)", (int)getpid(), (int)getppid()); } } #endif @@ -986,7 +986,7 @@ static void hook_insert_error_filter(request_rec *r) { */ static void modsec_register_tfn(const char *name, void *fn) { if (modsecurity != NULL) { - msre_engine_tfn_register(modsecurity->msre, name, fn); + msre_engine_tfn_register(modsecurity->msre, name, (fn_tfn_execute_t)fn); } } @@ -996,7 +996,7 @@ static void modsec_register_tfn(const char *name, void *fn) { */ static void modsec_register_operator(const char *name, void *fn_init, void *fn_exec) { if (modsecurity != NULL) { - msre_engine_op_register(modsecurity->msre, name, fn_init, fn_exec); + msre_engine_op_register(modsecurity->msre, name, (fn_op_param_init_t)fn_init, (fn_op_execute_t)fn_exec); } } diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index 6448ca0b..eec3eed0 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -480,9 +480,8 @@ apr_status_t modsecurity_process_phase(modsec_rec *msr, int phase) { break; default : msr_log(msr, 1, "Invalid processing phase: %d", msr->phase); - return -1; break; } - return 0; + return -1; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 0c867b20..0555f1b2 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -75,8 +75,8 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define PHASE_FIRST PHASE_REQUEST_HEADERS #define PHASE_LAST PHASE_LOGGING -#define NOT_SET -1 -#define NOT_SET_P (void *)-1 +#define NOT_SET -1l +#define NOT_SET_P (void *)-1l #define CREATEMODE ( APR_UREAD | APR_UWRITE | APR_GREAD ) #define CREATEMODE_DIR ( APR_UREAD | APR_UWRITE | APR_UEXECUTE | APR_GREAD | APR_GEXECUTE ) diff --git a/apache2/re.h b/apache2/re.h index 66ba0bcf..9d9dc1f1 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -145,17 +145,16 @@ apr_status_t DSOLOCAL msre_rule_process(msre_rule *rule, modsec_rec *msr); #define PHASE_RESPONSE_BODY 4 #define PHASE_LOGGING 5 -#define FN_OP_PARAM_INIT(X) int (*X)(msre_rule *rule, char **error_msg) -#define FN_OP_EXECUTE(X) int (*X)(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) - +typedef int (*fn_op_param_init_t)(msre_rule *rule, char **error_msg); +typedef int (*fn_op_execute_t)(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg); struct msre_op_metadata { const char *name; - FN_OP_PARAM_INIT (param_init); - FN_OP_EXECUTE (execute); + fn_op_param_init_t param_init; + fn_op_execute_t execute; }; -#define FN_TFN_EXECUTE(X) int (*X)(apr_pool_t *pool, unsigned char *input, long int input_length, char **rval, long int *rval_length) +typedef int (*fn_tfn_execute_t)(apr_pool_t *pool, unsigned char *input, long int input_length, char **rval, long int *rval_length); struct msre_tfn_metadata { const char *name; @@ -171,14 +170,14 @@ struct msre_tfn_metadata { * * NOTE Strict transformation functions not supported yet. */ - FN_TFN_EXECUTE(execute); + fn_tfn_execute_t execute; }; void DSOLOCAL msre_engine_tfn_register(msre_engine *engine, const char *name, - FN_TFN_EXECUTE(execute)); + fn_tfn_execute_t execute); void DSOLOCAL msre_engine_op_register(msre_engine *engine, const char *name, - FN_OP_PARAM_INIT(fn1), FN_OP_EXECUTE(fn2)); + fn_op_param_init_t fn1, fn_op_execute_t fn2); void DSOLOCAL msre_engine_register_default_tfns(msre_engine *engine); @@ -193,16 +192,16 @@ msre_tfn_metadata DSOLOCAL *msre_engine_tfn_resolve(msre_engine *engine, const c #define VAR_DONT_CACHE 0 #define VAR_CACHE 1 -#define FN_VAR_VALIDATE(X) char *(*X)(msre_ruleset *ruleset, msre_var *var) -#define FN_VAR_GENERATE(X) int (*X)(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *table, apr_pool_t *mptmp) +typedef char *(*fn_var_validate_t)(msre_ruleset *ruleset, msre_var *var); +typedef int (*fn_var_generate_t)(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *table, apr_pool_t *mptmp); struct msre_var_metadata { const char *name; unsigned int type; /* VAR_TYPE_ constants */ unsigned int argc_min; unsigned int argc_max; - FN_VAR_VALIDATE (validate); - FN_VAR_GENERATE (generate); + fn_var_validate_t validate; + fn_var_generate_t generate; unsigned int is_cacheable; /* 0 - no, 1 - yes */ unsigned int availability; /* when does this variable become available? */ }; @@ -250,7 +249,7 @@ struct msre_actionset { void DSOLOCAL msre_engine_variable_register(msre_engine *engine, const char *name, unsigned int type, unsigned int argc_min, unsigned int argc_max, - FN_VAR_VALIDATE(validate), FN_VAR_GENERATE(generate), + fn_var_validate_t validate, fn_var_generate_t generate, unsigned int is_cacheable, unsigned int availability); msre_actionset DSOLOCAL *msre_actionset_create(msre_engine *engine, const char *text, @@ -263,9 +262,9 @@ msre_actionset DSOLOCAL *msre_actionset_create_default(msre_engine *engine); void DSOLOCAL msre_actionset_init(msre_actionset *actionset, msre_rule *rule); -#define FN_ACTION_VALIDATE(X) char *(*X)(msre_engine *engine, msre_action *action) -#define FN_ACTION_INIT(X) apr_status_t (*X)(msre_engine *engine, msre_actionset *actionset, msre_action *action) -#define FN_ACTION_EXECUTE(X) apr_status_t (*X)(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) +typedef char *(*fn_action_validate_t)(msre_engine *engine, msre_action *action); +typedef apr_status_t (*fn_action_init_t)(msre_engine *engine, msre_actionset *actionset, msre_action *action); +typedef apr_status_t (*fn_action_execute_t)(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action); #define ACTION_DISRUPTIVE 1 #define ACTION_NON_DISRUPTIVE 2 @@ -285,9 +284,9 @@ struct msre_action_metadata { unsigned int argc_max; unsigned int allow_param_plusminus; unsigned int cardinality; - FN_ACTION_VALIDATE (validate); - FN_ACTION_INIT (init); - FN_ACTION_EXECUTE (execute); + fn_action_validate_t validate; + fn_action_init_t init; + fn_action_execute_t execute; }; struct msre_action { diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 97481827..bef9de42 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -16,8 +16,8 @@ */ static void msre_engine_action_register(msre_engine *engine, const char *name, unsigned int type, unsigned int argc_min, unsigned int argc_max, unsigned int allow_param_plusminus, - unsigned int cardinality, FN_ACTION_VALIDATE(validate), FN_ACTION_INIT(init), - FN_ACTION_EXECUTE(execute)) + unsigned int cardinality, fn_action_validate_t validate, fn_action_init_t init, + fn_action_execute_t execute) { msre_action_metadata *metadata = (msre_action_metadata *)apr_pcalloc(engine->mp, sizeof(msre_action_metadata)); diff --git a/apache2/re_operators.c b/apache2/re_operators.c index a91cf90d..32ca54fa 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -19,7 +19,7 @@ * */ void msre_engine_op_register(msre_engine *engine, const char *name, - FN_OP_PARAM_INIT(fn1), FN_OP_EXECUTE(fn2)) + fn_op_param_init_t fn1, fn_op_execute_t fn2) { msre_op_metadata *metadata = (msre_op_metadata *)apr_pcalloc(engine->mp, sizeof(msre_op_metadata)); @@ -1240,17 +1240,17 @@ static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, int rc = validate_url_encoding(var->value, var->value_len); switch(rc) { case 1 : - return 0; /* Encoding is valid, no match. */ + /* Encoding is valid */ break; case -2 : *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Non-hexadecimal " "digits used."); - return 1; /* Invalid, match. */ + return 1; /* Invalid match. */ break; case -3 : *error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Not enough characters " "at the end of input."); - return 1; /* Invalid, match. */ + return 1; /* Invalid match. */ break; case -1 : default : diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index be0d5711..992f4835 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -432,7 +432,7 @@ static int msre_fn_normalisePathWin_execute(apr_pool_t *mptmp, unsigned char *in * Registers one transformation function with the engine. */ void msre_engine_tfn_register(msre_engine *engine, const char *name, - FN_TFN_EXECUTE(execute)) + fn_tfn_execute_t execute) { msre_tfn_metadata *metadata = (msre_tfn_metadata *)apr_pcalloc(engine->mp, sizeof(msre_tfn_metadata)); diff --git a/apache2/re_variables.c b/apache2/re_variables.c index d75afafa..18e97eee 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -2094,7 +2094,7 @@ static int var_webappid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule */ void msre_engine_variable_register(msre_engine *engine, const char *name, unsigned int type, unsigned int argc_min, unsigned int argc_max, - FN_VAR_VALIDATE(validate), FN_VAR_GENERATE(generate), + fn_var_validate_t validate, fn_var_generate_t generate, unsigned int is_cacheable, unsigned int availability) { msre_var_metadata *metadata = (msre_var_metadata *)apr_pcalloc(engine->mp, From 7f71ae377ceb7bf9f7f1eabca0cda8582db2adfb Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 7 Nov 2007 20:00:26 +0000 Subject: [PATCH 152/372] Fix another warning on %u used where %lu needed. --- apache2/apache2_config.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index eec00ce7..ae2f1bb9 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1315,7 +1315,7 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const * 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: %u", (unsigned long)NOT_SET); + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET); } dcfg->cache_trans_min = (apr_size_t)intval; } @@ -1335,10 +1335,10 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const * 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: %u", (unsigned long)NOT_SET); + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET); } if ((intval != 0) && ((apr_size_t)intval < dcfg->cache_trans_min)) { - return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %u < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min); + return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %lu < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min); } dcfg->cache_trans_max = (apr_size_t)intval; From 83fb4b4da409d06958d4666cc163b64dbca59364 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 7 Nov 2007 20:22:09 +0000 Subject: [PATCH 153/372] Fix more formatting errors/warnings on 64bit systems. --- apache2/msc_geo.c | 4 ++-- apache2/msc_reqbody.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 4f0df69c..6693f05d 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -165,7 +165,7 @@ static int db_open(directory_config *dcfg, char **error_msg) fprintf(stderr, "GEO: read 0x%02x%02x%02x\n", buf[0], buf[1], buf[2]); #endif if ((rc != APR_SUCCESS) || (nbytes != 3)) { - *error_msg = apr_psprintf(mp, "Could not read from geo database \"%s\" (%d/3 bytes read): %s", geo->dbfn, nbytes, apr_strerror(rc, errstr, 1024)); + *error_msg = apr_psprintf(mp, "Could not read from geo database \"%s\" (%" APR_SIZE_T_FMT "/3 bytes read): %s", geo->dbfn, nbytes, apr_strerror(rc, errstr, 1024)); return -1; } if ((buf[0] == 0xff) && (buf[1] == 0xff) && (buf[2] == 0xff)) { @@ -190,7 +190,7 @@ static int db_open(directory_config *dcfg, char **error_msg) memset(buf, 0, 3); rc = apr_file_read_full(geo->db, &buf, 3, &nbytes); if ((rc != APR_SUCCESS) || (nbytes != 3)) { - *error_msg = apr_psprintf(mp, "Could not read geo database \"%s\" country offset (%d/3 bytes read): %s", geo->dbfn, nbytes, apr_strerror(rc, errstr, 1024)); + *error_msg = apr_psprintf(mp, "Could not read geo database \"%s\" country offset (%" APR_SIZE_T_FMT "/3 bytes read): %s", geo->dbfn, nbytes, apr_strerror(rc, errstr, 1024)); return -1; } #ifdef DEBUG_CONF diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 8f96505d..d44efc97 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -197,7 +197,7 @@ 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 %d bytes for request body chunk.", 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; } @@ -316,12 +316,12 @@ 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: %d", 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); if (msr->msc_reqbody_buffer == NULL) { - *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %" APR_SIZE_T_FMT " bytes.", + *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; } @@ -442,7 +442,7 @@ 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 %d bytes for request body disk chunk.", 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; @@ -451,7 +451,7 @@ 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 %d bytes for request body disk chunk.", 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; } From cd2287a412652a0dfd957e3b7e63265390d03150 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 8 Nov 2007 18:12:51 +0000 Subject: [PATCH 154/372] Fix for an evasion false positive. --- apache2/msc_multipart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 64f5264e..9868b588 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -950,7 +950,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, } if ( (p != msr->mpd->buf + 2) - && (strncmp(p, msr->mpd->boundary, strlen(msr->mpd->boundary))) + && (strncmp(p, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) ) { /* Found whitespace in front of a boundary. */ msr->mpd->flag_error = 1; From b9defc0adb18dc695dd917c8d8a7958e93995e9f Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 8 Nov 2007 18:20:24 +0000 Subject: [PATCH 155/372] Warn in the debug log when request body processing fails. --- apache2/msc_reqbody.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index d44efc97..597b7c7a 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -265,6 +265,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, *error_msg = apr_psprintf(msr->mp, "Request body processor 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); } } #ifdef WITH_LIBXML2 @@ -274,6 +275,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, *error_msg = apr_psprintf(msr->mp, "Request body processor 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); } } #endif @@ -282,7 +284,8 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /* Do nothing, URLENCODED processor does not support streaming. */ } 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; } } From aff6900539c9e0bc69b541e5464199b73c535bcc Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 15 Nov 2007 19:09:14 +0000 Subject: [PATCH 156/372] Initial full pass through code to fix issues with 64-bit/mismatch sign/mismatch size printf style formatters. Still need to look more into how we are handling time and convert to apr_time_t (or time_t) where appropriate. Still need to look into our use of 'long' as windows is LLP64 where 'long' is still 32-bit. --- apache2/acmp.c | 1 - apache2/apache2_config.c | 6 +++--- apache2/apache2_io.c | 8 ++++---- apache2/apache2_util.c | 8 ++++---- apache2/mod_security2.c | 16 ++++++++-------- apache2/msc_logging.c | 12 ++++++------ apache2/msc_multipart.c | 8 ++++---- apache2/msc_multipart.h | 2 +- apache2/msc_reqbody.c | 5 +++-- apache2/msc_xml.c | 2 +- apache2/pdf_protect.c | 4 ++-- apache2/persist_dbm.c | 16 ++++++++-------- apache2/re.c | 10 ++++++---- apache2/re_actions.c | 30 +++++++++++++++--------------- apache2/re_operators.c | 4 ++-- apache2/re_variables.c | 12 ++++++------ 16 files changed, 73 insertions(+), 71 deletions(-) diff --git a/apache2/acmp.c b/apache2/acmp.c index d5a6dba8..a12da9aa 100644 --- a/apache2/acmp.c +++ b/apache2/acmp.c @@ -431,7 +431,6 @@ static void acmp_found(ACMP *parser, acmp_node_t *node) { parser->bp_buffer[(parser->char_pos - node->depth - 1) % parser->bp_buff_len], parser->char_pos - node->depth - 1); } - /* printf("found: %s at position %d\n", node->pattern, parser->char_pos - node->depth - 1); */ node->hit_count++; parser->hit_count++; } diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index ae2f1bb9..1225120b 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -27,7 +27,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { if (dcfg == NULL) return NULL; #ifdef DEBUG_CONF - fprintf(stderr, "Created directory config %x path %s\n", dcfg, path); + fprintf(stderr, "Created directory config %p path %s\n", (void *)dcfg, path); #endif dcfg->mp = mp; @@ -193,7 +193,7 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { directory_config *merged = create_directory_config(mp, NULL); #ifdef DEBUG_CONF - fprintf(stderr, "Merge parent %x child %x RESULT %x\n", _parent, _child, merged); + fprintf(stderr, "Merge parent %p child %p RESULT %p\n", _parent, _child, merged); #endif if (merged == NULL) return NULL; @@ -1114,7 +1114,7 @@ static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, const cha msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); #ifdef DEBUG_CONF - fprintf(stderr, "Added exception %x (%d %s) to dcfg %x.\n", re, re->type, re->param, dcfg); + fprintf(stderr, "Added exception %p (%d %s) to dcfg %p.\n", re, re->type, re->param, dcfg); #endif return NULL; diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 411105e1..7c773210 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -48,7 +48,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, if ((msr->if_status == IF_STATUS_COMPLETE)||(msr->if_status == IF_STATUS_NONE)) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %x, r %x).", f, f->r); + msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %p, r %p).", f, f->r); } ap_remove_input_filter(f); return ap_get_brigade(f->next, bb_out, mode, block, nbytes); @@ -56,7 +56,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Forwarding input: mode=%d, block=%d, nbytes=%" APR_OFF_T_FMT - " (f %x, r %x).", mode, block, nbytes, f, f->r); + " (f %p, r %p).", mode, block, nbytes, f, f->r); } if (msr->if_started_forwarding == 0) { @@ -209,7 +209,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Input filter: Bucket type %s contains %d bytes.", + msr_log(msr, 9, "Input filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.", bucket->type->name, buflen); } @@ -483,7 +483,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { msr->r = r; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Receiving output (f %x, r %x).", f, f->r); + msr_log(msr, 4, "Output filter: Receiving output (f %p, r %p).", f, f->r); } /* Initialise on first invocation */ diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index e9c75b73..11a65014 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -92,7 +92,7 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * procnew = apr_pcalloc(r->pool, sizeof(*procnew)); if (procnew == NULL) { - msr_log(msr, 1, "Exec: Unable to allocate %d bytes.", sizeof(*procnew)); + msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew)); return -1; } @@ -246,9 +246,9 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, /* Construct the message. */ apr_vsnprintf(str1, sizeof(str1), text, ap); - apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%lx][rid#%lx][%s][%d] %s\n", - current_logtime(msr->mp), ap_get_server_name(r), (unsigned long)(r->server), - (unsigned long)r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), + apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%p][rid#%p][%s][%d] %s\n", + current_logtime(msr->mp), ap_get_server_name(r), (r->server), + r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), level, str1); /* Write to the debug log. */ diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index a936a95d..a5e4670a 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -348,7 +348,7 @@ static modsec_rec *create_tx_context(request_rec *r) { store_tx_context(msr, r); if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Transaction context created (dcfg %x).", msr->dcfg1); + msr_log(msr, 4, "Transaction context created (dcfg %p).", msr->dcfg1); } return msr; @@ -449,7 +449,7 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t if (first_time == 0) { ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot checkpoint #2 (pid=%d ppid=%d)", (int)getpid(), (int)getppid()); + "ModSecurity: chroot checkpoint #2 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid()); if (chdir(chroot_dir) < 0) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, @@ -478,7 +478,7 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t "ModSecurity: chroot successful, path=%s", chroot_dir); } else { ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity: chroot checkpoint #1 (pid=%d ppid=%d)", (int)getpid(), (int)getppid()); + "ModSecurity: chroot checkpoint #1 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid()); } } #endif @@ -625,7 +625,7 @@ static int hook_request_late(request_rec *r) { } if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Second phase starting (dcfg %x).", msr->dcfg2); + msr_log(msr, 4, "Second phase starting (dcfg %p).", msr->dcfg2); } /* Figure out whether or not to extract multipart files. */ @@ -895,7 +895,7 @@ 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 %x).", (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); + msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %p).", (((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r); } ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection); @@ -910,7 +910,7 @@ static void hook_insert_filter(request_rec *r) { /* We always add the PDF XSS protection filter. */ if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %x).", r); + msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %p).", r); } ap_add_output_filter("PDFP_OUT", msr, r, r->connection); @@ -930,7 +930,7 @@ static void hook_insert_filter(request_rec *r) { */ if (msr->of_status != OF_STATUS_COMPLETE) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %x).", r); + msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %p).", r); } ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); @@ -963,7 +963,7 @@ static void hook_insert_error_filter(request_rec *r) { */ if (msr->of_status != OF_STATUS_COMPLETE) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %x).", r); + msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %p).", r); } /* Make a note that the output we will be receiving is a diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 334761f5..6fc26c88 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -78,7 +78,7 @@ char *construct_log_vcombinedus(modsec_rec *msr) { /* sessionid */ sessionid = (msr->sessionid == NULL ? "-" : msr->sessionid); - return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %d %" APR_OFF_T_FMT " \"%s\" \"%s\" %s \"%s\"", + return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %u %" APR_OFF_T_FMT " \"%s\" \"%s\" %s \"%s\"", log_escape_nq(msr->mp, msr->hostname), msr->remote_addr, log_escape_nq(msr->mp, remote_user), log_escape_nq(msr->mp, local_user), current_logtime(msr->mp), ((msr->request_line == NULL) ? "" : log_escape(msr->mp, msr->request_line)), @@ -203,7 +203,7 @@ char *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_li *was_limited = 0; } - return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %d %s \"%s\" \"%s\" %s \"%s\"", + return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %u %s \"%s\" \"%s\" %s \"%s\"", hostname, msr->remote_addr, remote_user, local_user, current_logtime(msr->mp), the_request, msr->response_status, bytes_sent, referer, user_agent, @@ -284,7 +284,7 @@ static void sanitise_request_line(modsec_rec *msr) { j = arg->value_origin_offset; while((*p != '\0')&&(j--)) p++; if (*p == '\0') { - msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %d of QUERY_STRING" + msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %u of QUERY_STRING" "because the request line is too short.", log_escape_ex(msr->mp, arg->name, arg->name_len), arg->value_origin_offset); @@ -297,7 +297,7 @@ static void sanitise_request_line(modsec_rec *msr) { *p++ = '*'; } if (*p == '\0') { - msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %d (size %d) " + msr_log(msr, 1, "Unable to sanitise variable \"%s\" at offset %u (size %d) " "of QUERY_STRING because the request line is too short.", log_escape_ex(msr->mp, arg->name, arg->name_len), arg->value_origin_offset, arg->value_origin_len); @@ -418,7 +418,7 @@ void sec_audit_logger(modsec_rec *msr) { /* Format: time transaction_id remote_addr remote_port local_addr local_port */ - text = apr_psprintf(msr->mp, "[%s] %s %s %d %s %d", + text = apr_psprintf(msr->mp, "[%s] %s %s %u %s %u", current_logtime(msr->mp), msr->txid, msr->remote_addr, msr->remote_port, msr->local_addr, msr->local_port); sec_auditlog_write(msr, text, strlen(text)); @@ -638,7 +638,7 @@ void sec_audit_logger(modsec_rec *msr) { text = apr_psprintf(msr->mp, "%s %s\n", msr->response_protocol, msr->status_line); } else { - text = apr_psprintf(msr->mp, "%s %d\n", msr->response_protocol, + text = apr_psprintf(msr->mp, "%s %u\n", msr->response_protocol, msr->response_status); } sec_auditlog_write(msr, text, strlen(text)); diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 9868b588..22dfddea 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -520,7 +520,7 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err *(multipart_part **)apr_array_push(msr->mpd->parts) = msr->mpd->mpp; if (msr->mpd->mpp->type == MULTIPART_FILE) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added file part %x to the list: name \"%s\" " + msr_log(msr, 9, "Multipart: Added file part %p to the list: name \"%s\" " "file name \"%s\" (offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), log_escape(msr->mp, msr->mpd->mpp->filename), @@ -529,7 +529,7 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err } else { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added part %x to the list: name \"%s\" " + msr_log(msr, 9, "Multipart: Added part %p to the list: name \"%s\" " "(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), msr->mpd->mpp->offset, msr->mpd->mpp->length); } @@ -833,7 +833,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, msr->mpd->flag_data_before = 1; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %d bytes)", size); + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %u bytes)", size); } return 1; @@ -1031,7 +1031,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, msr->mpd->flag_data_after = 1; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%d bytes left)", inleft); + msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%u bytes left)", inleft); } return 1; diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h index c68d2780..cb07bf2c 100644 --- a/apache2/msc_multipart.h +++ b/apache2/msc_multipart.h @@ -45,7 +45,7 @@ struct multipart_part { /* files only, the name of the temporary file holding data */ char *tmp_file_name; int tmp_file_fd; - unsigned tmp_file_size; + unsigned int tmp_file_size; /* files only, filename as supplied by the browser */ char *filename; diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 597b7c7a..6358f797 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -300,7 +300,7 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, } /* Should never happen. */ - *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %d", + *error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %u", msr->msc_reqbody_storage); return -1; } @@ -592,7 +592,8 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, return 1; /* More data available. */ } - *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %d", + /* Should never happen. */ + *error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %u", msr->msc_reqbody_storage); return -1; diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index 4e31544d..7eab6937 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -109,7 +109,7 @@ int xml_complete(modsec_rec *msr, char **error_msg) { /* Clean up everything else. */ xmlFreeParserCtxt(msr->xml->parsing_ctx); msr->xml->parsing_ctx = NULL; - msr_log(msr, 4, "XML: Parsing complete (well_formed %d).", msr->xml->well_formed); + msr_log(msr, 4, "XML: Parsing complete (well_formed %u).", msr->xml->well_formed); if (msr->xml->well_formed != 1) { *error_msg = apr_psprintf(msr->mp, "XML: Failed parsing document."); diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index fe26837d..edea9dc8 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -64,7 +64,7 @@ static char *create_hash(modsec_rec *msr, * */ static char *create_token(modsec_rec *msr) { - unsigned int current_time; + apr_time_t current_time; const char *time_string = NULL; const char *hash = NULL; int timeout = DEFAULT_TIMEOUT; @@ -74,7 +74,7 @@ static char *create_token(modsec_rec *msr) { } current_time = apr_time_sec(apr_time_now()); - time_string = apr_psprintf(msr->mp, "%d", current_time + timeout); + time_string = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(current_time + timeout)); if (time_string == NULL) return NULL; hash = create_hash(msr, time_string); diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 218eaff8..43c31938 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -150,7 +150,7 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, if (var == NULL) { /* Error. */ } else { - int td; + apr_time_t td; counter = atoi(var->value); var = (msc_string *)apr_table_get(col, "UPDATE_RATE"); if (var == NULL) { @@ -161,13 +161,13 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, } /* NOTE: No rate if there has been no time elapsed */ - td = (int)(apr_time_sec(apr_time_now()) - create_time); + td = (apr_time_sec(apr_time_now()) - create_time); if (td == 0) { var->value = apr_psprintf(msr->mp, "%d", 0); } else { - var->value = apr_psprintf(msr->mp, "%d", - (int)((60 * counter)/td)); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, + (apr_time_t)((60 * counter)/td)); } var->value_len = strlen(var->value); } @@ -279,7 +279,7 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { int timeout = atoi(var->value); var = (msc_string *)apr_table_get(col, "__expire_KEY"); if (var != NULL) { - var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(apr_time_now()) + timeout)); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout)); var->value_len = strlen(var->value); } } @@ -294,7 +294,7 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { var->name_len = strlen(var->name); apr_table_setn(col, var->name, (void *)var); } - var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(apr_time_now()))); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()))); var->value_len = strlen(var->value); } @@ -418,7 +418,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { apr_array_header_t *keys_arr; char **keys; int i; - unsigned int now = (unsigned int)apr_time_sec(msr->request_time); + apr_time_t now = apr_time_sec(msr->request_time); if (msr->txcfg->data_dir == NULL) { /* The user has been warned about this problem enough times already by now. @@ -493,7 +493,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { } else { unsigned int expiry_time = atoi(var->value); - msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %d seconds.", + msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.", log_escape(msr->mp, col_name), log_escape(msr->mp, key.dptr), expiry_time - now); diff --git a/apache2/re.c b/apache2/re.c index 4fe98bb6..908a2bdf 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -351,6 +351,7 @@ int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, for(;;) { if (*p == '\0') { + // TODO better 64-bit support here *error_msg = apr_psprintf(mp, "Missing closing quote at position %d: %s", (int)(p - text), text); free(value); @@ -358,6 +359,7 @@ int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, } else if (*p == '\\') { if ( (*(p + 1) == '\0') || ((*(p + 1) != '\'')&&(*(p + 1) != '\\')) ) { + // TODO better 64-bit support here *error_msg = apr_psprintf(mp, "Invalid quoted pair at position %d: %s", (int)(p - text), text); free(value); @@ -773,7 +775,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) if (rule->actionset != NULL && rule->actionset->rev != NULL) { rev = apr_psprintf(p, " [rev \"%s\"]", rule->actionset->rev); } - msr_log(msr, 4, "Recipe: Invoking rule %x;%s%s%s.", + msr_log(msr, 4, "Recipe: Invoking rule %p;%s%s%s.", rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : "")); } @@ -926,7 +928,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; - msr_log(msr, 1, "Rule %x [id \"%s\"][file \"%s\"][line \"%d\"]: %lu usec", rule, + msr_log(msr, 1, "Rule %p [id \"%s\"][file \"%s\"][line \"%d\"]: %lu usec", rule, ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", rule->filename != NULL ? rule->filename : "-", rule->line_num, @@ -1554,11 +1556,11 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { /* check the cache options */ if (var->value_len < msr->txcfg->cache_trans_min) { - msr_log(msr, 9, "CACHE: Disabled - %s value length=%d, smaller than minlen=%d", var->name, var->value_len, msr->txcfg->cache_trans_min); + 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)) { - msr_log(msr, 9, "CACHE: Disabled - %s value length=%d, larger than maxlen=%d", var->name, var->value_len, msr->txcfg->cache_trans_max); + 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; } diff --git a/apache2/re_actions.c b/apache2/re_actions.c index bef9de42..5691c433 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -557,7 +557,7 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) if (limit > REQUEST_BODY_HARD_LIMIT) { return apr_psprintf(engine->mp, "Request size limit cannot exceed " - "the hard limit: %li", RESPONSE_BODY_HARD_LIMIT); + "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT); } return NULL; @@ -572,7 +572,7 @@ static char *msre_action_ctl_validate(msre_engine *engine, msre_action *action) if (limit > RESPONSE_BODY_HARD_LIMIT) { return apr_psprintf(engine->mp, "Response size limit cannot exceed " - "the hard limit: %li", RESPONSE_BODY_HARD_LIMIT); + "the hard limit: %ld", RESPONSE_BODY_HARD_LIMIT); } return NULL; @@ -1118,7 +1118,7 @@ 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); - var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(msr->request_time) + 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); @@ -1140,8 +1140,8 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t char *s = NULL; apr_table_t *target_col = NULL; msc_string *var = NULL, *var_last_update_time = NULL; - unsigned int last_update_time, current_time; - long int current_value, new_value; + apr_time_t last_update_time, current_time; + long current_value, new_value; /* Extract the name and the value. */ /* IMP1 We have a function for this now, parse_name_eq_value? */ @@ -1193,7 +1193,7 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t return 0; } - current_time = (unsigned int)apr_time_sec(apr_time_now()); + current_time = apr_time_sec(apr_time_now()); last_update_time = atoi(var_last_update_time->value); s = strstr(var_value, "/"); @@ -1209,24 +1209,24 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t * time elapsed since the last update. */ new_value = current_value - - (atoi(var_value) * ((current_time - last_update_time) / atoi(s))); + (atol(var_value) * ((current_time - last_update_time) / atol(s))); if (new_value < 0) new_value = 0; /* Only change the value if it differs. */ if (new_value != current_value) { - var->value = apr_psprintf(msr->mp, "%d", (int)new_value); + var->value = apr_psprintf(msr->mp, "%ld", new_value); var->value_len = strlen(var->value); - msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %li to %li (%d seconds since " + msr_log(msr, 4, "Deprecated variable \"%s.%s\" from %ld to %ld (%" APR_TIME_T_FMT " seconds since " "last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), - current_value, new_value, current_time - last_update_time); + current_value, new_value, (apr_time_t)(current_time - last_update_time)); apr_table_set(msr->collections_dirty, col_name, "1"); } else { - msr_log(msr, 9, "Not deprecating variable \"%s.%s\" because the new value (%li) is " - "the same as the old one (%li) (%d seconds since last update).", + msr_log(msr, 9, "Not deprecating variable \"%s.%s\" because the new value (%ld) is " + "the same as the old one (%ld) (%" APR_TIME_T_FMT " seconds since last update).", log_escape(msr->mp, col_name), log_escape(msr->mp, var_name), current_value, - new_value, current_time - last_update_time); + new_value, (apr_time_t)(current_time - last_update_time)); } return 1; @@ -1263,7 +1263,7 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = "__expire_KEY"; var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%d", (int)(apr_time_sec(msr->request_time) + 3600)); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(msr->request_time) + 3600)); var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); @@ -1311,7 +1311,7 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var = apr_pcalloc(msr->mp, sizeof(msc_string)); var->name = "CREATE_TIME"; var->name_len = strlen(var->name); - var->value = apr_psprintf(msr->mp, "%d", (int)apr_time_sec(msr->request_time)); + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)apr_time_sec(msr->request_time)); var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 32ca54fa..95fdeaf5 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -278,7 +278,7 @@ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { rc = apr_file_gets(buf, HUGE_STRING_LEN, fd); if (rc == APR_EOF) break; if (rc != APR_SUCCESS) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Could read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); return 0; } @@ -407,7 +407,7 @@ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *va /* scan for first character, then compare from there until we * have a match or there is no room left in the target */ - msr_log(msr, 9, "match[%d]='%s' target[%d]='%s'", match_length, match, target_length, target); + msr_log(msr, 9, "match[%u]='%s' target[%u]='%s'", match_length, match, target_length, target); i_max = match_length - target_length; for (i = 0; i <= i_max; i++) { if (match[i] == target[0]) { diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 18e97eee..3a5d4535 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -681,7 +681,7 @@ static int var_remote_host_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_remote_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - char *value = apr_psprintf(mptmp, "%d", msr->remote_port); + char *value = apr_psprintf(mptmp, "%u", msr->remote_port); return var_simple_generate(var, vartab, mptmp, value); } @@ -1563,7 +1563,7 @@ static int var_time_epoch_generate(modsec_rec *msr, msre_var *var, msre_rule *ru tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); - rvar->value = apr_psprintf(mptmp, "%d", (int)tc); + rvar->value = apr_psprintf(mptmp, "%ld", (long)tc); rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1824,7 +1824,7 @@ static int var_server_name_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_server_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - char *value = apr_psprintf(mptmp, "%d", msr->local_port); + char *value = apr_psprintf(mptmp, "%u", msr->local_port); return var_simple_generate(var, vartab, mptmp, value); } @@ -1851,7 +1851,7 @@ static int var_script_filename_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_script_gid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - char *value = apr_psprintf(mptmp, "%d", msr->r->finfo.group); + char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.group); return var_simple_generate(var, vartab, mptmp, value); } @@ -1881,7 +1881,7 @@ static int var_script_mode_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_script_uid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - char *value = apr_psprintf(mptmp, "%d", msr->r->finfo.user); + char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.user); return var_simple_generate(var, vartab, mptmp, value); } @@ -2039,7 +2039,7 @@ static int var_response_protocol_generate(modsec_rec *msr, msre_var *var, msre_r static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - const char *value = apr_psprintf(mptmp, "%d", msr->response_status); + const char *value = apr_psprintf(mptmp, "%u", msr->response_status); return var_simple_generate(var, vartab, mptmp, value); } From 40c5b2004f4c827aae025c3ed59c6687bb1ccc77 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 15 Nov 2007 19:11:59 +0000 Subject: [PATCH 157/372] Remove extraneous 'void *' cast. --- apache2/apache2_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 1225120b..8386ab2f 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -27,7 +27,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { if (dcfg == NULL) return NULL; #ifdef DEBUG_CONF - fprintf(stderr, "Created directory config %p path %s\n", (void *)dcfg, path); + fprintf(stderr, "Created directory config %p path %s\n", dcfg, path); #endif dcfg->mp = mp; From 2cefbda2e35d995fd596987b2184dfa23102725d Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 19 Nov 2007 17:19:13 +0000 Subject: [PATCH 158/372] Fix quotes in an 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 b31370f1..5b3ba93f 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3688,8 +3688,8 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requ Example: - SecRule REQUEST_HEADERS:User-Agent "nikto" log,deny,msg:'Nikto Scanners Identified" + SecRule REQUEST_HEADERS:User-Agent "nikto" "log,deny,msg:'Nikto Scanners Identified'"
From f0be2ff6b0b5446788a16a853b707bb75f979405 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 26 Nov 2007 15:05:48 +0000 Subject: [PATCH 159/372] Added warning message when XML request body parser fails. --- apache2/msc_reqbody.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 6358f797..2ef6d9ea 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -76,6 +76,7 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { *error_msg = apr_psprintf(msr->mp, "Multipart parser init failed: %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); } } #ifdef WITH_LIBXML2 @@ -85,6 +86,7 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { *error_msg = apr_psprintf(msr->mp, "XML parser init failed: %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); } } #endif From e467d3cac0bd103f270f835e635f003e1b980490 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 26 Nov 2007 15:39:37 +0000 Subject: [PATCH 160/372] Unified messages in the error log and in the audit log. --- apache2/mod_security2.c | 10 +++++----- apache2/modsecurity.h | 4 ++-- apache2/msc_logging.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index a5e4670a..3ae85d8a 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -488,13 +488,13 @@ 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); + + /* If we've changed the server signature make note of the original. */ if (new_server_signature != NULL) { ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity for Apache %s (build %s) configured - %s", MODULE_RELEASE, modsec_build(mp_temp), real_server_signature); - } - else { - ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, - "ModSecurity for Apache %s (build %s) configured", MODULE_RELEASE, modsec_build(mp_temp)); + "Original server signature: %s", real_server_signature); } } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 0555f1b2..3c325597 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -61,11 +61,11 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define MODSEC_VERSION_TYPE "dev" #define MODSEC_VERSION_RELEASE "3" -#define MODULE_NAME "ModSecurity" +#define MODULE_NAME "ModSecurity for Apache" #define MODULE_RELEASE \ MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \ "-" MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE -#define MODULE_NAME_FULL MODULE_NAME " v" MODULE_RELEASE " (Apache 2.x)" +#define MODULE_NAME_FULL MODULE_NAME "/" MODULE_RELEASE " (http://www.modsecurity.org)" #define PHASE_REQUEST_HEADERS 1 #define PHASE_REQUEST_BODY 2 diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 6fc26c88..789650aa 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -743,7 +743,7 @@ void sec_audit_logger(modsec_rec *msr) { } /* Producer */ - text = apr_psprintf(msr->mp, "Producer: %s\n", MODULE_NAME_FULL); + text = apr_psprintf(msr->mp, "Producer: %s.\n", MODULE_NAME_FULL); sec_auditlog_write(msr, text, strlen(text)); /* Server */ From b163864ba73ca969d31ead0ed08934df47d7151e Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 26 Nov 2007 16:05:56 +0000 Subject: [PATCH 161/372] Implemented SecComponentSignature. --- apache2/apache2_config.c | 176 ++++++++++++++++++++------------------- apache2/modsecurity.h | 5 ++ apache2/msc_logging.c | 37 +++++++- 3 files changed, 130 insertions(+), 88 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 8386ab2f..86970c3c 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -99,6 +99,8 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->cache_trans_min = NOT_SET; dcfg->cache_trans_max = NOT_SET; + dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *)); + return dcfg; } @@ -406,6 +408,10 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { merged->cache_trans_max = (child->cache_trans_max == (apr_size_t)NOT_SET ? parent->cache_trans_max : child->cache_trans_max); + /* Merge component signatures. */ + merged->component_signatures = apr_array_append(mp, parent->component_signatures, + child->component_signatures); + return merged; } @@ -767,6 +773,18 @@ static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { return NULL; } +/** + * Adds component signature to the list of signatures kept in configuration. + */ +static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, const char *p1) { + directory_config *dcfg = (directory_config *)_dcfg; + + /* TODO Enforce "Name/VersionX.Y.Z (comment)" format. */ + *(char **)apr_array_push(dcfg->component_signatures) = (char *)p1; + + return NULL; +} + static const char *cmd_content_injection(cmd_parms *cmd, void *_dcfg, int flag) { directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1056,7 +1074,7 @@ static const char *cmd_rule_import_by_id(cmd_parms *cmd, void *_dcfg, const char re->type = RULE_EXCEPTION_IMPORT_ID; // TODO verify p1 re->param = p1; - *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; + *(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re; return NULL; } @@ -1428,12 +1446,28 @@ const command_rec module_directives[] = { "path to the audit log storage area; absolute, or relative to the root of the server" ), + AP_INIT_TAKE12 ( + "SecCacheTransformations", + cmd_cache_transformations, + NULL, + CMD_SCOPE_ANY, + "whether or not to cache transformations. Defaults to true." + ), + AP_INIT_TAKE1 ( "SecChrootDir", cmd_chroot_dir, NULL, CMD_SCOPE_MAIN, - "Path of the directory to which server will be chrooted" + "path of the directory to which server will be chrooted" + ), + + AP_INIT_TAKE1 ( + "SecComponentSignature", + cmd_component_signature, + NULL, + CMD_SCOPE_MAIN, + "component signature to add to ModSecurity signature." ), AP_INIT_FLAG ( @@ -1485,6 +1519,14 @@ const command_rec module_directives[] = { "" // TODO ), + AP_INIT_TAKE1 ( + "SecGeoLookupsDb", + cmd_geo_lookups_db, + NULL, + RSRC_CONF, + "database for geographical lookups module." + ), + AP_INIT_TAKE12 ( "SecGuardianLog", cmd_guardian_log, @@ -1493,6 +1535,54 @@ const command_rec module_directives[] = { "The filename of the filter debugging log file" ), + AP_INIT_FLAG ( + "SecPdfProtect", + cmd_pdf_protect, + NULL, + RSRC_CONF, + "enable PDF protection module." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectSecret", + cmd_pdf_protect_secret, + NULL, + RSRC_CONF, + "secret that will be used to construct protection tokens." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectTimeout", + cmd_pdf_protect_timeout, + NULL, + RSRC_CONF, + "duration for which protection tokens will be valid." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectTokenName", + cmd_pdf_protect_token_name, + NULL, + RSRC_CONF, + "name of the protection token. The name 'PDFTOKEN' is used by default." + ), + + AP_INIT_FLAG ( + "SecPdfProtectInterceptGETOnly", + cmd_pdf_protect_intercept_get_only, + NULL, + RSRC_CONF, + "whether or not to intercept only GET and HEAD requess. Defaults to true." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectMethod", + cmd_pdf_protect_method, + NULL, + RSRC_CONF, + "protection method to use. Can be 'TokenRedirection' (default) or 'ForcedDownload'" + ), + AP_INIT_TAKE1 ( "SecRequestBodyAccess", cmd_request_body_access, @@ -1573,24 +1663,6 @@ const command_rec module_directives[] = { "On or Off" ), - /* - AP_INIT_TAKE1 ( - "SecRuleImportById", - cmd_rule_import_by_id, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - - AP_INIT_TAKE1 ( - "SecRuleImportByMsg", - cmd_rule_import_by_msg, - NULL, - CMD_SCOPE_ANY, - "" // TODO - ), - */ - AP_INIT_FLAG ( "SecRuleInheritance", cmd_rule_inheritance, @@ -1655,69 +1727,5 @@ const command_rec module_directives[] = { "" // TODO ), - AP_INIT_FLAG ( - "SecPdfProtect", - cmd_pdf_protect, - NULL, - RSRC_CONF, - "enable PDF protection module." - ), - - AP_INIT_TAKE1 ( - "SecPdfProtectSecret", - cmd_pdf_protect_secret, - NULL, - RSRC_CONF, - "secret that will be used to construct protection tokens." - ), - - AP_INIT_TAKE1 ( - "SecPdfProtectTimeout", - cmd_pdf_protect_timeout, - NULL, - RSRC_CONF, - "duration for which protection tokens will be valid." - ), - - AP_INIT_TAKE1 ( - "SecPdfProtectTokenName", - cmd_pdf_protect_token_name, - NULL, - RSRC_CONF, - "name of the protection token. The name 'PDFTOKEN' is used by default." - ), - - AP_INIT_FLAG ( - "SecPdfProtectInterceptGETOnly", - cmd_pdf_protect_intercept_get_only, - NULL, - RSRC_CONF, - "whether or not to intercept only GET and HEAD requess. Defaults to true." - ), - - AP_INIT_TAKE1 ( - "SecPdfProtectMethod", - cmd_pdf_protect_method, - NULL, - RSRC_CONF, - "protection method to use. Can be 'TokenRedirection' (default) or 'ForcedDownload'" - ), - - AP_INIT_TAKE1 ( - "SecGeoLookupsDb", - cmd_geo_lookups_db, - NULL, - RSRC_CONF, - "database for geographical lookups module." - ), - - AP_INIT_TAKE12 ( - "SecCacheTransformations", - cmd_cache_transformations, - NULL, - CMD_SCOPE_ANY, - "whether or not to cache transformations. Defaults to true." - ), - { NULL } }; diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 3c325597..135ff7cd 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -446,6 +446,11 @@ struct directory_config { int cache_trans; apr_size_t cache_trans_min; apr_size_t cache_trans_max; + + /* Array to hold signatures of components, which will + * appear in the ModSecurity signature in the audit log. + */ + apr_array_header_t *component_signatures; }; struct error_message { diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 789650aa..3f79c2e0 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -307,6 +307,37 @@ static void sanitise_request_line(modsec_rec *msr) { } } +/** + * Output the Producer header. + */ +static void sec_auditlog_write_producer_header(modsec_rec *msr) { + char **signatures = NULL; + char *text = NULL; + int i; + + /* 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); + sec_auditlog_write(msr, text, strlen(text)); + + return; + } + + /* Start with the ModSecurity signature. */ + text = apr_psprintf(msr->mp, "Producer: %s", MODULE_NAME_FULL); + sec_auditlog_write(msr, text, strlen(text)); + + + /* Then loop through the components and output individual signatures. */ + signatures = (char **)msr->txcfg->component_signatures->elts; + for(i = 0; i < msr->txcfg->component_signatures->nelts; i++) { + text = apr_psprintf(msr->mp, "; %s", (char *)signatures[i]); + sec_auditlog_write(msr, text, strlen(text)); + } + + sec_auditlog_write(msr, ".\n", 2); +} + /** * Produce an audit log entry. */ @@ -741,10 +772,8 @@ void sec_audit_logger(modsec_rec *msr) { text = apr_psprintf(msr->mp, "Response-Body-Transformed: Dechunked\n"); sec_auditlog_write(msr, text, strlen(text)); } - - /* Producer */ - text = apr_psprintf(msr->mp, "Producer: %s.\n", MODULE_NAME_FULL); - sec_auditlog_write(msr, text, strlen(text)); + + sec_auditlog_write_producer_header(msr); /* Server */ if (msr->server_software != NULL) { From 6ca5b831fbb2b4cc7fe914808702243b3f88cfd2 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 26 Nov 2007 16:22:33 +0000 Subject: [PATCH 162/372] Document SecComponentSignature. Update CHANGES. --- CHANGES | 5 +- doc/modsecurity2-apache-reference.xml | 1756 ++++++++++++------------- 2 files changed, 844 insertions(+), 917 deletions(-) diff --git a/CHANGES b/CHANGES index 46b66c40..1d0c74f4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -02 Nov 2007 - 2.5.0-dev3 + +26 Nov 2007 - 2.5.0-dev3 ------------------------ + * Implement SecComponentSignature. + * Fix warnings on Solaris and/or 64bit builds. * Added skipAfter: action to allow skipping all rules until a rule diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 5b3ba93f..ab0096db 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -149,8 +149,7 @@ 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. + Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, and HP-UX.
@@ -410,7 +409,7 @@ "allow" rules or to correct any false positives in the Core rules as they are applied to your site. - Note + Note It is highly encouraged that you do not edit the Core rules files themselves but rather place all changes (such as @@ -421,23 +420,23 @@
<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. + 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,action2 + Syntax: SecAction + action1,action2,action2 - Example Usage: Example Usage: SecAction nolog,redirect:http://www.hostname.com - ProcessingPhase: Any + ProcessingPhase: Any - Scope: Any + Scope: Any - Dependencies/Notes: None + Dependencies/Notes: None SecAction is best used when you uncondiationally execute an action. This is explicit triggering whereas the normal Actions are @@ -449,25 +448,24 @@
<literal>SecArgumentSeparator</literal> - Description: Specifies which - character to use as separator for + 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: Syntax: SecArgumentSeparator character - Example Usage: Example Usage: SecArgumentSeparator ; - Processing Phase: Any + Processing Phase: Any - Scope: - Main + Scope: Main - Dependencies/Notes: None + Dependencies/Notes: None This directive is needed if a backend web appliaction is using a non-standard argument separator. If this directive is not set properly @@ -479,31 +477,31 @@
<literal>SecAuditEngine</literal> - Description: Configures the audit - logging engine. + Description: Configures the audit logging + engine. - Syntax: Syntax: SecAuditEngine On|Off|RelevantOnly - Example Usage: Example Usage: SecAuditEngine On - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: Can be - set/changed with the "ctl" action for the current transaction. + 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 + SecAuditEngine RelevantOnly SecAuditLog logs/audit/audit.log SecAuditLogParts ABCFHZ SecAuditLogType concurrent SecAuditLogStorageDir logs/audit -SecAuditLogRelevantStatus ^[45] +SecAuditLogRelevantStatus ^[45] Possible values are: @@ -530,22 +528,22 @@ SecAuditLogStorageDir logs/audit
<literal>SecAuditLog</literal> - Description: Defines the path to - the main audit log file. + Description: Defines the path to the main + audit log file. - Syntax: SecAuditLog /path/to/auditlog + Syntax: SecAuditLog + /path/to/auditlog - Example Usage: Example Usage: SecAuditLog /usr/local/apache/logs/audit.log - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: This file is - open on startup when the server typically still runs as + 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.. @@ -564,50 +562,48 @@ SecAuditLogStorageDir logs/audit
<literal>SecAuditLog2</literal> - Description: Defines the path to - the secondary audit log index file when concurrent logging is enabled. - See SecAuditLog2 for more - details. + 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 + Syntax: SecAuditLog2 + /path/to/auditlog2 - Example Usage: Example Usage: SecAuditLog2 /usr/local/apache/logs/audit2.log - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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 the path to - the main audit log file. + Description: Defines the path to the main + audit log file. - Syntax: Syntax: SecAuditLogParts PARTS - Example Usage: Example Usage: SecAuditLogParts ABCFHZ - Processing Phase: N/A + Processing Phase: N/A - Scope: - Any + Scope: Any - Dependencies/Notes: At this time - ModSecurity does not log response bodies of stock Apache responses (e.g. - 404), or the 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. @@ -690,22 +686,23 @@ SecAuditLogStorageDir logs/audit
<literal>SecAuditLogRelevantStatus</literal> - Description: Configures which - response status code is to be considered relevant for the purpose of - audit logging. + Description: Configures which response status + code is to be considered relevant for the purpose of audit + logging. - Syntax: Syntax: SecAuditLogRelevantStatus REGEX - Example Usage: Example Usage: SecAuditLogRelevantStatus ^[45] - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: Must have the - SecAuditEngine set to RelevantOnly. The parameter is a regular + 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 @@ -719,26 +716,25 @@ SecAuditLogStorageDir logs/audit
<literal>SecAuditLogStorageDir</literal> - Description: Configures the - storage directory where concurrent audit log entries are to be - stored. + Description: Configures the storage directory + where concurrent audit log entries are to be stored. - Syntax: Syntax: SecAuditLogStorageDir /path/to/storage/dir - Example Usage: Example Usage: SecAuditLogStorageDir /usr/local/apache/logs/audit - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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. + 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 @@ -748,21 +744,22 @@ SecAuditLogStorageDir logs/audit
<literal>SecAuditLogType</literal> - Description: Configures the type - of audit logging mechanism to be used. + Description: Configures the type of audit + logging mechanism to be used. - Syntax: Syntax: SecAuditLogType Serial|Concurrent - Example Usage: Example Usage: SecAuditLogType Serial - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: Must specify - SecAuditLogStorageDir if you use concurrent logging. + Dependencies/Notes: Must specify + SecAuditLogStorageDir if you use concurrent + logging. Possible values are: @@ -786,22 +783,24 @@ SecAuditLogStorageDir logs/audit
<literal>SecCacheTransformations</literal> - Description: Controls caching of + Description: Controls caching of transformations. - Syntax: Syntax: SecCacheTransformations On|Off [options] - Example Usage: Example Usage: SecCacheTransformations On "minlen:64,maxlen:0" - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: N/A + Version: 2.5.0 + + Dependencies/Notes: N/A First parameter: @@ -839,24 +838,24 @@ SecAuditLogStorageDir logs/audit
<literal>SecChrootDir</literal> - Description: Configures the - directory path that will be used to jail the web server process. + Description: Configures the directory path + that will be used to jail the web server process. - Syntax: SecChrootDir /path/to/chroot/dir + Syntax: SecChrootDir + /path/to/chroot/dir - Example Usage: Example Usage: SecChrootDir /chroot - Processing Phase: N/A + Processing Phase: N/A - Scope: Main + Scope: Main - 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: 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: @@ -889,37 +888,61 @@ SecAuditLogStorageDir logs/audit 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" + + Scope: Main + + Version: 2.5.0 + + 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 (Experimental)</literal> - Description: Enables content - injection using actions append and - prepend. + Description: Enables content injection using + actions append and prepend. - Syntax: - SecContentInjection (On|Off) + Syntax: SecContentInjection + (On|Off) - Example Usage: - SecContentInjection On + Example Usage: SecContentInjection + On + + Version: 2.5.0
<literal>SecCookieFormat</literal> - Description: Selects the cookie - format that will be used in the current configuration context. + Description: Selects the cookie format that + will be used in the current configuration context. - Syntax: Syntax: SecCookieFormat 0|1 - Example Usage: Example Usage: SecCookieFormat 0 - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: None + Dependencies/Notes: None Possible values are: @@ -940,68 +963,67 @@ SecAuditLogStorageDir logs/audit
<literal>SecDataDir</literal> - Description: Path where - persistent data (e.g. IP address data, session data, etc) is to be - stored. + Description: Path where persistent data (e.g. + IP address data, session data, etc) is to be stored. - Syntax: SecDataDir /path/to/dir + Syntax: SecDataDir + /path/to/dir - Example Usage: Example Usage: SecDataDir /usr/local/apache/logs/data - Processing Phase: N/A + Processing Phase: N/A - Scope: + 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. + Description: Path to the ModSecurity debug + log file. - Syntax: SecDebugLog /path/to/modsec-debug.log + Syntax: SecDebugLog + /path/to/modsec-debug.log - Example Usage: Example Usage: SecDebugLog /usr/local/apache/logs/modsec-debug.log - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: None + Dependencies/Notes: None
<literal>SecDebugLogLevel</literal> - Description: Configures the - verboseness of the debug log data. + Description: Configures the verboseness of + the debug log data. - Syntax: Syntax: SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 - Example Usage: Example Usage: SecDebugLogLevel 4 - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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 + 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: @@ -1044,31 +1066,31 @@ SecAuditLogStorageDir logs/audit
<literal>SecDefaultAction</literal> - Description: Defines the default - action to take on a rule match. + Description: Defines the default action to + take on a rule match. - Syntax: Syntax: SecDefaultAction action1,action2,action3 - Example Usage: Example Usage: SecDefaultAction log,auditlog,deny,status:403,phase:2,t:lowercase - Processing Phase: Any + Processing Phase: Any - Scope: Any + Scope: Any - Dependencies/Notes: Rules - following a SecDefaultAction directive will inherit this setting unless - a specific action is specified for an indivdual rule or until another + Dependencies/Notes: Rules following a + SecDefaultAction directive will inherit this setting unless a specific + action is specified for an indivdual rule or until another SecDefaultAction is specified. The default value is: SecDefaultAction log,auditlog,deny,status:403,phase:2,t:none - Note + Note SecDefaultAction must specify a disruptive action and a processing phase. @@ -1077,45 +1099,45 @@ SecAuditLogStorageDir logs/audit
<literal>SecGeoLookupsDb</literal> - Description: Defines the path to - the geograpical database file. + Description: Defines the path to the + geograpical database file. - Syntax: Syntax: SecGeoLookupsDb /path/to/db - Example Usage: Example Usage: SecGeoLookupsDb /usr/local/geo/data/GeoLiteCity.dat - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: Check out - www.maxmind.com for free database files. + Dependencies/Notes: Check out www.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. + Description: Configuration directive to use + the httpd-guardian script to monitor for Denial of Service (DoS) + attacks. - Syntax: Syntax: SecGuardianLog |/path/to/httpd-guardian - Example Usage: Example Usage: SecGuardianLog |/usr/local/apache/bin/httpd-guardian - Processing Phase: N/A + Processing Phase: N/A - Scope: Main + Scope: Main - Dependencies/Notes: By default - httpd-guardian will defend against clients that send more 120 requests - in a minute, or more than 360 requests in five minutes. + Dependencies/Notes: By default httpd-guardian + will defend against clients that send more 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 @@ -1143,23 +1165,22 @@ SecAuditLogStorageDir logs/audit
<literal>SecPdfProtect</literal> (Experimental) - 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. + 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.
<literal>SecPdfProtectMethod</literal> (Experimental) - Description: Configure desired - protection method to be used when requests for PDF files are detected. - Possible values are TokenRedirection and + 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 @@ -1168,71 +1189,67 @@ SecAuditLogStorageDir logs/audit download is considered to be more secure but may cause usability problems for users ("This PDF won't open anymore!"). - Default: + Default: TokenRedirection
<literal>SecPdfProtectSecret</literal> (Experimental) - 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 as it will break the the - tokens that were sent prior to change. But it's not a big deal even if - you change it. It will just force dowload of PDF files with tokens that - were issued in the last few seconds. + 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 as it will break the the tokens that + were sent prior to change. But it's not a big deal even if you change + it. It will just force dowload of PDF files with tokens that were issued + in the last few seconds.
<literal>SecPdfProtectTimeout</literal> (Experimental) - 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. + 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. - Default: - 10 + Default: 10
<literal>SecPdfProtectTokenName</literal> (Experimental) - 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. + 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. - Default: - PDFTOKEN + Default: PDFTOKEN
<literal>SecRequestBodyAccess</literal> - Description: Configures whether - request bodies will be buffered and processed by ModSecurity by - default. + Description: Configures whether request + bodies will be buffered and processed by ModSecurity by default. - Syntax: Syntax: SecRequestBodyAccess On|Off - Example Usage: Example Usage: SecRequestBodyAccess On - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: This - directive is required if you plan to inspect POST_PAYLOADS of requests. - 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_PAYLOADS of requests. 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: @@ -1252,43 +1269,43 @@ SecAuditLogStorageDir logs/audit
<literal>SecRequestBodyLimit</literal> - Description: Configures the - maximum request body size ModSecurity will accept for buffering. + Description: Configures the maximum request + body size ModSecurity will accept for buffering. - Syntax: Syntax: SecRequestBodyLimit NUMBER_IN_BYTES - Example Usage: Example Usage: SecRequestBodyLimit 134217728 - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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>SecRequestBodyInMemoryLimit</literal> - Description: Configures the - maximum request body size ModSecurity will store in memory. + Description: Configures the maximum request + body size ModSecurity will store in memory. - Syntax: Syntax: SecRequestBodyInMemoryLimit NUMBER_IN_BYTES - Example Usage: Example Usage: SecRequestBodyInMemoryLimit 131072 - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: None + Dependencies/Notes: None By default the limit is 128 KB: @@ -1299,22 +1316,22 @@ SecRequestBodyInMemoryLimit 131072
<literal>SecResponseBodyLimit</literal> - Description: Configures the - maximum response body size that will be accepted for buffering. + Description: Configures the maximum response + body size that will be accepted for buffering. - Syntax: Syntax: SecResponseBodyLimit NUMBER_IN_BYTES - Example Usage: Example Usage: SecResponseBodyLimit 524228 - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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 + 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: @@ -1326,8 +1343,8 @@ SecResponseBodyLimit 524288
<literal>SecResponseBodyLimitAction</literal> - Description: Controls what - happens once a response body limit, configured with + Description: Controls what happens once a + response body limit, configured with SecResponseBodyLimit, is encountered. By default ModSecurity wil reject a response body that is longer than specified. Some web sites, however, will produce very long responses making it @@ -1345,40 +1362,38 @@ SecResponseBodyLimit 524288 before it is sent back, and therefore bypass any monitoring device. - Syntax: - SecResponseBodyLimitAction + Syntax: SecResponseBodyLimitAction Reject|ProcessPartial - Example Usage: + Example Usage: SecResponseBodyLimitAction ProcessPartial - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any
<literal>SecResponseBodyMimeType</literal> - Description: Configures - which MIME types are to be considered - for response body buffering. + Description: Configures which MIME types are to be considered for response + body buffering. - Syntax: Syntax: SecResponseBodyMimeType mime/type - Example Usage: Example Usage: SecResponseBodyMimeType text/plain text/html - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: - Multiple SecResponseBodyMimeType - directives can be used to add MIME - types. + Dependencies/Notes: Multiple SecResponseBodyMimeType directives can be + used to add MIME types. The default value is text/plaintext/html: @@ -1389,45 +1404,45 @@ SecResponseBodyLimit 524288
<literal>SecResponseBodyMimeTypesClear</literal> - Description: Clears the list of - MIME types considered for response - body buffering, allowing you to start populating the list from + Description: Clears the list of MIME types considered for response body + buffering, allowing you to start populating the list from scratch. - Syntax: Syntax: SecResponseBodyMimeTypesClear - Example Usage: Example Usage: SecResponseBodyMimeTypesClear - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: None + Dependencies/Notes: None
<literal>SecResponseBodyAccess</literal> - Description: Configures whether - response bodies are to be buffer and analysed or not. + Description: Configures whether response + bodies are to be buffer and analysed or not. - Syntax: Syntax: SecResponseBodyAccess On|Off - Example Usage: Example Usage: SecResponseBodyAccess On - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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: @@ -1447,21 +1462,21 @@ SecResponseBodyLimit 524288
<literal>SecRule</literal> - Description: 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] + Syntax: SecRule + VARIABLES OPERATOR [ACTIONS] - Example Usage: Example Usage: SecRule REQUEST_URI "attack" - Processing Phase: Any + Processing Phase: Any - Scope: Any + Scope: Any - Dependencies/Notes: None + Dependencies/Notes: None In general, the format of this rule is as follows: @@ -1544,30 +1559,29 @@ SecResponseBodyLimit 524288
<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 + 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: Syntax: SecRuleInheritance On|Off - Example Usage: Example Usage: SecRuleInheritance Off - Processing Phase: Any + Processing Phase: Any - Scope: Any + Scope: Any - 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. + 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 @@ -1581,7 +1595,7 @@ SecDefaultAction log,pass,phase:2 <VirtualHost *:80> ServerName app1.com -ServerAlias www.app1.com +ServerAlias www.app1.com SecRuleInheritance Off SecDefaultAction log,deny,phase:1,redirect:http://www.site2.com ... @@ -1590,7 +1604,7 @@ SecDefaultAction log,deny,phase:1,redirect:http://www.site2.com <VirtualHost *:80> ServerName app2.com ServerAlias www.app2.com -SecRuleInheritance On SecRule ARGS "attack" +SecRuleInheritance On SecRule ARGS "attack" ... </VirtualHost> @@ -1612,22 +1626,22 @@ ServerAlias www.app2.com
<literal>SecRuleEngine</literal> - Description: Configures the rules + Description: Configures the rules engine. - Syntax: Syntax: SecRuleEngine On|Off|DetectionOnly - Example Usage: Example Usage: SecRuleEngine On - Processing Phase: Any + Processing Phase: Any - Scope: Any + Scope: Any - Dependencies/Notes: Thisdirective - can also be controled by the ctl action (ctl:ruleEngine=off) for per - rule processing. + Dependencies/Notes: Thisdirective can also be + controled by the ctl action (ctl:ruleEngine=off) for per rule + processing. Possible values are: @@ -1652,23 +1666,23 @@ ServerAlias www.app2.com
<literal>SecRuleRemoveById</literal> - Description: Removes matching - rules from the parent contexts. + Description: Removes matching rules from the + parent contexts. - Syntax: Syntax: SecRuleRemoveById RULEID - Example Usage: Example Usage: SecRuleRemoveByID 1 2 "9000-9010" - Processing Phase: Any + Processing Phase: Any - Scope: Any + Scope: Any - 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
@@ -1676,116 +1690,114 @@ ServerAlias www.app2.com
<literal>SecRuleRemoveByMsg</literal> - Description: Removes matching - rules from the parent contexts. + Description: Removes matching rules from the + parent contexts. - Syntax: Syntax: SecRuleRemoveByMsg REGEX - Example Usage: Example Usage: SecRuleRemoveByMsg "FAIL" - Processing Phase: Any + Processing Phase: Any - Scope: Any + Scope: Any - 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>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: Syntax: SecServerSignature "WEB SERVER SOFTWARE" - Example Usage: Example Usage: SecServerSignature "Netscape-Enterprise/6.0" - Processing Phase: N/A + Processing Phase: N/A - Scope: Main + Scope: Main - 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>SecTmpDir</literal> - Description: Configures the - directory where temporary files will be created. + Description: Configures the directory where + temporary files will be created. - Syntax: SecTmpDir /path/to/dir + Syntax: SecTmpDir + /path/to/dir - Example Usage: Example Usage: SecTmpDir /tmp - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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. + Description: Configures the directory where + intercepted files will be stored. - Syntax: SecUploadDir /path/to/dir + Syntax: SecUploadDir + /path/to/dir - Example Usage: Example Usage: SecUploadDir /tmp - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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>SecUploadKeepFiles</literal> - Description: Configures whether - or not the intercepted files will be kept after transaction is - processed. + Description: Configures whether or not the + intercepted files will be kept after transaction is processed. - Syntax: Syntax: SecUploadKeepFiles On|Off|RelevantOnly - Example Usage: Example Usage: SecUploadKeepFiles On - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - Dependencies/Notes: This - directive requires the storage directory to be defined (using Dependencies/Notes: This directive requires + the storage directory to be defined (using SecUploadDir). Possible values are: @@ -1812,30 +1824,30 @@ ServerAlias www.app2.com
<literal>SecWebAppId</literal> - Description: Creates a partition - on the server that belongs to one web application. + Description: Creates a partition on the + server that belongs to one web application. - Syntax: SecWebAppId "NAME" + Syntax: SecWebAppId + "NAME" - Example Usage: Example Usage: SecWebAppId "WebApp1" - Processing Phase: N/A + Processing Phase: N/A - Scope: Any + Scope: Any - 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. + 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 -SecWebAppId "App1" +SecWebAppId "App1" SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} ... @@ -1843,7 +1855,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} <VirtualHost *:80> ServerName app2.com -ServerAlias www.app2.com +ServerAlias www.app2.com SecWebAppId "App2" SecRule REQUEST_COOKIES:PHPSESSID !^$ chain,nolog,pass SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} @@ -1904,8 +1916,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} - ModSecurity Processing Phases - Diagram + ModSecurity Processing Phases Diagram Below is a diagram of the standard Apache Request Cycle. In the diagram, the 5 ModSecurity processing phases are @@ -1918,11 +1929,10 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} action either directly in the rule or in using the SecDefaultAction directive: - SecDefaultAction "log,pass,phase:2" -SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" + SecDefaultAction "log,pass,phase:2" +SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" - Note on Rule and Phases + Note on Rule and Phases 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 @@ -1944,7 +1954,7 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1 - Note + Note Rules in this phase can not leverage Apache scope directives (Directory, Location, LocationMatch, etc...) as the post-read-request @@ -2080,7 +2090,7 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1 SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" "chain,log,deny,phase:2" -SecRule ARGS_COMBINED_SIZE "@gt 25" +SecRule ARGS_COMBINED_SIZE "@gt 25"
@@ -2094,7 +2104,7 @@ SecRule ARGS_COMBINED_SIZE "@gt 25" SecRule REQUEST_FILENAME "/index.php" "chain,log,deny,status:403,phase:2" -SecRule ARGS_NAMES "!^(p|a)$" +SecRule ARGS_NAMES "!^(p|a)$"
@@ -2134,9 +2144,9 @@ SecRule ARGS_NAMES "!^(p|a)$" This variable holds the authentication method used to validate a user. Example: - SecRule AUTH_TYPE "basic" log,deny,status:403,phase:1,t:lowercase + SecRule AUTH_TYPE "basic" log,deny,status:403,phase:1,t:lowercase - Note + 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 @@ -2151,9 +2161,8 @@ SecRule ARGS_NAMES "!^(p|a)$" 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" + SecRule REQUEST_FILENAME "printenv" pass,setenv:tag=suspicious +SecRule ENV:tag "suspicious"
@@ -2163,7 +2172,7 @@ SecRule ENV:tag "suspicious" 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 + SecRule FILES "\.conf$" log,deny,status:403,phase:2
@@ -2172,7 +2181,7 @@ SecRule ENV:tag "suspicious" 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 + SecRule FILES_COMBINED_SIZE "@gt 1000" log,deny,status:403,phase:2
@@ -2182,7 +2191,7 @@ SecRule ENV:tag "suspicious" 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 + SecRule FILES_NAMES "^upfile$" log,deny,status:403,phase:2
@@ -2192,7 +2201,7 @@ SecRule ENV:tag "suspicious" 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 + SecRule FILES_SIZES "@gt 100" log,deny,status:403,phase:2
@@ -2203,7 +2212,7 @@ SecRule ENV:tag "suspicious" moreinfo="none">@inspectFile. Note: only available if files were extracted from the request body. Example: - SecRule FILES_TMPNAMES "@inspectFile /path/to/inspect_script.pl" + SecRule FILES_TMPNAMES "@inspectFile /path/to/inspect_script.pl"
@@ -2219,64 +2228,60 @@ SecRule ENV:tag "suspicious" - 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 teo - character continent that the country is located. EX: EU + COUNTRY_CONTINENT: The teo 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. + CITY: The city name. - POSTAL_CODE: The postal - code. + POSTAL_CODE: The postal code. - LATITUDE: The - latitude. + LATITUDE: The latitude. - LONGITUDE: The - longitude. + LONGITUDE: The longitude. - DMA_CODE: The metropoliton - area code. (US only) + DMA_CODE: The metropoliton 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 REMOTE_ADDR "@geoLookup" chain,drop,msg:'Non-UK IP address' SecRule GEO:COUNTRY_CODE "!@streq UK"
@@ -2307,7 +2312,7 @@ SecRule GEO:COUNTRY_CODE "!@streq UK" SecRule ARGS pattern chain,deny ... -SecRule MATCHED_VAR "further scrutiny" +SecRule MATCHED_VAR "further scrutiny"
@@ -2318,7 +2323,7 @@ SecRule MATCHED_VAR "further scrutiny"SecRule ARGS pattern setvar:tx.mymatch=%{MATCHED_VAR_NAME} ... -SecRule TX:MYMATCH "@eq ARGS:param" deny +SecRule TX:MYMATCH "@eq ARGS:param" deny
@@ -2328,7 +2333,7 @@ SecRule TX:MYMATCH "@eq ARGS:param" deny - SecRule MODSEC_BUILD "!@ge 02050102" skip:1 + SecRule MODSEC_BUILD "!@ge 02050102" skip:1 SecRule ARGS "@pm some key words" deny,status:500
@@ -2347,7 +2352,7 @@ SecRule ARGS "@pm some key words" deny,status:500 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.
+ MULTIPART_CRLF_LF_LINES.
@@ -2422,7 +2427,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" also pass additional data, known as extra path information, as part of the URL. Example: - SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)" + SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)"
@@ -2432,7 +2437,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" appending data after a question mark. Warning: Not URL-decoded. Example: - SecRule QUERY_STRING "attack" + SecRule QUERY_STRING "attack"
@@ -2441,7 +2446,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" This variable holds the IP address of the remote client. Example: - SecRule REMOTE_ADDR "^192\.168\.1\.101$" + SecRule REMOTE_ADDR "^192\.168\.1\.101$"
@@ -2453,7 +2458,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" known bad client hosts or network blocks, or conversely, to allow in authorized hosts. Example: - SecRule REMOTE_HOST "\.evil\.network\org$" + SecRule REMOTE_HOST "\.evil\.network\org$"
@@ -2465,7 +2470,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" 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 + SecRule REMOTE_PORT "@lt 1024" phase:1,log,pass,setenv:remote_port=privileged
@@ -2475,9 +2480,9 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" there are no password (basic|digest) access controls in place, then this variable will be empty. Example: - SecRule REMOTE_USER "admin" + SecRule REMOTE_USER "admin" - Note + Note This data will not be available in a proxy-mode deployment as the authentication is not local. @@ -2491,7 +2496,7 @@ SM %{MULTIPART_SEMICOLON_MISSING}'" MULTIPART, and XML. Example: - SecRule REQBODY_PROCESSOR "^XML$ chain + SecRule REQBODY_PROCESSOR "^XML$ chain SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
@@ -2506,7 +2511,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" Example: - SecRule REQBODY_PROCESSOR_ERROR "@eq 1" deny,phase:2 + SecRule REQBODY_PROCESSOR_ERROR "@eq 1" deny,phase:2 Your policies must have a rule to check @@ -2528,7 +2533,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" Empty, or contains the error message from the processor. Example: - SecRule REQBODY_PROCESSOR_ERROR_MSG "failed to parse" t:lowercase + SecRule REQBODY_PROCESSOR_ERROR_MSG "failed to parse" t:lowercase
@@ -2538,7 +2543,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" REQUEST_FILENAME (e.g. index.php). Warning: not urlDecoded. Example: - SecRule REQUEST_BASENAME "^login\.php$" + SecRule REQUEST_BASENAME "^login\.php$"
@@ -2549,9 +2554,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" the arguements is important (ARGS should be used in all other cases). Example: - SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" + SecRule REQUEST_BODY "^username=\w{25,}\&password=\w{25,}\&Submit\=login$" - Note + Note This variable is only available if the content type is application/x-www-form-urlencoded. @@ -2565,7 +2570,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" 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" + SecRule &REQUEST_COOKIES "@eq 0"
@@ -2575,7 +2580,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" headers. Example: the following rule will trigger if the JSESSIONID cookie is not present. - SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0" + SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0"
@@ -2584,7 +2589,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" 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$"
@@ -2596,12 +2601,12 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" example uses REQUEST_HEADERS as a collection and is applying the validateUrlEncoding operator against all headers. - SecRule REQUEST_HEADERS "@validateUrlEncoding" + SecRule REQUEST_HEADERS "@validateUrlEncoding" Example: the second example is targeting only the Host header. - SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \ + SecRule REQUEST_HEADERS:Host "^[\d\.]+$" \ "deny,log,status:400,msg:'Host header is a numeric IP address'"
@@ -2611,7 +2616,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable is a collection of the names of all of the Request Headers. Example: - SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \ + SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" \ "log,deny,status:403,t:lowercase,msg:'Proxy Server Used'"
@@ -2624,9 +2629,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" 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)$)" + SecRule REQUEST_LINE "!(^((?:(?:pos|ge)t|head))|http/(0\.9|1\.0|1\.1)$)" - Note + Note Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2640,9 +2645,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" Example: the following example will trigger if the Request Method is either CONNECT or TRACE. - SecRule REQUEST_METHOD "^((?:connect|trace))$" + SecRule REQUEST_METHOD "^((?:connect|trace))$" - Note + Note Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2655,9 +2660,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the Request Protocol Version information. Example: - SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" + SecRule REQUEST_PROTOCOL "!^http/(0\.9|1\.0|1\.1)$" - Note + Note Due to the default action transformation function lowercase, the regex strings should be in lowercase as well unless the t:none @@ -2673,7 +2678,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" does not include either the REQUEST_METHOD or the HTTP version info. Example: - SecRule REQUEST_URI "attack" + SecRule REQUEST_URI "attack"
@@ -2684,7 +2689,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" http://www.example.com/index.php?p=X). Warning: not urlDecoded. Example: - SecRule REQUEST_URI_RAW "http:/" + SecRule REQUEST_URI_RAW "http:/"
@@ -2693,7 +2698,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the data for the response payload. Example: - SecRule RESPONSE_BODY "ODBC Error Code" + SecRule RESPONSE_BODY "ODBC Error Code"
@@ -2725,10 +2730,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable is similar to the REQUEST_HEADERS variable and can be used in the same manner. Example: - SecRule RESPONSE_HEADERS:X-Cache "MISS" + SecRule RESPONSE_HEADERS:X-Cache "MISS" - Note + Note This variable may not have access to some headers when running in embedded-mode. Headers such as Server, Date, Connection and Content-Type @@ -2743,9 +2747,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable is a collection of the response header names. Example: - SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" + SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" - Note + Note Same limitations as RESPONSE_HEADERS with regards to access to some headers in embedded-mode. @@ -2757,7 +2761,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the HTTP Response Protocol information. Example: - SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9" + SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9"
@@ -2766,9 +2770,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the HTTP Response Status Code generated by Apache. Example: - SecRule RESPONSE_STATUS "^[45]" + SecRule RESPONSE_STATUS "^[45]" - Note + 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 @@ -2787,8 +2791,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" 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}" + SecRule &REQUEST_HEADERS:Host "@eq 0" "log,deny,setvar:tx.varname=%{rule.id}"
@@ -2797,9 +2800,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds just the local filename part of SCRIPT_FILENAME. Example: - SecRule SCRIPT_BASENAME "^login\.php$" + SecRule SCRIPT_BASENAME "^login\.php$" - Note + Note This variable is not available in proxy mode.
@@ -2810,9 +2813,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" 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$" + SecRule SCRIPT_FILENAME "^/usr/local/apache/cgi-bin/login\.php$" - Note + Note This variable is not available in proxy mode.
@@ -2823,9 +2826,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the groupid (numerical value) of the group owner of the script. Example: - SecRule SCRIPT_GID "!^46$" + SecRule SCRIPT_GID "!^46$" - Note + Note This variable is not available in proxy mode.
@@ -2836,9 +2839,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the group name of the group owner of the script. Example: - SecRule SCRIPT_GROUPNAME "!^apache$" + SecRule SCRIPT_GROUPNAME "!^apache$" - Note + Note This variable is not available in proxy mode.
@@ -2850,9 +2853,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" - 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)$" + SecRule SCRIPT_MODE "^(2|3|6|7)$" - Note + Note This variable is not available in proxy mode.
@@ -2864,9 +2867,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" the script. Example: the example rule below will trigger if the UID is not 46 (the Apache user).
- SecRule SCRIPT_UID "!^46$" + SecRule SCRIPT_UID "!^46$" - Note + Note This variable is not available in proxy mode.
@@ -2877,9 +2880,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable holds the username of the owner of the script. Example: - SecRule SCRIPT_USERNAME "!^apache$" + SecRule SCRIPT_USERNAME "!^apache$" - Note + Note This variable is not available in proxy mode.
@@ -2890,7 +2893,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable contains the IP address of the server. Example: - SecRule SERVER_ADDR "^192\.168\.1\.100$" + SecRule SERVER_ADDR "^192\.168\.1\.100$"
@@ -2899,9 +2902,9 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable contains the server's hostname or IP address. Example: - SecRule SERVER_NAME "hostname\.com$" + SecRule SERVER_NAME "hostname\.com$" - Note + Note This data is taken from the Host header submitted in the client request. @@ -2913,7 +2916,7 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd" This variable contains the local port that the web server is listening on. Example: - SecRule SERVER_PORT "^80$" + SecRule SERVER_PORT "^80$"
@@ -2928,10 +2931,9 @@ 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 SESSION:SCORE "@gt 50" "pass,log,setvar:session.blocked=1" -SecRule SESSION:BLOCKED "@eq 1" "log,deny,status:403" +SecRule REQUEST_URI "^/cgi-bin/finger$" "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"
@@ -2940,7 +2942,7 @@ SecRule SESSION:BLOCKED "@eq 1" "log,deny,statu This variable is the value set with setsid. Example: - SecRule SESSIONID !^$ chain,nolog,pass + SecRule SESSIONID !^$ chain,nolog,pass SecRule REQUEST_COOKIES:PHPSESSID !^$ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}
@@ -2951,7 +2953,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} 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}$" + SecRule TIME "^(([1](8|9))|([2](0|1|2|3))):\d{2}:\d{2}$"
@@ -2961,7 +2963,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} 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)$" + SecRule TIME_DAY "^(([1](0|1|2|3|4|5|6|7|8|9))|20)$"
@@ -2970,7 +2972,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} This variable holds the time in seconds since 1970. Example: - SecRule TIME_EPOCH "@gt 1000" + SecRule TIME_EPOCH "@gt 1000"
@@ -2979,7 +2981,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} 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))$" + SecRule TIME_HOUR "^(0|1|2|3|4|5|6|[1](8|9)|[2](0|1|2|3))$"
@@ -2988,7 +2990,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} 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)" + SecRule TIME_MIN "^(3|4|5)"
@@ -2998,7 +3000,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} would match if the month was either November (10) or December (11). - SecRule TIME_MON "^1" + SecRule TIME_MON "^1"
@@ -3007,7 +3009,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} This variable holds the current second count (0-59). Example: - SecRule TIME_SEC "@gt 30" + SecRule TIME_SEC "@gt 30"
@@ -3016,7 +3018,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} 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)$" + SecRule TIME_WDAY "^(0|6)$"
@@ -3025,7 +3027,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} This variable holds the current four-digit year data. Example: - SecRule TIME_YEAR "^2006$" + SecRule TIME_YEAR "^2006$"
@@ -3059,9 +3061,8 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} - SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" -SecRule TX:SCORE "@gt 20" deny,log + SecRule WEBSERVER_ERROR_LOG "does not exist" "phase:5,pass,setvar:tx.score=+5" +SecRule TX:SCORE "@gt 20" deny,log
@@ -3071,7 +3072,7 @@ SecRule TX:SCORE "@gt 20" deny,logsetuid. Example: SecAction setuid:%{REMOTE_USER},nolog -SecRule USERID "Admin" +SecRule USERID "Admin"
@@ -3081,7 +3082,7 @@ SecRule USERID "Admin" moreinfo="none">SecWebAppId. Example: SecWebAppId "WebApp1" -SecRule WEBAPPID "WebApp1" "chain,log,deny,status:403" +SecRule WEBAPPID "WebApp1" "chain,log,deny,status:403" SecRule REQUEST_HEADERS:Transfer-Encoding "!^$"
@@ -3091,7 +3092,7 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" 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" + SecRule WEBSERVER_ERROR_LOG "File does not exist" "phase:5,setvar:tx.score=+5"
@@ -3104,11 +3105,10 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" 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$" skip:2 -SecRule XML:/employees/employee/name/text() Fred -SecRule XML:/xq:employees/employee/name/text() Fred \ + phase:1,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML +SecRule REQBODY_PROCESSOR "!^XML$" skip:2 +SecRule XML:/employees/employee/name/text() Fred +SecRule XML:/xq:employees/employee/name/text() Fred \ xmlns:xq=http://www.example.com/employees The first XPath expression does not use namespaces. It would match @@ -3188,7 +3188,7 @@ SecRule XML:/xq:employees/employee/name/text() data is never altered. Transformation functions are used to transform a variable before testing it in a rule. - Note + Note The default transformation function setting is - lowercase, replaceNulls and compressWhitespace (in this order). @@ -3196,24 +3196,21 @@ SecRule XML:/xq:employees/employee/name/text() 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 - tranformation actions can be used in the same rule, for example the - following rule also ensures that an attacker does not use URL encoding + SecRule ARG:p "xp_cmdshell" "t:lowercase" + multiple tranformation 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" + SecRule ARG:p "xp_cmdshell" "t:urlDecode,t:lowercase" One can use the SetDefaultAction command to ensure the translation occurs for every rule until the next. Note that translation actions are additive, so if a rule explicitly list actions, the translation actions set by SetDefaultAction are still performed. - SecDefaultAction t:urlDecode,t:lowercase + SecDefaultAction t:urlDecode,t:lowercase The following transformation functions are supported: @@ -3477,17 +3474,16 @@ SecRule XML:/xq:employees/employee/name/text()
<literal>allow</literal> - Description: Stops processing on - a successful match and allows transaction to proceed. + Description: Stops processing on a successful + match and allows transaction to proceed. - Action Group: Disruptive + Action Group: Disruptive Example: - SecRule REMOTE_ADDR "^192\.168\.1\.100$" nolog,phase:1,allow + SecRule REMOTE_ADDR "^192\.168\.1\.100$" nolog,phase:1,allow - Note + Note The allow action only applies to the current processing phase. If your intent is to explicitly allow a request, then you should use the @@ -3498,40 +3494,35 @@ SecRule XML:/xq:employees/employee/name/text()
append (Experimental) - 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 + Action Group: Non-Disruptive - Processing Phases: 3 and - 4. + Processing Phases: 3 and 4. Example: - SecRule RESPONSE_CONTENT_TYPE "^text/html" "nolog,pass,append:'<hr>Footer'" + 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 + Action Group: Non-Disruptive Example: - SecRule REMOTE_ADDR "^192\.168\.1\.100$" auditlog,phase:1,allow + SecRule REMOTE_ADDR "^192\.168\.1\.100$" auditlog,phase:1,allow - Note + Note The auditlog action is now explicit if log is already specified. @@ -3540,23 +3531,20 @@ SecRule XML:/xq:employees/employee/name/text()
<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 + Action Group: Non-Disruptive Example: - SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain + SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain SecRule TX:1 "(?:(?:a(dmin|nonymous)))" - Note + Note The 0 data captures the entire REGEX match and 1 captures the data in the first parantheses, etc... @@ -3565,23 +3553,23 @@ SecRule TX:1 "(?:(?:a(dmin|nonymous)))"
<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 + 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 + Action Group: Flow Example: # Refuse to accept POST requests that do # not specify request body length -SecRule REQUEST_METHOD ^POST$ chain +SecRule REQUEST_METHOD ^POST$ chain SecRule REQUEST_HEADER:Content-Length ^$ - Note + Note In programming language concepts, think of chained rules somewhat similar to AND conditional statements. The actions specified in the @@ -3596,18 +3584,17 @@ SecRule REQUEST_HEADER:Content-Length ^$
<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 + 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 +SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requestBodyProcessor=XML - Note + Note The following configuration options are supported: @@ -3681,32 +3668,30 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requ
<literal>deny</literal> - Description: Stops rule - processing and intercepts transaction. + Description: Stops rule processing and + intercepts transaction. - Action Group: Disruptive + Action Group: Disruptive Example: - SecRule REQUEST_HEADERS:User-Agent "nikto" "log,deny,msg:'Nikto Scanners Identified'" + 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 + Action Group: Non-Disruptive Example: The following example will decrement the counter by 60 every 300 seconds. SecAction deprecatevar:session.score=60/300 - Note + Note Counter values are always positive, meaning the value will never go below zero. @@ -3715,11 +3700,11 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requ
<literal>drop</literal> - Description: Immediately initiate - a "connection close" action to tear down the TCP connection by sending a + Description: Immediately initiate a + "connection close" action to tear down the TCP connection by sending a FIN packet. - Action Group: Disruptive + Action Group: Disruptive Example: The following example initiates an IP collection for tracking Basic Authentication attempts. If the client goes over the @@ -3730,9 +3715,9 @@ SecRule REQUEST_CONTENT_TYPE ^text/xml nolog,pass,ctl:requ 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" + log,drop,phase:1,msg:'Possible Brute Force Attack" - Note + Note This action is extremely useful when responding to both Brute Force and Denial of Service attacks in that, in both cases, you want to @@ -3744,18 +3729,17 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \
<literal>exec</literal> - Description: Executes an external + Description: Executes an external script/binary supplied as parameter. - Action Group: - Non-Disruptive + Action Group: Non-Disruptive Example: SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ - "log,exec:/usr/local/apache/bin/test.sh,phase:1" + "log,exec:/usr/local/apache/bin/test.sh,phase:1" - Note + Note This directive does not effect a primary action if it exists. This action will always call script with no parameters, but providing all @@ -3772,20 +3756,19 @@ SecRule IP:AUTH_ATTEMPT "@gt 25" \
<literal>expirevar</literal> - Description: Configurescollection - variable to expire after the given time in seconds. + Description: Configurescollection variable to + expire after the given time in seconds. - Action Group: - Non-Disruptive + 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" \ - "log,allow,setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" + "log,allow,setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1" - Note + Note You should use expirevar actions at the same time that you use setvar actions in order to keep the indended expiration time. If they @@ -3799,17 +3782,17 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
<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: Metadata + Action Group: Metadata Example: SecRule &REQUEST_HEADERS:Host "@eq 0" \ - "log,id:60008,severity:2,msg:'Request Missing a Host Header'" + "log,id:60008,severity:2,msg:'Request Missing a Host Header'" - Note + Note These are the reserved ranges: @@ -3864,19 +3847,18 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
<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 + Action Group: Non-Disruptive Example: The following example initiates IP address tracking. - SecAction initcol:ip=%{REMOTE_ADDR},nolog + SecAction initcol:ip=%{REMOTE_ADDR},nolog - Note + Note Every collection contains several built-in variables that are read-only: @@ -3949,18 +3931,16 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
<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 + Action Group: Non-Disruptive Example: - SecAction initcol:ip=%{REMOTE_ADDR},log + SecAction initcol:ip=%{REMOTE_ADDR},log - Note + Note This action will log matches to the Apache error log file and the ModSecurity audit log. @@ -3969,17 +3949,16 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
<literal>logdata</literal> - Description: Allows logging a - data fragment. + Description: Allows logging a data + fragment. - Action Group: Metadata + Action Group: Metadata Example: - SecRule &ARGS:p "@eq 0" "log,logdata:'%{TX.0}'" + SecRule &ARGS:p "@eq 0" "log,logdata:'%{TX.0}'" - Note + 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 @@ -3991,18 +3970,17 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \
<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: Metadata + Action Group: Metadata Example: SecRule &REQUEST_HEADERS:Host "@eq 0" \ - "log,id:60008,severity:2,msg:'Request Missing a Host Header'" + "log,id:60008,severity:2,msg:'Request Missing a Host Header'" - Note + Note The msg information appears in the error and/or audit log files and is not sent back to the client in response headers. @@ -4011,19 +3989,18 @@ 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 + Action Group: Non-Disruptive Example: SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase -SecRule ARGS "attack" multiMatch +SecRule ARGS "attack" multiMatch - Note + Note Normally, variables are evaluated once, only after all transformation functions have completed. With multiMatch, variables are @@ -4034,19 +4011,17 @@ SecRule ARGS "attack" multiMatch <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 + Action Group: Non-Disruptive Example: - SecRule REQUEST_HEADERS:User-Agent "Test" allow,noauditlog + SecRule REQUEST_HEADERS:User-Agent "Test" allow,noauditlog - Note + 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 @@ -4060,18 +4035,16 @@ SecRule ARGS "attack" multiMatch <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 + Action Group: Non-Disruptive Example: - SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog + SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog - Note + Note The nolog action also implies noauditlog.
@@ -4079,17 +4052,16 @@ SecRule ARGS "attack" multiMatch <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 + Action Group: Disruptive Example: - SecRule REQUEST_HEADERS:User-Agent "Test" log,pass + SecRule REQUEST_HEADERS:User-Agent "Test" log,pass - Note + Note Transaction will not be interrupted but it will be logged (unless logging has been suppressed). @@ -4098,17 +4070,16 @@ SecRule ARGS "attack" multiMatch <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: Disruptive + Action Group: Disruptive Example: - SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403,pause:5000 + SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403,pause:5000 - Note + 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 @@ -4120,18 +4091,17 @@ SecRule ARGS "attack" multiMatch <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: Disruptive + Action Group: Disruptive Example: - SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase + SecDefaultAction log,deny,phase:1,t:removeNulls,t:lowercase SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403 - Note + 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 @@ -4143,40 +4113,35 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
prepend (Experimental) - 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 + Action Group: Non-Disruptive - Processing Phases: 3 and - 4. + Processing Phases: 3 and 4. Example: - SecRule RESPONSE_CONTENT_TYPE ^text/html "phase:3,nolog,pass,prepend:'Header<br>'" + 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 + Action Group: Disruptive Example: - SecRule REQUEST_HEADERS:User-Agent "Test" log,proxy:http://www.honeypothost.com/ + SecRule REQUEST_HEADERS:User-Agent "Test" log,proxy:http://www.honeypothost.com/ - Note + 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 @@ -4186,17 +4151,17 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<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 + Action Group: Disruptive Example: SecRule REQUEST_HEADERS:User-Agent "Test" \ - log,redirect:http://www.hostname.com/failed.html + log,redirect:http://www.hostname.com/failed.html - Note + Note If the status action is present and its value is acceptable (301, 302, 303, or 307) it will be used for @@ -4206,17 +4171,15 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<literal>rev</literal> - Description: Specifies rule - revision. + Description: Specifies rule revision. - Action Group: Metadata + Action Group: Metadata Example: - SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" + SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" - Note + Note This action is used in combination with the id action to allow the same rule ID to be used @@ -4227,19 +4190,17 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<literal>sanitiseArg</literal> - Description: Sanitises (replaces - each byte with an asterisk) a named request argument prior to audit + Description: Sanitises (replaces each byte + with an asterisk) a named request argument prior to audit logging. - Action Group: - Non-Disruptive + Action Group: Non-Disruptive Example: - SecAction nolog,phase:2,sanitiseArg:password + SecAction nolog,phase:2,sanitiseArg:password - Note + 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 @@ -4251,22 +4212,20 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<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 + 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. - SecRule ARGS_NAMES password nolog,pass,sanitiseMatched + SecRule ARGS_NAMES password nolog,pass,sanitiseMatched - Note + Note Same note as sanitiseArg.
@@ -4274,19 +4233,17 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<literal>sanitiseRequestHeader</literal> - Description: Sanitises a named - request header. + Description: Sanitises a named request + header. - Action Group: - Non-Disruptive + Action Group: Non-Disruptive Example: This will sanitise the data in the Authorization header. - SecAction log,phase:1,sanitiseRequestHeader:Authorization + SecAction log,phase:1,sanitiseRequestHeader:Authorization - Note + Note Same note as sanitiseArg.
@@ -4294,19 +4251,17 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<literal>sanitiseResponseHeader</literal> - Description: Sanitises a named - response header. + 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. - SecAction log,phase:3,sanitiseResponseHeader:Set-Cookie + SecAction log,phase:3,sanitiseResponseHeader:Set-Cookie - Note + Note Same note as sanitiseArg.
@@ -4314,17 +4269,16 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<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: Metadata + Action Group: Metadata Example: - SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" + SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:2,msg:'Restricted HTTP function'" - Note + Note The severity numbers follow the Syslog convention: @@ -4366,18 +4320,17 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<literal>setuid</literal> - Description: Special-purpose - action that initialises the USER + Description: Special-purpose action that + initialises the USER collection. - Action Group: - Non-Disruptive + Action Group: Non-Disruptive Example: - SecAction setuid:%{REMOTE_USER},nolog + SecAction setuid:%{REMOTE_USER},nolog - Note + Note After initialisation takes place the variable USERID will be available for use in the @@ -4387,20 +4340,19 @@ SecRule REQUEST_HEADERS:User-Agent "Test" log,deny,status:403
<literal>setsid</literal> - Description: - Special-purposeaction that initialises the SESSION collection. + Description: Special-purposeaction that + initialises the SESSION + collection. - Action Group: - Non-Disruptive + 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} +SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} - Note + Note On first invocation of this action the collection will be empty (not taking the pre-defined variables into account - see setsid:%{REQUEST_COOKIES.PHPSESSID} <literal>setenv</literal> - Description: Creates, removes, or - updates an environment variable. + Description: Creates, removes, or updates an + environment variable. - Action Group: - Non-Disruptive + Action Group: Non-Disruptive Examples: @@ -4433,7 +4384,7 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID}setenv:!name - Note + Note This action can be used to establish communication with other Apache modules. @@ -4442,11 +4393,10 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} <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 + Action Group: Non-Disruptive Examples: @@ -4468,16 +4418,14 @@ SecAction setsid:%{REQUEST_COOKIES.PHPSESSID} <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: - Non-Disruptive + Action Group: Non-Disruptive Example: - SecRule REQUEST_URI "^/$" "chain,skip:2" + SecRule REQUEST_URI "^/$" "chain,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" \ @@ -4485,7 +4433,7 @@ SecRule &REQUEST_HEADERS:Host "@eq 0" \ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ "log,deny,log,status:400,id:960015,msg:'Request Missing an Accept Header'" - Note + Note Skip only applies to the current processing phase and not necessarily the order in which the rules appear in the configuration @@ -4498,18 +4446,17 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \
<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: Disruptive + Action Group: Disruptive Example: - SecDefaultAction log,deny,status:403,phase:1 + SecDefaultAction log,deny,status:403,phase:1 - Note + Note Staus actions defined in Apache scope locations (such as Directory, Location, etc...) may be superceded by phase:1 action @@ -4522,21 +4469,20 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \
<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 + 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 + log,deny,status:403,t:md5 - Note + Note Any transformation functions that you specify in a SecRule will be in addtion to previous ones specified in SecDefaultAction. Use of @@ -4547,17 +4493,17 @@ SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \
<literal>tag</literal> - Description: Assigns custom text - to a rule or chain. + Description: Assigns custom text to a rule or + chain. - Action Group: Metadata + Action Group: Metadata 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'" + "deny,msg:'System Command Access',id:'950002',tag:'WEB_ATTACK/FILE_INJECTION',tag:'OWASP/A2',severity:'2'" - Note + 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 @@ -4568,18 +4514,15 @@ SecRule REQUEST_COOKIES:SESSIONID "47414e81cbbef3cf8366e84eeacba091" \
<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: - Non-Disruptive + Action Group: Non-Disruptive 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
@@ -4594,81 +4537,75 @@ SecRule XML:/soap:Envelope/soap:Body/q1:getInput/id() "123" phase:2,deny <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 + 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_LINE "!@beginsWith GET" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)\.\d+$" deny,status:403,capture,chain -SecRule ARGS:gw "!@beginsWith %{TX.1}" +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_LINE "!@contains .php" t:none,deny,status:403 SecRule REQUEST_ADDR "^(.*)$" deny,status:403,capture,chain -SecRule ARGS:ip "!@contains %{TX.1}" +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 + 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" + 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" + 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. + 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 SecGeoLookupsDb before this operator can be @@ -4681,64 +4618,59 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}"
<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" + 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. + Description: Executes the external + script/binary given as parameter to the operator against every file + extracted from the request. Example: - SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspect_script.pl" + SecRule FILES_TMPNAMES "@inspectFile /opt/apache/bin/inspect_script.pl"
<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" + 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" + 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 + 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. @@ -4747,9 +4679,9 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}"
<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 + 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. @@ -4774,8 +4706,7 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" Example: - SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "deny,status:403 + 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 @@ -4786,29 +4717,27 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}"
<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" + 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" + SecRule REQUEST_HEADERS:User-Agent "@rx nikto" - Note + Note Regular expressions are handled by the PCRE library (http://www.pcre.org). ModSecurity @@ -4842,31 +4771,29 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}"
<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 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}" +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" + SecRule ARG:text "@validateByteRange 10, 13, 32-126" - Note + 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 @@ -4902,8 +4829,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: This operator requires the + request body to be processed as XML. Example: @@ -4911,14 +4838,14 @@ SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}@validateDTD /path/to/apache2/conf/xml.dtd" +SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd"
<literal>validateSchema</literal> - Description: This operator - requires the request body to be processed as XML. + Description: This operator requires the + request body to be processed as XML. Example: @@ -4926,7 +4853,7 @@ SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd@validateSchema /path/to/apache2/conf/xml.xsd" +SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" This operator requires request body to be processed as XML.
@@ -4934,15 +4861,14 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd
<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" + SecRule ARGS "@validateUrlEncoding" - Note + 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 @@ -4955,15 +4881,14 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd
<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" + SecRule ARGS "@validateUtf8Encoding" - Note + 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 @@ -4998,20 +4923,19 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd
<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 + 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 + 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 +SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:lowercase,deny,status:403
@@ -5114,4 +5038,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}
- + \ No newline at end of file From 1860e2a35e10b261e90137a113ffad012af40df4 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 26 Nov 2007 17:04:42 +0000 Subject: [PATCH 163/372] Renamed SecGeoLookupsDb to SecGeoLookupDB. --- CHANGES | 2 ++ apache2/apache2_config.c | 6 +++--- apache2/re_operators.c | 2 +- doc/modsecurity2-apache-reference.xml | 10 +++++----- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 1d0c74f4..82f66026 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ 26 Nov 2007 - 2.5.0-dev3 ------------------------ + * Renamed SecGeoLookupsDb to SecGeoLookupDB. + * Implement SecComponentSignature. * Fix warnings on Solaris and/or 64bit builds. diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 86970c3c..fc462304 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1273,7 +1273,7 @@ static const char *cmd_pdf_protect_method(cmd_parms *cmd, void *_dcfg, /* -- Geo Lookup configuration -- */ -static const char *cmd_geo_lookups_db(cmd_parms *cmd, void *_dcfg, +static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, const char *p1) { char *error_msg; @@ -1520,8 +1520,8 @@ const command_rec module_directives[] = { ), AP_INIT_TAKE1 ( - "SecGeoLookupsDb", - cmd_geo_lookups_db, + "SecGeoLookupDB", + cmd_geo_lookup_db, NULL, RSRC_CONF, "database for geographical lookups module." diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 95fdeaf5..1c1bd7f8 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -931,7 +931,7 @@ static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var int rc; if (geo == NULL) { - msr_log(msr, 1, "Geo lookup for \"%s\" attempted without a database. Set SecGeoLookupDb.", geo_host); + msr_log(msr, 1, "Geo lookup for \"%s\" attempted without a database. Set SecGeoLookupDB.", geo_host); return 0; } diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index ab0096db..f90d4a99 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1097,16 +1097,16 @@ SecAuditLogStorageDir logs/audit
- <literal>SecGeoLookupsDb</literal> + <literal>SecGeoLookupDb</literal> Description: Defines the path to the geograpical database file. Syntax: SecGeoLookupsDb /path/to/db + moreinfo="none">SecGeoLookupDb /path/to/db Example Usage: SecGeoLookupsDb + moreinfo="none">SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat Processing Phase: N/A @@ -4608,7 +4608,7 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" t:none,deny in the GEO collection. You must provide a database via SecGeoLookupsDb before this operator can be + moreinfo="none">SecGeoLookupDb before this operator can be used. See the GEO variable for an @@ -5038,4 +5038,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- \ No newline at end of file + From 9447ae67b8575ef5b2385178449f1ac2fded3560 Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 26 Nov 2007 22:27:15 +0000 Subject: [PATCH 164/372] Added placeholder support for skipAfter so that it works with removed rules. See #258. --- apache2/apache2_config.c | 44 +++++++++++++++++++++-- apache2/mod_security2.c | 4 +++ apache2/modsecurity.h | 1 + apache2/re.c | 25 +++++++++----- apache2/re.h | 4 +++ doc/modsecurity2-apache-reference.xml | 50 +++++++++++++++++++++------ 6 files changed, 105 insertions(+), 23 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index fc462304..ef2c42bf 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -27,7 +27,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { if (dcfg == NULL) return NULL; #ifdef DEBUG_CONF - fprintf(stderr, "Created directory config %p path %s\n", dcfg, path); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Created directory config %pp path %s", dcfg, path); #endif dcfg->mp = mp; @@ -75,6 +75,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { /* These are only used during the configuration process. */ dcfg->tmp_chain_starter = NULL; dcfg->tmp_default_actionset = NULL; + dcfg->tmp_rule_placeholders = NULL; /* Misc */ dcfg->data_dir = NOT_SET_P; @@ -195,7 +196,7 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { directory_config *merged = create_directory_config(mp, NULL); #ifdef DEBUG_CONF - fprintf(stderr, "Merge parent %p child %p RESULT %p\n", _parent, _child, merged); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Merge parent %pp child %pp RESULT %pp", _parent, _child, merged); #endif if (merged == NULL) return NULL; @@ -580,11 +581,48 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * dcfg->upload_validates_files = 1; } + /* Create skip table if one does not already exist. */ + if (dcfg->tmp_rule_placeholders == NULL) { + dcfg->tmp_rule_placeholders = apr_table_make(cmd->pool, 10); + if (dcfg->tmp_rule_placeholders == NULL) return FATAL_ERROR; + } + + /* Keep track of any rule IDs we need to skip after */ + if (rule->actionset->skip_after != NOT_SET_P) { + char *tmp_id = apr_pstrdup(cmd->pool, rule->actionset->skip_after); + apr_table_setn(dcfg->tmp_rule_placeholders, tmp_id, tmp_id); + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Watching for skipafter target rule id=\"%s\".", tmp_id); + #endif + + } + /* Add rule to the recipe. */ if (msre_ruleset_rule_add(dcfg->ruleset, rule, rule->actionset->phase) < 0) { return "Internal Error: Failed to add rule to the ruleset."; } + /* Add an additional placeholder if this rule ID is on the list */ + if ((rule->actionset->id != NULL) && apr_table_get(dcfg->tmp_rule_placeholders, rule->actionset->id)) { + msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule)); + + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Adding placeholder for rule id=\"%s\".", rule->actionset->id); + #endif + + /* shallow copy of original rule with placeholder marked as target */ + memcpy(phrule, rule, sizeof(msre_rule)); + phrule->placeholder = RULE_PH_TARGET; + + /* Add placeholder. */ + if (msre_ruleset_rule_add(dcfg->ruleset, phrule, phrule->actionset->phase) < 0) { + return "Internal Error: Failed to add placeholder to the ruleset."; + } + + apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); + } + return NULL; } @@ -1132,7 +1170,7 @@ static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, const cha msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re); #ifdef DEBUG_CONF - fprintf(stderr, "Added exception %p (%d %s) to dcfg %p.\n", re, re->type, re->param, dcfg); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg); #endif return NULL; diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 3ae85d8a..a82685e6 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -418,6 +418,10 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t void *init_flag = NULL; int first_time = 0; + /* ENH Figure out a way to validate config before we start + * - skipafter: need to make sure we found all of our targets + */ + /* Figure out if we are here for the first time */ apr_pool_userdata_get(&init_flag, "modsecurity-init-flag", s->process->pool); if (init_flag == NULL) { diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 135ff7cd..91ceb486 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -423,6 +423,7 @@ struct directory_config { /* Used only in the configuration phase. */ msre_rule *tmp_chain_starter; msre_actionset *tmp_default_actionset; + apr_table_t *tmp_rule_placeholders; /* Misc */ const char *data_dir; diff --git a/apache2/re.c b/apache2/re.c index 908a2bdf..82a802f0 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -673,27 +673,34 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) apr_time_t time1 = 0; #endif - // TODO: Still need to skip over placeholders - /* SKIP_RULES is used to skip all rules until we hit a placeholder * with the specified rule ID and then resume execution after that. */ if (mode == SKIP_RULES) { /* Go to the next rule if we have not yet hit the skip_after ID */ - // TODO: must be a placeholder as well - if ((rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { + if ((rule->placeholder != RULE_PH_NONE) && ((rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0))) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Skipping rule id=\"%s\" while looking for id=\"%s\"", (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); + msr_log(msr, 9, "Skipping rule id=\"%s\": Skipping until after id=\"%s\"", (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); + } continue; } - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Continuing execution after rule id=\"%s\"", skip_after); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Found rule id=\"%s\"%s.", skip_after, (rule->placeholder ? " placeholder" : "")); } - skip_after = NULL; - mode = NEXT_RULE; /* Go to the rule *after* this one to continue execution. */ + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Continuing execution after rule id=\"%s\".", skip_after); + } + + skip_after = NULL; + mode = NEXT_RULE; + continue; + } + + /* Skip any rule marked as a placeholder */ + if (rule->placeholder != RULE_PH_NONE) { continue; } diff --git a/apache2/re.h b/apache2/re.h index 9d9dc1f1..0be1a760 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -109,6 +109,9 @@ int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset #define RULE_NO_MATCH 0 #define RULE_MATCH 1 +#define RULE_PH_NONE 0 /* Not a placeholder */ +#define RULE_PH_TARGET 1 /* Placeholder for skipafter targets */ + struct msre_rule { apr_array_header_t *targets; const char *op_name; @@ -120,6 +123,7 @@ struct msre_rule { const char *p1; const char *filename; int line_num; + int placeholder; msre_ruleset *ruleset; msre_rule *chain_starter; diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index f90d4a99..95c932f8 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1939,7 +1939,7 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1"skip - action.
+ and skipAfter actions.
Phase Request Headers @@ -2333,8 +2333,8 @@ SecRule TX:MYMATCH "@eq ARGS:param" deny 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" skip:1 -SecRule ARGS "@pm some key words" deny,status:500 + SecRule MODSEC_BUILD "!@ge 02050102" skipAfter:12345 +SecRule ARGS "@pm some key words" id:12345,deny,status:500
@@ -3106,10 +3106,10 @@ SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" 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$" skip:2 +SecRule REQBODY_PROCESSOR "!^XML$" skipAfter:12345 SecRule XML:/employees/employee/name/text() Fred SecRule XML:/xq:employees/employee/name/text() Fred \ - xmlns:xq=http://www.example.com/employees + 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: @@ -3576,8 +3576,8 @@ SecRule REQUEST_HEADER:Content-Length ^$ 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) - and skip actions can only be specified on by the chain starter + disruptive actions, execution phases, metadata actions (id, rev, msg), + skip and skipAfter actions can only be specified on by the chain starter rule.
@@ -4443,6 +4443,34 @@ SecRule &REQUEST_HEADERS:Accept "@eq 0" \ skip.
+
+ <literal>skipAfter</literal> + + Description: Skips rules (or chains) on + successful match resuming rule execution after the specified rule id is + found. + + Action Group: Non-Disruptive + + Example: + + SecRule REQUEST_URI "^/$" "chain,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'" + + 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 paramater denoting the last rule ID to skip. +
+
<literal>status</literal> @@ -4837,8 +4865,8 @@ SecRule REQUEST_HEADERS:Ip-Address "!@streq %{TX.1}"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,skip:1 -SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd" +SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 +SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd,id:12345"
@@ -4852,8 +4880,8 @@ SecRule XML "@validateDTD /path/to/apache2/conf/xml.dtd"

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,skip:1 -SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd" +SecRule REQBODY_PROCESSOR "!^XML$" nolog,pass,skipAfter:12345 +SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd,id:12345" This operator requires request body to be processed as XML.
From e47fdeb4203ef5a187855992519951b1b8d67a5f Mon Sep 17 00:00:00 2001 From: brectanus Date: Mon, 26 Nov 2007 22:53:51 +0000 Subject: [PATCH 165/372] Changed %p formatter to APRs %pp (wish that was documented). Marked msr_log() as a printf style function so GNU compiler can check formatting types. Fixed a few other warnings with msr_log() formatters. --- apache2/apache2.h | 2 +- apache2/apache2_io.c | 6 +++--- apache2/apache2_util.c | 2 +- apache2/mod_security2.c | 12 ++++++------ apache2/modsecurity.h | 5 +++++ apache2/msc_geo.c | 2 +- apache2/msc_logging.c | 4 ++-- apache2/msc_multipart.c | 4 ++-- apache2/msc_reqbody.c | 2 +- apache2/re.c | 4 ++-- 10 files changed, 24 insertions(+), 19 deletions(-) diff --git a/apache2/apache2.h b/apache2/apache2.h index e9157a81..cb96bb35 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -82,7 +82,7 @@ char DSOLOCAL *get_env_var(request_rec *r, char *name); void DSOLOCAL internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, int level, const char *text, va_list ap); -void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...); +void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...) PRINTF_ATTRIBUTE(3,4); char DSOLOCAL *format_error_log_message(apr_pool_t *mp, error_message *em); diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index 7c773210..d7c68791 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -48,7 +48,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, if ((msr->if_status == IF_STATUS_COMPLETE)||(msr->if_status == IF_STATUS_NONE)) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %p, r %p).", f, f->r); + msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %pp, r %pp).", f, f->r); } ap_remove_input_filter(f); return ap_get_brigade(f->next, bb_out, mode, block, nbytes); @@ -56,7 +56,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Forwarding input: mode=%d, block=%d, nbytes=%" APR_OFF_T_FMT - " (f %p, r %p).", mode, block, nbytes, f, f->r); + " (f %pp, r %pp).", mode, block, nbytes, f, f->r); } if (msr->if_started_forwarding == 0) { @@ -483,7 +483,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { msr->r = r; if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Receiving output (f %p, r %p).", f, f->r); + msr_log(msr, 4, "Output filter: Receiving output (f %pp, r %pp).", f, f->r); } /* Initialise on first invocation */ diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 11a65014..072f39c9 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -246,7 +246,7 @@ void internal_log(request_rec *r, directory_config *dcfg, modsec_rec *msr, /* Construct the message. */ apr_vsnprintf(str1, sizeof(str1), text, ap); - apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%p][rid#%p][%s][%d] %s\n", + apr_snprintf(str2, sizeof(str2), "[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s\n", current_logtime(msr->mp), ap_get_server_name(r), (r->server), r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)), level, str1); diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index a82685e6..1553c962 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -348,7 +348,7 @@ static modsec_rec *create_tx_context(request_rec *r) { store_tx_context(msr, r); if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Transaction context created (dcfg %p).", msr->dcfg1); + msr_log(msr, 4, "Transaction context created (dcfg %pp).", msr->dcfg1); } return msr; @@ -629,7 +629,7 @@ static int hook_request_late(request_rec *r) { } if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Second phase starting (dcfg %p).", msr->dcfg2); + msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2); } /* Figure out whether or not to extract multipart files. */ @@ -899,7 +899,7 @@ 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 %p).", (((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); @@ -914,7 +914,7 @@ static void hook_insert_filter(request_rec *r) { /* We always add the PDF XSS protection filter. */ if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %p).", r); + msr_log(msr, 4, "Hook insert_filter: Adding PDF XSS protection output filter (r %pp).", r); } ap_add_output_filter("PDFP_OUT", msr, r, r->connection); @@ -934,7 +934,7 @@ static void hook_insert_filter(request_rec *r) { */ if (msr->of_status != OF_STATUS_COMPLETE) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %p).", r); + msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %pp).", r); } ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection); @@ -967,7 +967,7 @@ static void hook_insert_error_filter(request_rec *r) { */ if (msr->of_status != OF_STATUS_COMPLETE) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %p).", r); + msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %pp).", r); } /* Make a note that the output we will be receiving is a diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 91ceb486..8ff1ac55 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -30,6 +30,11 @@ typedef struct msc_string msc_string; #define DSOLOCAL #endif +/* For GNU C, tell the compiler to check printf like formatters */ +#if defined(__GNUC__) +#define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) +#endif + #include "msc_logging.h" #include "msc_multipart.h" #include "msc_pcre.h" diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 6693f05d..a2e9c5e4 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -316,7 +316,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro /* Why is this in host byte order? */ ipnum = ntohl(addr->sa.sin.sin_addr.s_addr); - msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08x).", targetip, ipnum); + msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08lx).", targetip, ipnum); for (level = 31; level >= 0; level--) { diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 3f79c2e0..aeee51c0 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -921,14 +921,14 @@ void sec_audit_logger(modsec_rec *msr) { nbytes = strlen(text); if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Audit Log: Writing %d bytes to primary concurrent index", nbytes); + msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to primary concurrent index", nbytes); } apr_file_write_full(msr->txcfg->auditlog_fd, text, nbytes, &nbytes_written); /* Write to the secondary audit log if we have one */ if (msr->txcfg->auditlog2_fd != NULL) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Audit Log: Writing %d bytes to secondary concurrent index", nbytes); + msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to secondary concurrent index", nbytes); } apr_file_write_full(msr->txcfg->auditlog2_fd, text, nbytes, &nbytes_written); } diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 22dfddea..ba8bd5e3 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -520,7 +520,7 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err *(multipart_part **)apr_array_push(msr->mpd->parts) = msr->mpd->mpp; if (msr->mpd->mpp->type == MULTIPART_FILE) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added file part %p to the list: name \"%s\" " + msr_log(msr, 9, "Multipart: Added file part %pp to the list: name \"%s\" " "file name \"%s\" (offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), log_escape(msr->mp, msr->mpd->mpp->filename), @@ -529,7 +529,7 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err } else { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Multipart: Added part %p to the list: name \"%s\" " + msr_log(msr, 9, "Multipart: Added part %pp to the list: name \"%s\" " "(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name), msr->mpd->mpp->offset, msr->mpd->mpp->length); } diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 2ef6d9ea..19db695a 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -170,7 +170,7 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, msr->msc_reqbody_chunks = NULL; apr_pool_clear(msr->msc_reqbody_mp); - msr_log(msr, 4, "Input filter: Wrote %" APR_SIZE_T_FMT " bytes from memory to disk.", disklen); + msr_log(msr, 4, "Input filter: Wrote %u bytes from memory to disk.", disklen); /* Continue with disk storage from now on */ return modsecurity_request_body_store_disk(msr, data, length, error_msg); diff --git a/apache2/re.c b/apache2/re.c index 82a802f0..1f5d60e0 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -782,7 +782,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) if (rule->actionset != NULL && rule->actionset->rev != NULL) { rev = apr_psprintf(p, " [rev \"%s\"]", rule->actionset->rev); } - msr_log(msr, 4, "Recipe: Invoking rule %p;%s%s%s.", + msr_log(msr, 4, "Recipe: Invoking rule %pp;%s%s%s.", rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : "")); } @@ -935,7 +935,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; - msr_log(msr, 1, "Rule %p [id \"%s\"][file \"%s\"][line \"%d\"]: %lu usec", rule, + msr_log(msr, 1, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"]: %lu usec", rule, ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-", rule->filename != NULL ? rule->filename : "-", rule->line_num, From 800cfc2cc2bd5c7a10df738005c969e3c490e2f8 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 27 Nov 2007 00:17:50 +0000 Subject: [PATCH 166/372] Added missing #else block for printf attributes. --- apache2/modsecurity.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 8ff1ac55..a3776823 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -33,6 +33,8 @@ typedef struct msc_string msc_string; /* For GNU C, tell the compiler to check printf like formatters */ #if defined(__GNUC__) #define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b))) +#else +#define PRINTF_ATTRIBUTE(a,b) #endif #include "msc_logging.h" From 4a08d7e6bfcb037a42de913a4e926761fdf3c80e Mon Sep 17 00:00:00 2001 From: ivanr Date: Tue, 27 Nov 2007 10:52:14 +0000 Subject: [PATCH 167/372] Handle out-of-disk-space conditions gracefully when writing to audit log. --- CHANGES | 3 +++ apache2/msc_logging.c | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 82f66026..c7b56f64 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ 26 Nov 2007 - 2.5.0-dev3 ------------------------ + * Enhance handling of the case where we run out of disk space while + writing to audit log entry. + * Renamed SecGeoLookupsDb to SecGeoLookupDB. * Implement SecComponentSignature. diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index aeee51c0..24727300 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -23,22 +23,39 @@ static int sec_auditlog_write(modsec_rec *msr, const char *data, unsigned int le apr_size_t nbytes_written, nbytes = len; apr_status_t rc; - if ((msr->new_auditlog_fd == NULL)||(data == NULL)) return -1; + /* Do nothing if there's no data. */ + if (data == NULL) return -1; - rc = apr_file_write_full(msr->new_auditlog_fd, data, nbytes, &nbytes_written); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed writing (requested %" APR_SIZE_T_FMT - " bytes, written %" APR_SIZE_T_FMT ")", nbytes, nbytes_written); - return -1; - } - - /* Note the following will only take into account the actual - * amount of bytes we've written. + /* Update size counters and the hash calculation. We always do this, + * even in cases where write fails. That will make it easier to detect + * problems with partial writes. */ msr->new_auditlog_size += nbytes_written; apr_md5_update(&msr->new_auditlog_md5ctx, data, nbytes_written); - return rc; + /* Do not write if we do not have a file descriptor. */ + if (msr->new_auditlog_fd == NULL) return -1; + + /* Write data to file. */ + rc = apr_file_write_full(msr->new_auditlog_fd, data, nbytes, &nbytes_written); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Audit log: Failed writing (requested %" APR_SIZE_T_FMT + " bytes, written %" APR_SIZE_T_FMT ")", nbytes, nbytes_written); + + /* Set to NULL to prevent more than one error message on + * out-of-disk-space events and to prevent further attempts + * to write to the same file in this request. + * + * Note that, as we opened the file throught the pool mechanism of + * the APR, we do not need to close the file here. It will be closed + * automatically at the end of the request. + */ + msr->new_auditlog_fd = NULL; + + return -1; + } + + return 1; } /** From 8cec4dd251a21654c90e36b1f81fdb711a9bb6d5 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 28 Nov 2007 01:04:26 +0000 Subject: [PATCH 168/372] Some more debugging and fixes for skipAfter. See #258. --- apache2/apache2_config.c | 32 +++++++++++++++++++++++++++++--- apache2/re.c | 8 ++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index ef2c42bf..300e3d9e 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -15,8 +15,6 @@ #include "pdf_protect.h" #include "http_log.h" -/* #define DEBUG_CONF 1 */ - /* -- Directory context creation and initialisation -- */ /** @@ -149,6 +147,10 @@ static void copy_rules_phase(apr_pool_t *mp, apr_array_header_t *parent_phase_ar } if (copy > 0) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, rule->actionset->id); + #endif + /* Copy the rule. */ *(msre_rule **)apr_array_push(child_phase_arr) = rule; if (rule->actionset->is_chained) mode = 2; @@ -157,6 +159,10 @@ static void copy_rules_phase(apr_pool_t *mp, apr_array_header_t *parent_phase_ar } } else { if (mode == 2) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy chain %pp for rule %pp [id \"%s\"]", rule, rule->chain_starter, rule->chain_starter->actionset->id); + #endif + /* Copy the rule (it belongs to the chain we want to include. */ *(msre_rule **)apr_array_push(child_phase_arr) = rule; } @@ -279,14 +285,26 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { if ((child->rule_inheritance == NOT_SET)||(child->rule_inheritance == 1)) { merged->rule_inheritance = parent->rule_inheritance; if ((child->ruleset == NULL)&&(parent->ruleset == NULL)) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "No rules in this context."); + #endif + /* Do nothing, there are no rules in either context. */ } else if (child->ruleset == NULL) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent rules in this context."); + #endif + /* Copy the rules from the parent context. */ merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); } else if (parent->ruleset == NULL) { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using child rules in this context."); + #endif + /* Copy child rules. */ merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp); merged->ruleset->phase_request_headers = apr_array_copy(mp, @@ -300,6 +318,10 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { merged->ruleset->phase_logging = apr_array_copy(mp, child->ruleset->phase_logging); } else { + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent then child rules in this context."); + #endif + /* Copy parent rules, then add child rules to it. */ merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); @@ -598,6 +620,10 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * } + #ifdef DEBUG_CONF + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Adding rule %pp id=\"%s\".", rule, (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id)); + #endif + /* Add rule to the recipe. */ if (msre_ruleset_rule_add(dcfg->ruleset, rule, rule->actionset->phase) < 0) { return "Internal Error: Failed to add rule to the ruleset."; @@ -608,7 +634,7 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule)); #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Adding placeholder for rule id=\"%s\".", rule->actionset->id); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id); #endif /* shallow copy of original rule with placeholder marked as target */ diff --git a/apache2/re.c b/apache2/re.c index 1f5d60e0..7def2fec 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -678,15 +678,15 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) */ if (mode == SKIP_RULES) { /* Go to the next rule if we have not yet hit the skip_after ID */ - if ((rule->placeholder != RULE_PH_NONE) && ((rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0))) { + if ((rule->placeholder == RULE_PH_NONE) || (rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Skipping rule id=\"%s\": Skipping until after id=\"%s\"", (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); + msr_log(msr, 9, "Skipping rule %pp id=\"%s\": Skipping until after id=\"%s\"", rule, (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); } continue; } if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Found rule id=\"%s\"%s.", skip_after, (rule->placeholder ? " placeholder" : "")); + msr_log(msr, 9, "Found rule %pp id=\"%s\".", rule, skip_after); } /* Go to the rule *after* this one to continue execution. */ @@ -838,7 +838,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) mode = SKIP_RULES; if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Skipping after rule id=\"%s\" -> mode SKIP_RULES.", skip_after); + msr_log(msr, 9, "Skipping after rule %pp id=\"%s\" -> mode SKIP_RULES.", rule, skip_after); } continue; From 1cfc906facb154559f964f694b08fa46eedea2ab Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 28 Nov 2007 01:09:15 +0000 Subject: [PATCH 169/372] Fixed apr_size_t formatting warnings by using portable %APR_SIZE_T_FMT instead of %lu. --- apache2/apache2_io.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index d7c68791..cd0b6c8b 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -104,7 +104,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, APR_BRIGADE_INSERT_TAIL(bb_out, bucket); if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Forwarded %lu bytes.", chunk->length); + msr_log(msr, 4, "Input filter: Forwarded %" APR_SIZE_T_FMT " bytes.", chunk->length); } } @@ -240,7 +240,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { modsecurity_request_body_end(msr, error_msg); if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Completed receiving request body (length %lu).", + msr_log(msr, 4, "Input filter: Completed receiving request body (length %" APR_SIZE_T_FMT ").", msr->reqbody_length); } @@ -444,7 +444,7 @@ static int flatten_response_body(modsec_rec *msr) { msr->resbody_data = apr_palloc(msr->mp, msr->resbody_length + 1); if (msr->resbody_data == NULL) { - msr_log(msr, 1, "Output filter: Response body data memory allocation failed. Asked for: %li", + msr_log(msr, 1, "Output filter: Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT, msr->resbody_length + 1); return -1; } @@ -702,7 +702,7 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Output filter: Completed receiving response body (buffered %s - %lu bytes).", + msr_log(msr, 4, "Output filter: Completed receiving response body (buffered %s - %" APR_SIZE_T_FMT " bytes).", (msr->of_partial ? "partial" : "full"), msr->resbody_length); } } else { /* Not looking at response data. */ From ab6a81fe7a6f0553ea40c1774f1402569a0c5cc0 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 10:46:12 +0000 Subject: [PATCH 170/372] Remove unused reqbody_status from modsec_rec. --- apache2/modsecurity.h | 1 - 1 file changed, 1 deletion(-) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index a3776823..35ed26e4 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -217,7 +217,6 @@ struct modsec_rec { unsigned int if_started_forwarding; apr_size_t reqbody_length; - unsigned int reqbody_status; apr_bucket_brigade *of_brigade; unsigned int of_status; From fd5e4fb32c28b8db20387f962a1d7c0d9c443a5e Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 11:09:38 +0000 Subject: [PATCH 171/372] Fix bugs introduced by the recent change to audit logging. --- apache2/msc_logging.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 24727300..20fe4339 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -30,8 +30,8 @@ static int sec_auditlog_write(modsec_rec *msr, const char *data, unsigned int le * even in cases where write fails. That will make it easier to detect * problems with partial writes. */ - msr->new_auditlog_size += nbytes_written; - apr_md5_update(&msr->new_auditlog_md5ctx, data, nbytes_written); + msr->new_auditlog_size += len; + apr_md5_update(&msr->new_auditlog_md5ctx, data, len); /* Do not write if we do not have a file descriptor. */ if (msr->new_auditlog_fd == NULL) return -1; From 575e86388a8baeeb58a1a20b80b8eab96002dc83 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 11:41:48 +0000 Subject: [PATCH 172/372] Implemented SecRequestBodyNoFilesLimit (#103). --- CHANGES | 4 +++- apache2/apache2_config.c | 30 ++++++++++++++++++++++++- apache2/apache2_io.c | 9 +++++++- apache2/modsecurity.h | 4 ++++ apache2/msc_multipart.c | 6 +++++ apache2/msc_reqbody.c | 25 +++++++++++++++++++-- doc/modsecurity2-apache-reference.xml | 32 +++++++++++++++++++++++++-- 7 files changed, 103 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index c7b56f64..cec15afd 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,9 @@ -26 Nov 2007 - 2.5.0-dev3 +29 Nov 2007 - 2.5.0-dev3 ------------------------ + * Implemented SecRequestBodyNoFilesLimit. + * Enhance handling of the case where we run out of disk space while writing to audit log entry. diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 300e3d9e..a62d88f7 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -34,6 +34,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->reqbody_access = NOT_SET; dcfg->reqbody_inmemory_limit = NOT_SET; dcfg->reqbody_limit = NOT_SET; + dcfg->reqbody_no_files_limit = NOT_SET; dcfg->resbody_access = NOT_SET; dcfg->debuglog_name = NOT_SET_P; @@ -221,6 +222,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { ? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit); merged->reqbody_limit = (child->reqbody_limit == NOT_SET ? parent->reqbody_limit : child->reqbody_limit); + merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET + ? parent->reqbody_no_files_limit : child->reqbody_no_files_limit); merged->resbody_access = (child->resbody_access == NOT_SET ? parent->resbody_access : child->resbody_access); @@ -453,6 +456,7 @@ void init_directory_config(directory_config *dcfg) { 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; + if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0; if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT; @@ -1022,6 +1026,22 @@ static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, const cha return NULL; } +static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + if (dcfg == NULL) return NULL; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyNoFilesLimit: %s", p1); + } + + dcfg->reqbody_no_files_limit = limit; + + return NULL; +} + static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1668,7 +1688,15 @@ const command_rec module_directives[] = { cmd_request_body_limit, NULL, CMD_SCOPE_ANY, - "maximum request body size ModSecurity is allowed to access." + "maximum request body size ModSecurity will accept." + ), + + AP_INIT_TAKE1 ( + "SecRequestBodyNoFilesLimit", + cmd_request_body_no_files_limit, + NULL, + CMD_SCOPE_ANY, + "maximum request body size ModSecurity will accept, but excluding the size of uploaded files." ), AP_INIT_TAKE1 ( diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index cd0b6c8b..2bb85e49 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -221,7 +221,14 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { } if (buflen != 0) { - if (modsecurity_request_body_store(msr, buf, buflen, error_msg) < 0) { + 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; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 35ed26e4..4d0b42bc 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -107,6 +107,7 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define REQUEST_BODY_HARD_LIMIT 1073741824L #define REQUEST_BODY_DEFAULT_INMEMORY_LIMIT 131072 #define REQUEST_BODY_DEFAULT_LIMIT 134217728 +#define REQUEST_BODY_NO_FILES_DEFAULT_LIMIT 1048576 #define RESPONSE_BODY_DEFAULT_LIMIT 524288 #define RESPONSE_BODY_HARD_LIMIT 1073741824L @@ -306,6 +307,8 @@ struct modsec_rec { int msc_reqbody_error; const char *msc_reqbody_error_msg; + apr_size_t msc_reqbody_no_files_length; + multipart_data *mpd; /* MULTIPART processor data structure */ #ifdef WITH_LIBXML2 @@ -366,6 +369,7 @@ struct directory_config { int reqbody_access; long int reqbody_inmemory_limit; long int reqbody_limit; + long int reqbody_no_files_limit; int resbody_access; long int of_limit; diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index ba8bd5e3..41a6475b 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -192,6 +192,9 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { } } + /* The buffer is data so increase the data length counter. */ + msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft); + if (len > 1) { if (msr->mpd->buf[len - 2] == '\r') { msr->mpd->flag_crlf_line = 1; @@ -422,6 +425,9 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { } else if (msr->mpd->mpp->type == MULTIPART_FORMDATA) { value_part_t *value_part = apr_pcalloc(msr->mp, sizeof(value_part_t)); + + /* The buffer contains data so increase the data length counter. */ + msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]; /* add this part to the list of parts */ diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index 19db695a..1f3126f6 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -25,7 +25,6 @@ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char ** msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 32, sizeof(msc_data_chunk *)); if (msr->msc_reqbody_chunks == NULL) { - *error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage."); return -1; } @@ -263,6 +262,11 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, char *my_error_msg = NULL; if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) { + /* The per-request data length counter will + * be updated by the multipart parser. + */ + + /* 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); msr->msc_reqbody_error = 1; @@ -273,6 +277,10 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, #ifdef WITH_LIBXML2 else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) { + /* Increase per-request data length counter. */ + msr->msc_reqbody_no_files_length += length; + + /* 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); msr->msc_reqbody_error = 1; @@ -283,7 +291,10 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, #endif else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) { - /* Do nothing, URLENCODED processor does not support streaming. */ + /* Increase per-request data length counter. */ + msr->msc_reqbody_no_files_length += length; + + /* Do nothing else, URLENCODED processor does not support streaming. */ } else { *error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s", @@ -291,6 +302,11 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, return -1; } } + + /* Check that we are not over the request body no files limit. */ + if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) { + return -5; + } /* Store data. */ if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { @@ -395,8 +411,10 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { } } + /* Note that we've read the body. */ msr->msc_reqbody_read = 1; + /* Finalise body processing. */ if ((msr->msc_reqbody_processor != NULL)&&(msr->msc_reqbody_error == 0)) { char *my_error_msg = NULL; @@ -432,6 +450,9 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { #endif } + /* Note the request body no files length. */ + msr_log(msr, 4, "Reqest body no files length: %lu", msr->msc_reqbody_no_files_length); + return 1; } diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 95c932f8..d210f087 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1278,8 +1278,6 @@ SecAuditLogStorageDir logs/audit Example Usage: SecRequestBodyLimit 134217728 - Processing Phase: N/A - Scope: Any Dependencies/Notes: 131072 KB (134217728 @@ -1287,6 +1285,36 @@ SecAuditLogStorageDir logs/audit 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 susceptability 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 + + 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> From d3a0a2887a191db253b328a719a472d630e9b7e2 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 13:30:39 +0000 Subject: [PATCH 173/372] Fix utf-8 validation (again\!\!\!). --- apache2/re_operators.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 1c1bd7f8..f52e7d72 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -1273,7 +1273,7 @@ static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, #define UNICODE_ERROR_DECODING_ERROR -5 /* NOTE: This is over-commented for ease of verification */ -static int detect_utf8_character(const char *p_read, unsigned int length) { +static int detect_utf8_character(const unsigned char *p_read, unsigned int length) { int unicode_len = 0; unsigned int d = 0; unsigned char c; @@ -1366,34 +1366,42 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule bytes_left = var->value_len; - for(i = 0; i < var->value_len; i++) { - int rc = detect_utf8_character(&var->value[i], bytes_left); + for(i = 0; i < var->value_len;) { + int rc = detect_utf8_character((unsigned char *)&var->value[i], bytes_left); + switch(rc) { case UNICODE_ERROR_CHARACTERS_MISSING : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: not enough bytes in " - "character."); + "character (pos %i).", i); return 1; break; case UNICODE_ERROR_INVALID_ENCODING : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: invalid byte value " - "in character."); + "in character (pos %i).", i); return 1; break; case UNICODE_ERROR_OVERLONG_CHARACTER : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: overlong " - "character detected."); + "character detected (pos %i).", i); return 1; break; case UNICODE_ERROR_RESTRICTED_CHARACTER : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: use of restricted character"); + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: use of restricted character" + " (pos %i).", i); return 1; break; case UNICODE_ERROR_DECODING_ERROR : - *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding"); + *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding (pos %i).", i); return 1; break; } + if (rc <= 0) { + *error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation."); + return 1; + } + + i += rc; bytes_left -= rc; } From c5c759d6f0761779b5866bc011f9f95c1174e5b4 Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 14:03:05 +0000 Subject: [PATCH 174/372] Forgot to update CHANGES. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index cec15afd..9965ff5e 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ + * Fixed utf-8 validation (again). + * Implemented SecRequestBodyNoFilesLimit. * Enhance handling of the case where we run out of disk space while From 526bcc0b5a7809fead3f72b562be4701031616cd Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 14:50:54 +0000 Subject: [PATCH 175/372] More informative change log message for fixing utf-8 validation. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 9965ff5e..f51156b8 100644 --- a/CHANGES +++ b/CHANGES @@ -2,7 +2,7 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ - * Fixed utf-8 validation (again). + * Fixed utf-8 validation (again). It was broken in 2.1.2. * Implemented SecRequestBodyNoFilesLimit. From ff12e6f1c0a397a31e5183e05c8d5b45711f843c Mon Sep 17 00:00:00 2001 From: ivanr Date: Thu, 29 Nov 2007 18:10:54 +0000 Subject: [PATCH 176/372] Remove comment that is not needed. --- CHANGES | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGES b/CHANGES index f51156b8..cec15afd 100644 --- a/CHANGES +++ b/CHANGES @@ -2,8 +2,6 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ - * Fixed utf-8 validation (again). It was broken in 2.1.2. - * Implemented SecRequestBodyNoFilesLimit. * Enhance handling of the case where we run out of disk space while From 85053718d93efa1f67b6f38d20d42e3dfaa5edc0 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 29 Nov 2007 23:14:02 +0000 Subject: [PATCH 177/372] Cleanup log output for skipAfter. See #258. --- apache2/re.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index 7def2fec..c2a98012 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -680,8 +680,14 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) /* Go to the next rule if we have not yet hit the skip_after ID */ if ((rule->placeholder == RULE_PH_NONE) || (rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Skipping rule %pp id=\"%s\": Skipping until after id=\"%s\"", rule, (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); + if (rule->chain_starter != NULL) { + msr_log(msr, 9, "Skipping chain rule %pp id=\"%s\" until after id=\"%s\"", rule, (rule->chain_starter->actionset->id ? rule->chain_starter->actionset->id : "(none)"), skip_after); + } + else { + msr_log(msr, 9, "Skipping rule %pp id=\"%s\" until after id=\"%s\"", rule, (rule->actionset->id ? rule->actionset->id : "(none)"), skip_after); + + } } continue; } From dcdce0cbc5fd0b30b1d88f1a00de7e13730f578c Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 30 Nov 2007 00:52:21 +0000 Subject: [PATCH 178/372] Added matching rules to audit log data. See #93. --- CHANGES | 2 ++ apache2/apache2_config.c | 2 +- apache2/modsecurity.c | 3 +++ apache2/modsecurity.h | 4 ++++ apache2/msc_logging.c | 9 +++++++++ apache2/re.c | 4 ++++ apache2/re.h | 1 + 7 files changed, 24 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cec15afd..7764ec22 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ + * Added matching rules to audit log data. + * Implemented SecRequestBodyNoFilesLimit. * Enhance handling of the case where we run out of disk space while diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index a62d88f7..3c98d7c3 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -659,7 +659,7 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * /* -- Configuration directives -- */ static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) { - return add_rule(cmd, (directory_config *)_dcfg, "REQUEST_URI", "@unconditionalMatch", p1); + return add_rule(cmd, (directory_config *)_dcfg, SECACTION_TARGETS, SECACTION_ARGS, p1); } static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, const char *p1) { diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index eec3eed0..cc543618 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -304,6 +304,9 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { 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; diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 4d0b42bc..6e36652b 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -114,6 +114,9 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define RESPONSE_BODY_LIMIT_ACTION_REJECT 0 #define RESPONSE_BODY_LIMIT_ACTION_PARTIAL 1 +#define SECACTION_TARGETS "REQUEST_URI" +#define SECACTION_ARGS "@unconditionalMatch" + #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) #include "unixd.h" #define __SET_MUTEX_PERMS @@ -333,6 +336,7 @@ struct modsec_rec { apr_time_t time_checkpoint_2; apr_time_t time_checkpoint_3; + apr_array_header_t *matched_rules; msc_string *matched_var; int highest_severity; diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 20fe4339..71e216c2 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -8,6 +8,7 @@ * write to Breach Security, Inc. at support@breach.com. * */ +#include "re.h" #include "msc_logging.h" #include "httpd.h" #include "apr_strings.h" @@ -362,6 +363,7 @@ void sec_audit_logger(modsec_rec *msr) { const apr_array_header_t *arr = NULL; apr_table_entry_t *te = NULL; char *str1 = NULL, *str2 = NULL, *text = NULL; + const msre_rule *rule = NULL; apr_size_t nbytes, nbytes_written; unsigned char md5hash[APR_MD5_DIGESTSIZE]; int was_limited = 0; @@ -732,6 +734,13 @@ void sec_audit_logger(modsec_rec *msr) { sec_auditlog_write(msr, text, strlen(text)); } + /* Matched Rules */ + for(i = 0; i < msr->matched_rules->nelts; i++) { + rule = ((msre_rule **)msr->matched_rules->elts)[i]; + text = apr_psprintf(msr->mp, "MatchedRule: %s\n", rule->unparsed); + sec_auditlog_write(msr, text, strlen(text)); + } + /* Apache error messages */ for(i = 0; i < msr->error_messages->nelts; i++) { error_message *em = (((error_message**)msr->error_messages->elts)[i]); diff --git a/apache2/re.c b/apache2/re.c index c2a98012..e84bb5d2 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1214,6 +1214,7 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, rule->ruleset = ruleset; rule->targets = apr_array_make(ruleset->mp, 10, sizeof(const msre_var *)); rule->p1 = apr_pstrdup(ruleset->mp, targets); + rule->unparsed = apr_pstrcat(ruleset->mp, ((strcmp(SECACTION_TARGETS, targets) || strcmp(SECACTION_TARGETS, args)) ? "SecRule" : "SecAction"), " ", targets, " ", args, " ", actions, NULL); rule->filename = apr_pstrdup(ruleset->mp, fn); rule->line_num = line; @@ -1424,6 +1425,9 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, log_escape(msr->mp, full_varname)); } + /* Save the rules that match */ + *(const msre_rule **)apr_array_push(msr->matched_rules) = rule; + /* Save the last matched var data */ msr->matched_var->name = apr_pstrdup(msr->mp, var->name); msr->matched_var->name_len = strlen(msr->matched_var->name); diff --git a/apache2/re.h b/apache2/re.h index 0be1a760..69687a6f 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -121,6 +121,7 @@ struct msre_rule { unsigned int op_negated; msre_actionset *actionset; const char *p1; + const char *unparsed; const char *filename; int line_num; int placeholder; From a6c2d867f40338401d8010e1a426bb7720897520 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 30 Nov 2007 21:31:12 +0000 Subject: [PATCH 179/372] Improvements to audit logging matching rules. See #93. --- CHANGES | 4 ++- apache2/msc_logging.c | 41 +++++++++++++++++---------- apache2/msc_logging.h | 4 ++- apache2/re.c | 10 ++++++- doc/modsecurity2-apache-reference.xml | 5 ++++ 5 files changed, 46 insertions(+), 18 deletions(-) diff --git a/CHANGES b/CHANGES index 7764ec22..397d7206 100644 --- a/CHANGES +++ b/CHANGES @@ -2,7 +2,9 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ - * Added matching rules to audit log data. + * The invoked rule is now logged in the debug log at level 5. + + * New audit log part 'K' logs all matching rules. * Implemented SecRequestBodyNoFilesLimit. diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 71e216c2..0110d61b 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -463,7 +463,7 @@ void sec_audit_logger(modsec_rec *msr) { /* AUDITLOG_PART_HEADER */ - text = apr_psprintf(msr->mp, "--%s-A--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_HEADER); sec_auditlog_write(msr, text, strlen(text)); /* Format: time transaction_id remote_addr remote_port local_addr local_port */ @@ -477,7 +477,7 @@ void sec_audit_logger(modsec_rec *msr) { /* AUDITLOG_PART_REQUEST_HEADERS */ if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_HEADERS) != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-B--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_REQUEST_HEADERS); sec_auditlog_write(msr, text, strlen(text)); sanitise_request_line(msr); @@ -581,7 +581,7 @@ void sec_audit_logger(modsec_rec *msr) { unsigned int sanitise_offset = 0; unsigned int sanitise_length = 0; - text = apr_psprintf(msr->mp, "\n--%s-C--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_REQUEST_BODY); sec_auditlog_write(msr, text, strlen(text)); for(;;) { @@ -669,7 +669,7 @@ void sec_audit_logger(modsec_rec *msr) { if (buffer == NULL) { msr_log(msr, 1, "Audit log: Failed to reconstruct request body."); } else { - text = apr_psprintf(msr->mp, "\n--%s-I--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_FAKE_REQUEST_BODY); sec_auditlog_write(msr, text, strlen(text)); sec_auditlog_write(msr, buffer, strlen(buffer)); } @@ -679,7 +679,7 @@ void sec_audit_logger(modsec_rec *msr) { /* AUDITLOG_PART_A_RESPONSE_HEADERS */ if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_A_RESPONSE_HEADERS) != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-F--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_A_RESPONSE_HEADERS); sec_auditlog_write(msr, text, strlen(text)); /* There are no response headers (or the status line) in HTTP 0.9 */ @@ -713,7 +713,7 @@ void sec_audit_logger(modsec_rec *msr) { if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_RESPONSE_BODY) != NULL) { if (msr->resbody_data != NULL) { - text = apr_psprintf(msr->mp, "\n--%s-E--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_RESPONSE_BODY); sec_auditlog_write(msr, text, strlen(text)); sec_auditlog_write(msr, msr->resbody_data, msr->resbody_length); wrote_response_body = 1; @@ -725,7 +725,7 @@ void sec_audit_logger(modsec_rec *msr) { if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_TRAILER) != NULL) { apr_time_t now = apr_time_now(); - text = apr_psprintf(msr->mp, "\n--%s-H--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_TRAILER); sec_auditlog_write(msr, text, strlen(text)); /* Messages */ @@ -734,13 +734,6 @@ void sec_audit_logger(modsec_rec *msr) { sec_auditlog_write(msr, text, strlen(text)); } - /* Matched Rules */ - for(i = 0; i < msr->matched_rules->nelts; i++) { - rule = ((msre_rule **)msr->matched_rules->elts)[i]; - text = apr_psprintf(msr->mp, "MatchedRule: %s\n", rule->unparsed); - sec_auditlog_write(msr, text, strlen(text)); - } - /* Apache error messages */ for(i = 0; i < msr->error_messages->nelts; i++) { error_message *em = (((error_message**)msr->error_messages->elts)[i]); @@ -880,10 +873,28 @@ void sec_audit_logger(modsec_rec *msr) { } } + /* AUDITLOG_PART_UPLOADS */ + // TODO: Implement + + + /* AUDITLOG_PART_MATCHEDRULES */ + + if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_MATCHEDRULES) != NULL) { + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_MATCHEDRULES); + sec_auditlog_write(msr, text, strlen(text)); + + /* Matched Rules */ + for(i = 0; i < msr->matched_rules->nelts; i++) { + rule = ((msre_rule **)msr->matched_rules->elts)[i]; + text = apr_psprintf(msr->mp, "%s\n", rule->unparsed); + sec_auditlog_write(msr, text, strlen(text)); + } + } + /* AUDITLOG_PART_ENDMARKER */ - text = apr_psprintf(msr->mp, "\n--%s-Z--\n", msr->new_auditlog_boundary); + text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_ENDMARKER); sec_auditlog_write(msr, text, strlen(text)); /* Return here if we were writing to a serial log diff --git a/apache2/msc_logging.h b/apache2/msc_logging.h index 6cfaa181..83496960 100644 --- a/apache2/msc_logging.h +++ b/apache2/msc_logging.h @@ -28,7 +28,9 @@ #define AUDITLOG_PART_A_RESPONSE_BODY 'G' #define AUDITLOG_PART_TRAILER 'H' #define AUDITLOG_PART_FAKE_REQUEST_BODY 'I' -#define AUDITLOG_PART_LAST 'I' +#define AUDITLOG_PART_UPLOADS 'J' +#define AUDITLOG_PART_MATCHEDRULES 'K' +#define AUDITLOG_PART_LAST 'K' #define AUDITLOG_PART_ENDMARKER 'Z' #include "modsecurity.h" diff --git a/apache2/re.c b/apache2/re.c index e84bb5d2..a8added5 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -790,6 +790,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) } msr_log(msr, 4, "Recipe: Invoking rule %pp;%s%s%s.", rule, (fn ? fn : ""), (id ? id : ""), (rev ? rev : "")); + msr_log(msr, 5, "Rule %pp: %s", rule, rule->unparsed); } #if defined(PERFORMANCE_MEASUREMENT) @@ -1214,10 +1215,17 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, rule->ruleset = ruleset; rule->targets = apr_array_make(ruleset->mp, 10, sizeof(const msre_var *)); rule->p1 = apr_pstrdup(ruleset->mp, targets); - rule->unparsed = apr_pstrcat(ruleset->mp, ((strcmp(SECACTION_TARGETS, targets) || strcmp(SECACTION_TARGETS, args)) ? "SecRule" : "SecAction"), " ", targets, " ", args, " ", actions, NULL); rule->filename = apr_pstrdup(ruleset->mp, fn); rule->line_num = line; + /* Add the unparsed rule */ + if ((strcmp(SECACTION_TARGETS, targets) == 0) && (strcmp(SECACTION_ARGS, args) == 0)) { + rule->unparsed = apr_pstrcat(ruleset->mp, "SecAction", " \"", actions, "\"", NULL); + } + else { + rule->unparsed = apr_pstrcat(ruleset->mp, "SecRule", " \"", targets, "\" \"", args, "\"", (actions != NULL ? " \"" : ""), (actions != NULL ? actions : ""), (actions != NULL ? "\"" : ""), NULL); + } + /* Parse targets */ rc = msre_parse_targets(ruleset, targets, rule->targets, &my_error_msg); if (rc < 0) { diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index d210f087..86121ac6 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -676,6 +676,11 @@ SecAuditLogStorageDir logs/audit 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. + + Z - final boundary, signifies the end of the entry (mandatory) From 13e209909f6cab4d6632f13ea6bdafd6ca0b058b Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 30 Nov 2007 23:26:06 +0000 Subject: [PATCH 180/372] Add in verifyCC operator from mod_security2_op_verifyCC.c. See #69. This still needs to be fixed. --- apache2/re_operators.c | 180 ++++++++++++++++++++++++++ doc/modsecurity2-apache-reference.xml | 17 +++ 2 files changed, 197 insertions(+) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index f52e7d72..30d6e338 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -916,6 +916,179 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre return 0; } +/* verifyCC */ + +static int validate_cc (modsec_rec *msr, const char *ccnumber, int len) { + char ccdigits[16]; + char *cdst = ccdigits; + const char *csrc = ccnumber; + int digits = 0; + int srclen = 0; + int multiply; + int digit; + int add; + int sum = 0; + + /* Remove non digits characters */ + + while ((srclen++ <= len) && (digits < 16)) { + if (isdigit(*csrc)) { + *(cdst++) = *csrc - 48; + ++digits; + } + csrc++; + + msr_log(msr, 9, "cdst[%d]: %.*s", digits, digits, ccdigits); + } + + /* Credit card checksum algorithm */ + + for (digit = digits - 1, multiply = 0; digit >= 0; digit--) { + if (multiply) { + add = ccdigits[digit] * 2; + } + else { + add = ccdigits[digit]; + } + + if (add > 9) { + sum += add - 9; + } + else { + sum += add; + } + + multiply = 1 - multiply; + } + + /* See if the final digit is a zero, if so the card is valid. */ + if ((sum % 10) != 0) { + return 0; + } else { + return 1; + } +} + +static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { + const char *errptr = NULL; + int erroffset; + msc_regex_t *regex; + const char *pattern = rule->op_param; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + /* Compile pattern */ + regex = msc_pregcomp(rule->ruleset->mp, pattern, 0, &errptr, &erroffset); + if (regex == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %i): %s", + erroffset, errptr); + return 0; + } + + rule->op_param_data = regex; + + return 1; /* OK */ +} + +static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; + const char *target; + unsigned int target_length; + char *my_error_msg = NULL; + int ovector[33]; + int rc; + int is_cc = 0; + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (regex == NULL) { + *error_msg = "Internal Error: regex data is null."; + return -1; + } + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg); + if (rc < -1) { + *error_msg = apr_psprintf(msr->mp, "Regex execution failed: %s", my_error_msg); + return -1; + } + + /* Handle captured subexpressions. */ + if (rc > 0) { + int capture = 0; + const apr_array_header_t *tarr; + const apr_table_entry_t *telts; + int i; + + /* Are we supposed to store the captured subexpressions? */ + /* IMP1 Can we use a flag to avoid having to iterate through the list every time. */ + tarr = apr_table_elts(rule->actionset->actions); + telts = (const apr_table_entry_t*)tarr->elts; + for (i = 0; i < tarr->nelts; i++) { + msre_action *action = (msre_action *)telts[i].val; + if (strcasecmp(action->metadata->name, "capture") == 0) { + capture = 1; + break; + } + } + + int k; + + /* Use the available captures. */ + /* to avoid memory manipulation I use the same captures for now to check for CC# */ + for(k = 0; k < rc; k++) { + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (s == NULL) return -1; + s->name = apr_psprintf(msr->mp, "%i", k); + s->value = apr_pstrmemdup(msr->mp, + target + ovector[2*k], ovector[2*k + 1] - ovector[2*k]); + s->value_len = (ovector[2*k + 1] - ovector[2*k]); + if ((s->name == NULL)||(s->value == NULL)) return -1; + apr_table_setn(msr->tx_vars, s->name, (void *)s); + + msr_log(msr, 9, "Adding regex subexpression to TXVARS (%i): %s", k, + log_escape_nq(msr->mp, s->value)); + + if ((k > 0) && validate_cc (msr, s->value, s->value_len)) { + is_cc = 1; + } + } + + /* Unset the remaining ones (from previous invocations). */ + for(k = rc; k <= 9; k++) { + char buf[24]; + apr_snprintf(buf, sizeof(buf), "%i", k); + apr_table_unset(msr->tx_vars, buf); + } + } + + if ((rc != PCRE_ERROR_NOMATCH) && is_cc) { + /* Match. */ + + /* This message will be logged. */ + *error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s.", + regex->pattern, var->name); + return 1; + } + + /* No match. */ + return 0; +} + + #endif /** @@ -1654,6 +1827,13 @@ void msre_engine_register_default_operators(msre_engine *engine) { #endif + /* verifyCC */ + msre_engine_op_register(engine, + "verifyCC", + msre_op_verifyCC_init, + msre_op_verifyCC_execute + ); + /* geoLookup */ msre_engine_op_register(engine, "geoLookup", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 86121ac6..c680a220 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4981,6 +4981,23 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd,
+
+ <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. + + Example: + + SecRule ARGS "@verifyCC \d{13,16}" \ + "phase:2,sanitiseMatched,log,auditlog,pass,msg:'Potential credit card number'" +
+ +
+
<literal>within</literal> From 9e9bb318b3e4c64774fb2912021d758a7c41f7f8 Mon Sep 17 00:00:00 2001 From: brectanus Date: Sat, 1 Dec 2007 00:42:28 +0000 Subject: [PATCH 181/372] Rewrite the luhn algorithm to be faster and easier to read. See #69. --- apache2/re_operators.c | 62 ++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 30d6e338..7467f3de 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -918,55 +918,47 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre /* verifyCC */ -static int validate_cc (modsec_rec *msr, const char *ccnumber, int len) { +/** + * Luhn Mod-10 Method (ISO 2894/ANSI 4.13) + */ +static int luhn_verify(const char *ccnumber, int len) { char ccdigits[16]; char *cdst = ccdigits; const char *csrc = ccnumber; int digits = 0; int srclen = 0; - int multiply; - int digit; - int add; int sum = 0; - - /* Remove non digits characters */ + int weight; + int i; + /* Weighted lookup table which is just a precalculated (i = index): + * i*2 + (( (i*2) > 9 ) ? -9 : 0) + */ + static int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; /* weight lookup table */ + + /* Remove non digits characters and calculate the true length */ while ((srclen++ <= len) && (digits < 16)) { if (isdigit(*csrc)) { - *(cdst++) = *csrc - 48; + *(cdst++) = *csrc; ++digits; } csrc++; - - msr_log(msr, 9, "cdst[%d]: %.*s", digits, digits, ccdigits); } + + /* Determine if the first digit is weighted: odd=no, even=yes */ + weight = !(digits & 1); - /* Credit card checksum algorithm */ - - for (digit = digits - 1, multiply = 0; digit >= 0; digit--) { - if (multiply) { - add = ccdigits[digit] * 2; - } - else { - add = ccdigits[digit]; - } - - if (add > 9) { - sum += add - 9; - } - else { - sum += add; - } - - multiply = 1 - multiply; - } - - /* See if the final digit is a zero, if so the card is valid. */ - if ((sum % 10) != 0) { - return 0; - } else { - return 1; + /* Add up digits (weighted digits via lookup table) */ + for (i = 0; i < digits; i++) { + sum += (weight ? wtable[ccdigits[i] - '0'] : (ccdigits[i] - '0')); + weight = !weight; /* alternate weights */ } + + /* Do a mod 10 on the sum */ + sum %= 10; + + /* If the result is a zero the card is valid. */ + return sum ? 0 : 1; } static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { @@ -1062,7 +1054,7 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * msr_log(msr, 9, "Adding regex subexpression to TXVARS (%i): %s", k, log_escape_nq(msr->mp, s->value)); - if ((k > 0) && validate_cc (msr, s->value, s->value_len)) { + if ((k > 0) && luhn_verify (s->value, s->value_len)) { is_cc = 1; } } From 2bf4556cd074313d762fa466d3c8ea63345f3ff2 Mon Sep 17 00:00:00 2001 From: brectanus Date: Sun, 2 Dec 2007 15:35:09 +0000 Subject: [PATCH 182/372] Checkin fix to rule removal code to avoid placeholders. --- apache2/re.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index a8added5..86a721dc 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1028,31 +1028,34 @@ static int msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, if (mode == 0) { /* Looking for next rule. */ int remove_rule = 0; - switch(re->type) { - case RULE_EXCEPTION_REMOVE_ID : - if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { - int ruleid = atoi(rule->actionset->id); + /* Only remove non-placeholder rules */ + if (rule->placeholder == RULE_PH_NONE) { + switch(re->type) { + case RULE_EXCEPTION_REMOVE_ID : + if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { + int ruleid = atoi(rule->actionset->id); - if (rule_id_in_range(ruleid, re->param)) { - remove_rule = 1; + if (rule_id_in_range(ruleid, re->param)) { + remove_rule = 1; + } } - } - break; + break; - case RULE_EXCEPTION_REMOVE_MSG : - if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { - char *my_error_msg = NULL; + case RULE_EXCEPTION_REMOVE_MSG : + if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) { + char *my_error_msg = NULL; - int rc = msc_regexec(re->param_data, - rule->actionset->msg, strlen(rule->actionset->msg), - &my_error_msg); - if (rc >= 0) { - remove_rule = 1; + int rc = msc_regexec(re->param_data, + rule->actionset->msg, strlen(rule->actionset->msg), + &my_error_msg); + if (rc >= 0) { + remove_rule = 1; + } } - } - break; + break; + } } if (remove_rule) { From 22873995f763249920726e412181304f95f45dbc Mon Sep 17 00:00:00 2001 From: brectanus Date: Sun, 2 Dec 2007 16:26:05 +0000 Subject: [PATCH 183/372] Rename placeholder type from RULE_PH_TARGET to RULE_PH_SKIPAFTER. --- apache2/apache2_config.c | 2 +- apache2/re.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 3c98d7c3..8a8b205c 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -643,7 +643,7 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * /* shallow copy of original rule with placeholder marked as target */ memcpy(phrule, rule, sizeof(msre_rule)); - phrule->placeholder = RULE_PH_TARGET; + phrule->placeholder = RULE_PH_SKIPAFTER; /* Add placeholder. */ if (msre_ruleset_rule_add(dcfg->ruleset, phrule, phrule->actionset->phase) < 0) { diff --git a/apache2/re.h b/apache2/re.h index 69687a6f..ed81a015 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -110,7 +110,7 @@ int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset #define RULE_MATCH 1 #define RULE_PH_NONE 0 /* Not a placeholder */ -#define RULE_PH_TARGET 1 /* Placeholder for skipafter targets */ +#define RULE_PH_SKIPAFTER 1 /* Placeholder for skipAfter targets */ struct msre_rule { apr_array_header_t *targets; From c25071b832a95aaf6006815921abd2037460cccf Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 3 Dec 2007 14:04:53 +0000 Subject: [PATCH 184/372] Initial experimental implementation of SecRequestEncoding. See #390 for more details. --- apache2/apache2_config.c | 27 ++++++++++++++ apache2/modsecurity.h | 3 ++ apache2/msc_multipart.c | 4 ++- apache2/msc_parsers.c | 77 +++++++++++++++++++++++++++++++++------- apache2/msc_parsers.h | 2 ++ 5 files changed, 100 insertions(+), 13 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 8a8b205c..b99d0f1b 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -101,6 +101,8 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *)); + dcfg->request_encoding = NOT_SET_P; + return dcfg; } @@ -438,6 +440,9 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { merged->component_signatures = apr_array_append(mp, parent->component_signatures, child->component_signatures); + merged->request_encoding = (child->request_encoding == NOT_SET_P + ? parent->request_encoding : child->request_encoding); + return merged; } @@ -517,6 +522,8 @@ void init_directory_config(directory_config *dcfg) { 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->request_encoding == NOT_SET_P) dcfg->request_encoding = NULL; } /** @@ -1055,6 +1062,18 @@ static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, const ch return NULL; } +static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, const char *p1) { + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + // TODO Validate encoding + // return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyAccess: %s", p1); + + dcfg->request_encoding = p1; + + return NULL; +} + static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; if (dcfg == NULL) return NULL; @@ -1699,6 +1718,14 @@ const command_rec module_directives[] = { "maximum request body size ModSecurity will accept, but excluding the size of uploaded files." ), + AP_INIT_TAKE1 ( + "SecRequestEncoding", + cmd_request_encoding, + NULL, + CMD_SCOPE_ANY, + "character encoding used in request." + ), + AP_INIT_TAKE1 ( "SecResponseBodyAccess", cmd_response_body_access, diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 6e36652b..715172ba 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -466,6 +466,9 @@ struct directory_config { * appear in the ModSecurity signature in the audit log. */ apr_array_header_t *component_signatures; + + /* Request character encoding. */ + const char *request_encoding; }; struct error_message { diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 41a6475b..4a9708db 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -12,6 +12,7 @@ #include "msc_multipart.h" #include "msc_util.h" +#include "msc_parsers.h" #if 0 @@ -1169,7 +1170,8 @@ int multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *argument arg->value_origin_len = parts[i]->length; arg->origin = origin; - apr_table_addn(arguments, arg->name, (void *)arg); + // apr_table_addn(arguments, arg->name, (void *)arg); + add_argument(msr, arguments, arg); } } diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index 69be93e4..e00f1bfd 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -9,7 +9,9 @@ * */ #include "msc_parsers.h" +#include "iconv.h" #include +#include /** * @@ -255,10 +257,8 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, arg->value_len = 0; arg->value = ""; - apr_table_addn(arguments, arg->name, (void *)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)); + // apr_table_addn(arguments, arg->name, (void *)arg); + add_argument(msr, arguments, arg); arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); arg->origin = origin; @@ -274,10 +274,8 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, arg->value_len = urldecode_nonstrict_inplace_ex((unsigned char *)value, arg->value_origin_len, invalid_count); arg->value = apr_pstrmemdup(msr->mp, value, arg->value_len); - apr_table_addn(arguments, arg->name, (void *)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)); + // apr_table_addn(arguments, arg->name, (void *)arg); + add_argument(msr, arguments, arg); arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); arg->origin = origin; @@ -294,12 +292,67 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, arg->value_len = 0; arg->value = ""; - apr_table_addn(arguments, arg->name, (void *)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)); + // apr_table_addn(arguments, arg->name, (void *)arg); + add_argument(msr, arguments, arg); } free(buf); + return 1; } + +/** + * + */ +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)); + + #ifdef WITH_ICONV + if (msr->txcfg->request_encoding != NULL) { + iconv_t convset; + + // TODO Convert parameter names too. + + /* Initialise iconv. */ + convset = iconv_open("ISO-8859-1", msr->txcfg->request_encoding); + if (convset == (iconv_t)(-1)) { + msr_log(msr, 1, "Iconv init to %s failed: %s", + msr->txcfg->request_encoding, strerror(errno)); + } else { + int ctlparam = 1; + size_t input_bytes = arg->value_len; + size_t output_bytes = arg->value_len; + char *o, *outbuf; + + // TODO Can output be longer than input? + o = outbuf = apr_palloc(msr->mp, output_bytes); + + /* Tell iconv to reject invalid character sequences. */ + iconvctl(convset, ICONV_SET_DISCARD_ILSEQ, &ctlparam); + + /* Convert input character sequence. */ + if (iconv(convset, (const char **)&arg->value, + (size_t *)&input_bytes, + (char **)&outbuf, + (size_t *)&output_bytes) == (size_t)(-1)) + { + msr_log(msr, 1, "Error converting to %s: %s", + msr->txcfg->request_encoding, strerror(errno)); + } else { + arg->value = o; + arg->value_len = arg->value_len - output_bytes; + + msr_log(msr, 5, "Parameter value after conversion from %s: %s", + msr->txcfg->request_encoding, + log_escape_nq_ex(msr->mp, arg->value, arg->value_len)); + } + + iconv_close(convset); + } + } + #endif + + apr_table_addn(arguments, arg->name, (void *)arg); +} diff --git a/apache2/msc_parsers.h b/apache2/msc_parsers.h index d4370c02..fe8afbc7 100644 --- a/apache2/msc_parsers.h +++ b/apache2/msc_parsers.h @@ -20,4 +20,6 @@ int DSOLOCAL parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t int DSOLOCAL parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, int argument_separator, const char *origin, apr_table_t *arguments, int *invalid_count); +void DSOLOCAL add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg); + #endif From bbcf1d08fc52df0e4af5e8dda7ee314951c8c4b4 Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 3 Dec 2007 14:46:00 +0000 Subject: [PATCH 185/372] Added an APR-Util variant of character encoding conversion. --- apache2/Makefile | 1 + apache2/msc_multipart.c | 1 - apache2/msc_parsers.c | 40 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/apache2/Makefile b/apache2/Makefile index a725f3b7..dd4a14d4 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -40,6 +40,7 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 #INCLUDES = -I /usr/include/libxml2 -I /path/to/httpd-x.y/srclib/pcre DEFS = -DWITH_LIBXML2 +# DEFS = -DWITH_ICONV #DEFS = -DWITH_LIBXML2 -DPERFORMANCE_MEASUREMENT #DEFS = -DWITH_LIBXML2 -DNO_MODSEC_API #DEFS = -DWITH_LIBXML2 -DDEBUG_CONF diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 4a9708db..7ffa4a09 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -1170,7 +1170,6 @@ int multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *argument arg->value_origin_len = parts[i]->length; arg->origin = origin; - // apr_table_addn(arguments, arg->name, (void *)arg); add_argument(msr, arguments, arg); } } diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index e00f1bfd..9a6d77c3 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -257,7 +257,6 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, arg->value_len = 0; arg->value = ""; - // apr_table_addn(arguments, arg->name, (void *)arg); add_argument(msr, arguments, arg); arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); @@ -274,7 +273,6 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, arg->value_len = urldecode_nonstrict_inplace_ex((unsigned char *)value, arg->value_origin_len, invalid_count); arg->value = apr_pstrmemdup(msr->mp, value, arg->value_len); - // apr_table_addn(arguments, arg->name, (void *)arg); add_argument(msr, arguments, arg); arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg)); @@ -292,7 +290,6 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, arg->value_len = 0; arg->value = ""; - // apr_table_addn(arguments, arg->name, (void *)arg); add_argument(msr, arguments, arg); } @@ -327,6 +324,9 @@ void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg) { char *o, *outbuf; // TODO Can output be longer than input? + // TODO We are currently using more memory than necessary + // as the parameter values before transformation are + // not freed after conversion. o = outbuf = apr_palloc(msr->mp, output_bytes); /* Tell iconv to reject invalid character sequences. */ @@ -352,7 +352,41 @@ void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg) { iconv_close(convset); } } + #else + if (msr->txcfg->request_encoding != NULL) { + apr_xlate_t *convset = NULL; + int rc; + + rc = apr_xlate_open(&convset, "ISO-8859-1", msr->txcfg->request_encoding, msr->mp); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "apr_xlate_open failed: %s", get_apr_error(msr->mp, rc)); + } else { + apr_size_t input_bytes = arg->value_len; + apr_size_t output_bytes = arg->value_len; + char *o, *outbuf; + + o = outbuf = apr_palloc(msr->mp, output_bytes); + + rc = apr_xlate_conv_buffer(convset, arg->value, &input_bytes, outbuf, &output_bytes); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Error converting to %s: %s", + msr->txcfg->request_encoding, get_apr_error(msr->mp, rc)); + } else { + apr_xlate_conv_buffer(convset, NULL, NULL, outbuf, &output_bytes); + + arg->value = o; + arg->value_len = arg->value_len - output_bytes; + + msr_log(msr, 5, "Parameter value after conversion from %s: %s", + msr->txcfg->request_encoding, + log_escape_nq_ex(msr->mp, arg->value, arg->value_len)); + } + + apr_xlate_close(convset); + } + } #endif apr_table_addn(arguments, arg->name, (void *)arg); } + From 37f5231ccd14aa6f883c7774d8ac983f3790b5ca Mon Sep 17 00:00:00 2001 From: ivanr Date: Mon, 3 Dec 2007 21:13:37 +0000 Subject: [PATCH 186/372] Minor code fixes. --- apache2/re_operators.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 7467f3de..1de69c26 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -973,7 +973,7 @@ static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { /* Compile pattern */ regex = msc_pregcomp(rule->ruleset->mp, pattern, 0, &errptr, &erroffset); if (regex == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %i): %s", + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %d): %s", erroffset, errptr); return 0; } @@ -1537,26 +1537,26 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule switch(rc) { case UNICODE_ERROR_CHARACTERS_MISSING : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: not enough bytes in " - "character (pos %i).", i); + "character (pos %d).", i); return 1; break; case UNICODE_ERROR_INVALID_ENCODING : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: invalid byte value " - "in character (pos %i).", i); + "in character (pos %d).", i); return 1; break; case UNICODE_ERROR_OVERLONG_CHARACTER : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: overlong " - "character detected (pos %i).", i); + "character detected (pos %d).", i); return 1; break; case UNICODE_ERROR_RESTRICTED_CHARACTER : *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: use of restricted character" - " (pos %i).", i); + " (pos %d).", i); return 1; break; case UNICODE_ERROR_DECODING_ERROR : - *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding (pos %i).", i); + *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding (pos %d).", i); return 1; break; } From 715a8eae587193a0a75d9cec55f63ef1393bcf48 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 11 Dec 2007 17:53:50 +0000 Subject: [PATCH 187/372] Implement SecMarker. See #416. --- CHANGES | 2 + apache2/apache2_config.c | 54 ++++++++++++++++++++++ apache2/modsecurity.h | 4 ++ apache2/re.c | 3 ++ apache2/re.h | 3 +- apache2/re_operators.c | 18 ++++++++ doc/modsecurity2-apache-reference.xml | 64 ++++++++++++++++++++------- 7 files changed, 130 insertions(+), 18 deletions(-) diff --git a/CHANGES b/CHANGES index 397d7206..047eee49 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ 29 Nov 2007 - 2.5.0-dev3 ------------------------ + * Added SecMarker directive to allow a fixed target for skipAfter. + * The invoked rule is now logged in the debug log at level 5. * New audit log part 'K' logs all matching rules. diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index b99d0f1b..34cf8718 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -657,18 +657,64 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * return "Internal Error: Failed to add placeholder to the ruleset."; } + /* No longer need to search for the ID */ apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); } return NULL; } +/** + * TODO + */ +static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, const char *p1, + const char *p2, const char *p3) +{ + char *my_error_msg = NULL; + msre_rule *rule = NULL; + extern msc_engine *modsecurity; + int p; + + /* Create a ruleset if one does not exist. */ + if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) { + dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool); + if (dcfg->ruleset == NULL) return FATAL_ERROR; + } + + /* Create the rule now. */ + rule = msre_rule_create(dcfg->ruleset, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg); + if (rule == NULL) { + return my_error_msg; + } + + /* This is a marker */ + rule->placeholder = RULE_PH_MARKER; + + /* Add placeholder to each phase */ + for (p = PHASE_FIRST; p <= PHASE_LAST; p++) { + if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) { + return "Internal Error: Failed to add marker to the ruleset."; + } + } + + /* No longer need to search for the ID */ + apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id); + + return NULL; +} + /* -- Configuration directives -- */ static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) { return add_rule(cmd, (directory_config *)_dcfg, SECACTION_TARGETS, SECACTION_ARGS, p1); } +static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) { + directory_config *dcfg = (directory_config *)_dcfg; + const char *action = apr_pstrcat(dcfg->mp, SECMARKER_BASE_ACTIONS, p1, NULL); + return add_marker(cmd, (directory_config *)_dcfg, SECMARKER_TARGETS, SECMARKER_ARGS, action); +} + static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, const char *p1) { directory_config *dcfg = (directory_config *)_dcfg; @@ -1638,6 +1684,14 @@ const command_rec module_directives[] = { "The filename of the filter debugging log file" ), + AP_INIT_TAKE1 ( + "SecMarker", + cmd_marker, + NULL, + CMD_SCOPE_ANY, + "marker for a skipAfter target" + ), + AP_INIT_FLAG ( "SecPdfProtect", cmd_pdf_protect, diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 715172ba..95cb2e7b 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -117,6 +117,10 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define SECACTION_TARGETS "REQUEST_URI" #define SECACTION_ARGS "@unconditionalMatch" +#define SECMARKER_TARGETS "REQUEST_URI" +#define SECMARKER_ARGS "@noMatch" +#define SECMARKER_BASE_ACTIONS "t:none,pass,id:" + #if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE) #include "unixd.h" #define __SET_MUTEX_PERMS diff --git a/apache2/re.c b/apache2/re.c index 86a721dc..9ed1597a 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1225,6 +1225,9 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, if ((strcmp(SECACTION_TARGETS, targets) == 0) && (strcmp(SECACTION_ARGS, args) == 0)) { rule->unparsed = apr_pstrcat(ruleset->mp, "SecAction", " \"", actions, "\"", NULL); } + 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_pstrcat(ruleset->mp, "SecMarker", " \"", (actions + strlen(SECMARKER_BASE_ACTIONS)), "\"", NULL); + } else { rule->unparsed = apr_pstrcat(ruleset->mp, "SecRule", " \"", targets, "\" \"", args, "\"", (actions != NULL ? " \"" : ""), (actions != NULL ? actions : ""), (actions != NULL ? "\"" : ""), NULL); } diff --git a/apache2/re.h b/apache2/re.h index ed81a015..ab314329 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -110,7 +110,8 @@ int DSOLOCAL msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset #define RULE_MATCH 1 #define RULE_PH_NONE 0 /* Not a placeholder */ -#define RULE_PH_SKIPAFTER 1 /* Placeholder for skipAfter targets */ +#define RULE_PH_SKIPAFTER 1 /* Implicit placeholder for skipAfter */ +#define RULE_PH_MARKER 2 /* Explicit placeholder for SecMarker */ struct msre_rule { apr_array_header_t *targets; diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 1de69c26..b8d86baf 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -53,6 +53,17 @@ static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule, return 1; } +/* noMatch */ + +static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule, + msre_var *var, char **error_msg) +{ + *error_msg = "No match."; + + /* Never match. */ + return 0; +} + /* rx */ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { @@ -1731,6 +1742,13 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_unconditionalmatch_execute ); + /* noMatch */ + msre_engine_op_register(engine, + "noMatch", + NULL, + msre_op_nomatch_execute + ); + /* rx */ msre_engine_op_register(engine, "rx", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index c680a220..89a4f4f1 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -678,7 +678,8 @@ SecAuditLogStorageDir logs/audit K - This part contains a - full list of every rule that matched (one per line) in the order they were matched. + full list of every rule that matched (one per line) in the order + they were matched. @@ -1167,6 +1168,35 @@ SecAuditLogStorageDir logs/audit 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. + + Syntax: SecMarker + id + + Example Usage: SecMarker 9999 + + ProcessingPhase: Any + + Scope: Any + + Dependencies/Notes: None + + SecRule REQUEST_URI "^/$" "chain,skipAfter:960099" +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'" +SecMarker 960099 +
+
<literal>SecPdfProtect</literal> (Experimental) @@ -1290,7 +1320,7 @@ SecAuditLogStorageDir logs/audit with status code 413 Request Entity Too Large. There is a hard limit of 1 GB.
- +
<literal>SecRequestBodyNoFilesLimit</literal> @@ -1299,26 +1329,28 @@ SecAuditLogStorageDir logs/audit files being transported in the request. This directive comes handy to further reduce susceptability 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. + 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 + moreinfo="none">SecRequestBodyNoFilesLimit + NUMBER_IN_BYTES Example Usage: SecRequestBodyLimit 131072 Scope: Any - 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.
@@ -4996,8 +5028,6 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd, "phase:2,sanitiseMatched,log,auditlog,pass,msg:'Potential credit card number'"
-
-
<literal>within</literal> @@ -5116,4 +5146,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- + \ No newline at end of file From 423fd0eea2d0e129ab0eb8177a384502b3c0db44 Mon Sep 17 00:00:00 2001 From: brectanus Date: Tue, 11 Dec 2007 18:03:37 +0000 Subject: [PATCH 188/372] Update skipAfter docs to mention markers. See #416. --- doc/modsecurity2-apache-reference.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 89a4f4f1..465afef7 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -4512,8 +4512,8 @@ 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 is - found. + successful match resuming rule execution after the specified rule id or + marker (see SecMarker) is found.
Action Group: Non-Disruptive @@ -5146,4 +5146,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- \ No newline at end of file + From 2dff0fb9f57f5f2f36e1cebf19b09bc98ec08505 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 12 Dec 2007 01:30:58 +0000 Subject: [PATCH 189/372] Speed up luhn algorithm and add multimatching capabilities to verifyCC. See #69. --- apache2/msc_pcre.c | 18 ++++- apache2/msc_pcre.h | 3 + apache2/re_operators.c | 156 +++++++++++++++++++++-------------------- 3 files changed, 100 insertions(+), 77 deletions(-) diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c index c4f99117..d5a71aa4 100644 --- a/apache2/msc_pcre.c +++ b/apache2/msc_pcre.c @@ -63,6 +63,20 @@ void *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, return regex; } +/** + * Executes regular expression with extended options. + * Returns PCRE_ERROR_NOMATCH when there is no match, error code < -1 + * on errors, and a value > 0 when there is a match. + */ +int msc_regexec_ex(msc_regex_t *regex, const char *s, unsigned int slen, + int startoffset, int options, int *ovector, int ovecsize, char **error_msg) +{ + if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ + *error_msg = NULL; + + return pcre_exec(regex->re, regex->pe, s, slen, startoffset, options, ovector, ovecsize); +} + /** * Executes regular expression, capturing subexpressions in the given * vector. Returns PCRE_ERROR_NOMATCH when there is no match, error code < -1 @@ -74,7 +88,7 @@ int msc_regexec_capture(msc_regex_t *regex, const char *s, unsigned int slen, if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ *error_msg = NULL; - return pcre_exec(regex->re, regex->pe, s, slen, 0, 0, ovector, ovecsize); + return msc_regexec_ex(regex, s, slen, 0, 0, ovector, ovecsize, error_msg); } /** @@ -87,7 +101,7 @@ int msc_regexec(msc_regex_t *regex, const char *s, unsigned int slen, if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ *error_msg = NULL; - return msc_regexec_capture(regex, s, slen, NULL, 0, error_msg); + return msc_regexec_ex(regex, s, slen, 0, 0, NULL, 0, error_msg); } /** diff --git a/apache2/msc_pcre.h b/apache2/msc_pcre.h index 6067de43..4a0cdf6c 100644 --- a/apache2/msc_pcre.h +++ b/apache2/msc_pcre.h @@ -28,6 +28,9 @@ apr_status_t DSOLOCAL msc_pcre_cleanup(msc_regex_t *regex); void DSOLOCAL *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, const char **_errptr, int *_erroffset); +int DSOLOCAL msc_regexec_ex(msc_regex_t *regex, const char *s, unsigned int slen, + int startoffset, int options, int *ovector, int ovecsize, char **error_msg); + int DSOLOCAL msc_regexec_capture(msc_regex_t *regex, const char *s, unsigned int slen, int *ovector, int ovecsize, char **error_msg); diff --git a/apache2/re_operators.c b/apache2/re_operators.c index b8d86baf..63d9e66e 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -933,13 +933,8 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre * Luhn Mod-10 Method (ISO 2894/ANSI 4.13) */ static int luhn_verify(const char *ccnumber, int len) { - char ccdigits[16]; - char *cdst = ccdigits; - const char *csrc = ccnumber; - int digits = 0; - int srclen = 0; - int sum = 0; - int weight; + int sum[2] = { 0, 0 }; + int odd = 0; int i; /* Weighted lookup table which is just a precalculated (i = index): @@ -947,42 +942,35 @@ static int luhn_verify(const char *ccnumber, int len) { */ static int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; /* weight lookup table */ - /* Remove non digits characters and calculate the true length */ - while ((srclen++ <= len) && (digits < 16)) { - if (isdigit(*csrc)) { - *(cdst++) = *csrc; - ++digits; + /* Add up only digits (weighted digits via lookup table) + * for both odd and even CC numbers to avoid 2 passes. + */ + for (i = 0; i < len; i++) { + if (isdigit(ccnumber[i])) { + sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); + sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0')); + odd = 1 - odd; /* alternate weights */ } - csrc++; - } - - /* Determine if the first digit is weighted: odd=no, even=yes */ - weight = !(digits & 1); - - /* Add up digits (weighted digits via lookup table) */ - for (i = 0; i < digits; i++) { - sum += (weight ? wtable[ccdigits[i] - '0'] : (ccdigits[i] - '0')); - weight = !weight; /* alternate weights */ } /* Do a mod 10 on the sum */ - sum %= 10; + sum[odd] %= 10; /* If the result is a zero the card is valid. */ - return sum ? 0 : 1; + return sum[odd] ? 0 : 1; } static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { const char *errptr = NULL; int erroffset; msc_regex_t *regex; - const char *pattern = rule->op_param; + const char *pattern = apr_psprintf(rule->ruleset->mp, "(?:%s)(?C)", rule->op_param); /* Add on a callback to the end */ if (error_msg == NULL) return -1; *error_msg = NULL; /* Compile pattern */ - regex = msc_pregcomp(rule->ruleset->mp, pattern, 0, &errptr, &erroffset); + regex = msc_pregcomp(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %d): %s", erroffset, errptr); @@ -1002,6 +990,8 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * int ovector[33]; int rc; int is_cc = 0; + int capture = 0; + int offset; if (error_msg == NULL) return -1; *error_msg = NULL; @@ -1011,6 +1001,8 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * return -1; } + memset(ovector, 0, sizeof(ovector)); + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -1023,62 +1015,76 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * target_length = var->value_len; } - rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg); - if (rc < -1) { - *error_msg = apr_psprintf(msr->mp, "Regex execution failed: %s", my_error_msg); - return -1; - } + for (offset = 0; ((unsigned int)offset < target_length) && (is_cc == 0); offset++) { - /* Handle captured subexpressions. */ - if (rc > 0) { - int capture = 0; - const apr_array_header_t *tarr; - const apr_table_entry_t *telts; - int i; + rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY | PCRE_ANCHORED, ovector, 30, &my_error_msg); - /* Are we supposed to store the captured subexpressions? */ - /* IMP1 Can we use a flag to avoid having to iterate through the list every time. */ - tarr = apr_table_elts(rule->actionset->actions); - telts = (const apr_table_entry_t*)tarr->elts; - for (i = 0; i < tarr->nelts; i++) { - msre_action *action = (msre_action *)telts[i].val; - if (strcasecmp(action->metadata->name, "capture") == 0) { - capture = 1; + msr_log(msr, 9, "ATTEMPT: %d rc=%d ov:%d,%d \"%.*s\"", offset, rc, ovector[0], ovector[1], (target_length - offset), (target + offset)); + + if (rc == PCRE_ERROR_NOMATCH) { + continue; + } + + if (rc < -1) { + *error_msg = apr_psprintf(msr->mp, "Regex execution failed: %s", my_error_msg); + return -1; + } + + /* Handle captured subexpressions. */ + if (rc > 0) { + int i; + + /* Are we supposed to capture subexpressions? */ + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + + /* Use the available captures. */ + /* to avoid memory manipulation I use the same captures for now to check for CC# */ + for(i = 0; i < rc; i++) { + const char *match = target + ovector[2*i]; + int length = ovector[2*i + 1] - ovector[2*i]; + + msr_log(msr, 9, "LUHN[%d]: %.*s", length, length, match); + + /* Verify agaist Luhn */ + is_cc = luhn_verify(match, length); + + if (!is_cc) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Luhn check failed for \"%.*s\"", length, match); + } + break; + } + + if (capture) { + msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (s == NULL) return -1; + s->name = apr_psprintf(msr->mp, "%d", i); + s->value = apr_pstrmemdup(msr->mp, + match, length); + s->value_len = length; + if ((s->name == NULL)||(s->value == NULL)) return -1; + + apr_table_setn(msr->tx_vars, s->name, (void *)s); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, + log_escape_nq_ex(msr->mp, s->value, s->value_len)); + } + continue; + } break; } - } - - int k; - - /* Use the available captures. */ - /* to avoid memory manipulation I use the same captures for now to check for CC# */ - for(k = 0; k < rc; k++) { - msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - if (s == NULL) return -1; - s->name = apr_psprintf(msr->mp, "%i", k); - s->value = apr_pstrmemdup(msr->mp, - target + ovector[2*k], ovector[2*k + 1] - ovector[2*k]); - s->value_len = (ovector[2*k + 1] - ovector[2*k]); - if ((s->name == NULL)||(s->value == NULL)) return -1; - apr_table_setn(msr->tx_vars, s->name, (void *)s); - - msr_log(msr, 9, "Adding regex subexpression to TXVARS (%i): %s", k, - log_escape_nq(msr->mp, s->value)); - - if ((k > 0) && luhn_verify (s->value, s->value_len)) { - is_cc = 1; - } - } - - /* Unset the remaining ones (from previous invocations). */ - for(k = rc; k <= 9; k++) { - char buf[24]; - apr_snprintf(buf, sizeof(buf), "%i", k); - apr_table_unset(msr->tx_vars, buf); + + /* Unset the remaining TX vars (from previous invocations). */ + for(i = rc; i <= 9; i++) { + char buf[24]; + apr_snprintf(buf, sizeof(buf), "%i", i); + apr_table_unset(msr->tx_vars, buf); + } } } - if ((rc != PCRE_ERROR_NOMATCH) && is_cc) { + if (is_cc) { /* Match. */ /* This message will be logged. */ From 3c1d5a021064c9bfd57bac5c063b7fff0d793fad Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 12 Dec 2007 17:56:25 +0000 Subject: [PATCH 190/372] More efficient multimatch support and cleaned up debugging and messages. See #69. --- apache2/re_operators.c | 69 ++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 63d9e66e..e458827b 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -990,7 +990,6 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * int ovector[33]; int rc; int is_cc = 0; - int capture = 0; int offset; if (error_msg == NULL) return -1; @@ -1016,51 +1015,54 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * } for (offset = 0; ((unsigned int)offset < target_length) && (is_cc == 0); offset++) { + if (msr->txcfg->debuglog_level >= 9) { + if (offset > 0) { + msr_log(msr, 9, "Continuing CC# search at target offset %d.", offset); + } + } - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY | PCRE_ANCHORED, ovector, 30, &my_error_msg); - - msr_log(msr, 9, "ATTEMPT: %d rc=%d ov:%d,%d \"%.*s\"", offset, rc, ovector[0], ovector[1], (target_length - offset), (target + offset)); + rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); + /* If there was no match, then we are done. */ if (rc == PCRE_ERROR_NOMATCH) { - continue; + break; } if (rc < -1) { - *error_msg = apr_psprintf(msr->mp, "Regex execution failed: %s", my_error_msg); + *error_msg = apr_psprintf(msr->mp, "CC# regex execution failed: %s", my_error_msg); return -1; } - /* Handle captured subexpressions. */ + /* Verify a match. */ if (rc > 0) { - int i; + const char *match = target + ovector[0]; + int length = ovector[1] - ovector[0]; + int i = 0; - /* Are we supposed to capture subexpressions? */ - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + offset = ovector[2*i]; - /* Use the available captures. */ - /* to avoid memory manipulation I use the same captures for now to check for CC# */ - for(i = 0; i < rc; i++) { - const char *match = target + ovector[2*i]; - int length = ovector[2*i + 1] - ovector[2*i]; + /* Check the Luhn using the match string */ + is_cc = luhn_verify(match, length); - msr_log(msr, 9, "LUHN[%d]: %.*s", length, length, match); - - /* Verify agaist Luhn */ - is_cc = luhn_verify(match, length); - - if (!is_cc) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Luhn check failed for \"%.*s\"", length, match); - } - break; + /* Not a CC number, then try another match where we left off. */ + if (!is_cc) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CC# Luhn check failed at target offset %d: \"%.*s\"", offset, length, match); } - if (capture) { + continue; + } + + /* We have a potential CC number and need to set any captures + * and we are done. + */ + + if (apr_table_get(rule->actionset->actions, "capture")) { + for(; i < rc; i++) { msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; s->name = apr_psprintf(msr->mp, "%d", i); - s->value = apr_pstrmemdup(msr->mp, - match, length); + s->value = apr_pstrmemdup(msr->mp, match, length); s->value_len = length; if ((s->name == NULL)||(s->value == NULL)) return -1; @@ -1070,17 +1072,17 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, log_escape_nq_ex(msr->mp, s->value, s->value_len)); } - continue; } - break; } /* Unset the remaining TX vars (from previous invocations). */ - for(i = rc; i <= 9; i++) { + for(; i <= 9; i++) { char buf[24]; apr_snprintf(buf, sizeof(buf), "%i", i); apr_table_unset(msr->tx_vars, buf); } + + break; } } @@ -1088,8 +1090,9 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * /* Match. */ /* This message will be logged. */ - *error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s.", - regex->pattern, var->name); + *error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s. [offset \"%d\"]", + regex->pattern, var->name, offset); + return 1; } From e7e9756966685869ae97e017ca74176c50b7ebec Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 12 Dec 2007 18:40:35 +0000 Subject: [PATCH 191/372] Add var name to validateUtf8Encoding message. See #408. --- apache2/re_operators.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index e458827b..2685421f 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -1556,33 +1556,39 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule switch(rc) { case UNICODE_ERROR_CHARACTERS_MISSING : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: not enough bytes in " - "character (pos %d).", i); + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "not enough bytes in character " + "at %s. [offset \"%d\"]", var->name, i); return 1; break; case UNICODE_ERROR_INVALID_ENCODING : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: invalid byte value " - "in character (pos %d).", i); + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "invalid byte value in character " + "at %s. [offset \"%d\"]", var->name, i); return 1; break; case UNICODE_ERROR_OVERLONG_CHARACTER : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: overlong " - "character detected (pos %d).", i); + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "overlong character detected " + "at %s. [offset \"%d\"]", var->name, i); return 1; break; case UNICODE_ERROR_RESTRICTED_CHARACTER : - *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: use of restricted character" - " (pos %d).", i); + *error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: " + "use of restricted character " + "at %s. [offset \"%d\"]", var->name, i); return 1; break; case UNICODE_ERROR_DECODING_ERROR : - *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding (pos %d).", i); + *error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding " + "at %s. [offset \"%d\"]", var->name, i); return 1; break; } if (rc <= 0) { - *error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation."); + *error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation " + "at %s.", var->name); return 1; } From 220342850749ffd044aeb3118f083600507dacef Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 12 Dec 2007 18:43:40 +0000 Subject: [PATCH 192/372] Prefer "offset" to "pos". --- apache2/msc_xml.c | 2 +- apache2/re_operators.c | 4 ++-- apache2/re_variables.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index 7eab6937..7f642dec 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -33,7 +33,7 @@ static void xml_receive_sax_error(void *data, const char *msg, ...) { if (msr == NULL) return; - apr_snprintf(message, sizeof(message), "%s (line %d pos %d)", + 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); diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 2685421f..e21c0182 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -78,7 +78,7 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { /* Compile pattern */ regex = msc_pregcomp(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); if (regex == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %d): %s", + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); return 0; } @@ -972,7 +972,7 @@ static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { /* Compile pattern */ regex = msc_pregcomp(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset); if (regex == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (pos %d): %s", + *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); return 0; } diff --git a/apache2/re_variables.c b/apache2/re_variables.c index 3a5d4535..0ccfb4fd 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -69,7 +69,7 @@ static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { regex = msc_pregcomp(ruleset->mp, pattern, PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); if (regex == NULL) { - return apr_psprintf(ruleset->mp, "Error compiling pattern (pos %d): %s", + return apr_psprintf(ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); } From 54cac6461ba69ebe95e6cc5d0f8961eef3528557 Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 12 Dec 2007 22:52:08 +0000 Subject: [PATCH 193/372] Add IS_NEW and IS_EXPIRED collection variables. See #345. --- CHANGES | 5 +-- apache2/persist_dbm.c | 47 ++++++++++++++++++++++++--- apache2/re_actions.c | 16 +++++++++ doc/modsecurity2-apache-reference.xml | 12 ++++++- 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 047eee49..78db1bb3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,8 @@ - -29 Nov 2007 - 2.5.0-dev3 +12 Dec 2007 - 2.5.0-dev3 ------------------------ + * Added IS_NEW and IS_EXPIRED built-in collection variables. + * Added SecMarker directive to allow a fixed target for skipAfter. * The invoked rule is now logged in the debug log at level 5. diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 43c31938..a92794d9 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -72,6 +72,7 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, apr_table_t *col = NULL; const apr_array_header_t *arr; apr_table_entry_t *te; + int expired = 0; int i; if (msr->txcfg->data_dir == NULL) { @@ -119,11 +120,16 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, msc_string *var = (msc_string *)te[i].val; int expiry_time = atoi(var->value); - /* Do not remove the record itself. */ - if (strcmp(te[i].key, "__expire_KEY") == 0) continue; - if (expiry_time <= apr_time_sec(msr->request_time)) { + // TODO Why dup this? char *key_to_expire = apr_pstrdup(msr->mp, te[i].key); + + /* Do not remove the record itself. */ + if (strcmp(te[i].key, "__expire_KEY") == 0) { + expired = 1; + continue; + } + msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire + 9); apr_table_unset(col, key_to_expire + 9); msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire); @@ -135,6 +141,19 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, } } while(i != arr->nelts); + /* Set IS_EXPIRED if expired */ + if (expired) { + msc_string *var = (msc_string *)apr_table_get(col, "IS_EXPIRED"); + if (var == NULL) { + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "IS_EXPIRED"; + var->name_len = strlen(var->name); + } + if (var != NULL) { + var->value = "1"; + var->value_len = strlen(var->value); + } + } /* Update UPDATE_RATE */ { @@ -176,8 +195,8 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, apr_sdbm_close(dbm); - msr_log(msr, 4, "Retrieved collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape(msr->mp, col_key)); + msr_log(msr, 4, "Retrieved collection (name \"%s\", key \"%s\", expired \"%d\").", + log_escape(msr->mp, col_name), log_escape(msr->mp, col_key), expired); return col; } @@ -272,6 +291,24 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { return 1; } + /* Set IS_NEW to 0 on store. */ + { + msc_string *var = (msc_string *)apr_table_get(col, "IS_NEW"); + if (var != NULL) { + var->value = "0"; + var->value_len = strlen(var->value); + } + } + + /* Set IS_EXPIRED to 0 on store. */ + { + msc_string *var = (msc_string *)apr_table_get(col, "IS_EXPIRED"); + if (var != NULL) { + var->value = "0"; + var->value_len = strlen(var->value); + } + } + /* Update the timeout value. */ { msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT"); diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 5691c433..b6e569fe 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -1322,6 +1322,22 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var->value = "0"; var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); + + /* This is a new collection. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "IS_NEW"; + var->name_len = strlen(var->name); + var->value = "1"; + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); + + /* It has not yet expired. */ + var = apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "IS_EXPIRED"; + var->name_len = strlen(var->name); + var->value = "0"; + var->value_len = strlen(var->value); + apr_table_setn(table, var->name, (void *)var); } /* Add the collection to the list. */ diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 465afef7..6e711fd7 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3934,6 +3934,16 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ the creation of the collection.
+ + IS_EXPIRED - set to 1 if + the collection is expired 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). @@ -5146,4 +5156,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- + \ No newline at end of file From cbf79d43ba1c26ff5ad464be2978b56d9d7b9cdd Mon Sep 17 00:00:00 2001 From: brectanus Date: Wed, 12 Dec 2007 23:08:14 +0000 Subject: [PATCH 194/372] Update version to ready for 2.5.0-rc1. --- CHANGES | 4 ++-- apache2/modsecurity.h | 8 ++++---- doc/modsecurity2-apache-reference.xml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 78db1bb3..5a3774f6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,5 @@ -12 Dec 2007 - 2.5.0-dev3 ------------------------- +12 Dec 2007 - 2.5.0-rc1 +----------------------- * Added IS_NEW and IS_EXPIRED built-in collection variables. diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 95cb2e7b..a9c8a79f 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -65,14 +65,14 @@ 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 "dev" -#define MODSEC_VERSION_RELEASE "3" +#define MODSEC_VERSION_TYPE "rc" +#define MODSEC_VERSION_RELEASE "1" #define MODULE_NAME "ModSecurity for Apache" #define MODULE_RELEASE \ MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \ "-" MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE -#define MODULE_NAME_FULL MODULE_NAME "/" MODULE_RELEASE " (http://www.modsecurity.org)" +#define MODULE_NAME_FULL MODULE_NAME "/" MODULE_RELEASE " (http://www.modsecurity.org/)" #define PHASE_REQUEST_HEADERS 1 #define PHASE_REQUEST_BODY 2 @@ -83,7 +83,7 @@ extern DSOLOCAL modsec_build_type_rec modsec_build_type[]; #define PHASE_LAST PHASE_LOGGING #define NOT_SET -1l -#define NOT_SET_P (void *)-1l +#define NOT_SET_P ((void *)-1l) #define CREATEMODE ( APR_UREAD | APR_UWRITE | APR_GREAD ) #define CREATEMODE_DIR ( APR_UREAD | APR_UWRITE | APR_UEXECUTE | APR_GREAD | APR_GEXECUTE ) diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 6e711fd7..a05136be 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-dev?/ (October 15, 2007) + Version 2.5.0-rc1/ (December 12, 2007) 2004-2007 @@ -5156,4 +5156,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- \ No newline at end of file + From b0de65913382769dcf4853c75e6c75c837c6cab6 Mon Sep 17 00:00:00 2001 From: brectanus Date: Thu, 13 Dec 2007 00:58:02 +0000 Subject: [PATCH 195/372] Added t:jsDecodeUni handling unicode similar to t:urlDecodeUni. See #193. --- CHANGES | 2 + apache2/msc_util.c | 80 +++++++++++++++++++++++---- apache2/msc_util.h | 2 + apache2/re_tfns.c | 20 +++++++ doc/modsecurity2-apache-reference.xml | 12 +++- 5 files changed, 104 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 5a3774f6..4a5e7edf 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ 12 Dec 2007 - 2.5.0-rc1 ----------------------- + * Added t:jsDecodeUni to decode JavScript \uXXXX encoding. + * Added IS_NEW and IS_EXPIRED built-in collection variables. * Added SecMarker directive to allow a fixed target for skipAfter. diff --git a/apache2/msc_util.c b/apache2/msc_util.c index d98d061c..4b6da97b 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -16,6 +16,11 @@ #include #include +/* NOTE: Be careful as this can only be used on static values for X. + * (i.e. VALID_HEX(c++) will NOT work) + */ +#define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) + /** * */ @@ -566,7 +571,68 @@ char *_log_escape(apr_pool_t *mp, const unsigned char *input, unsigned long int return ret; } -#define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) +/** + * JavaScript \uXXXX decoding. + */ +int jsdecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) { + unsigned char *d = (unsigned char *)input; + long int i, count; + + if (input == NULL) return -1; + + i = count = 0; + while (i < input_len) { + if (input[i] == '\\') { + /* Character is an escape. */ + + if ((i + 5 < input_len) && (input[i + 1] == 'u')) { + /* We have at least 4 data bytes. */ + if ( (VALID_HEX(input[i + 2])) && (VALID_HEX(input[i + 3])) + && (VALID_HEX(input[i + 4])) && (VALID_HEX(input[i + 5])) ) + { + /* We first make use of the lower byte here, ignoring the higher byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) + { + (*d) += 0x20; + } + + d++; + count++; + i += 6; + } + else { + /* Invalid data. */ + int j; + + for(j = 0; (j < 6)&&(i < input_len); j++) { + *d++ = input[i++]; + count++; + } + } + } + else { + /* Not enough bytes available (4 data bytes were needed). */ + while(i < input_len) { + *d++ = input[i++]; + count++; + } + } + } + else { + *d++ = input[i++]; + count++; + } + } + + *d = '\0'; + + return count; +} /** * @@ -632,12 +698,7 @@ int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) char c1 = input[i + 1]; char c2 = input[i + 2]; - /* ENH Use VALID_HEX? */ - if ( (((c1 >= '0')&&(c1 <= '9')) || ((c1 >= 'a')&&(c1 <= 'f')) || - ((c1 >= 'A')&&(c1 <= 'F'))) - && (((c2 >= '0')&&(c2 <= '9')) || ((c2 >= 'a')&&(c2 <= 'f')) || - ((c2 >= 'A')&&(c2 <= 'F'))) ) - { + if (VALID_HEX(c1) && VALID_HEX(c2)) { *d++ = x2c(&input[i + 1]); count++; i += 3; @@ -701,10 +762,7 @@ int urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_len, int char c1 = input[i + 1]; char c2 = input[i + 2]; - /* ENH Use VALID_HEX? */ - if ( (((c1 >= '0')&&(c1 <= '9')) || ((c1 >= 'a')&&(c1 <= 'f')) || ((c1 >= 'A')&&(c1 <= 'F'))) - && (((c2 >= '0')&&(c2 <= '9')) || ((c2 >= 'a')&&(c2 <= 'f')) || ((c2 >= 'A')&&(c2 <= 'F'))) ) - { + if (VALID_HEX(c1) && VALID_HEX(c2)) { /* Valid encoding - decode it. */ *d++ = x2c(&input[i + 1]); count++; diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 1ae7b873..a4014cb3 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -66,6 +66,8 @@ char DSOLOCAL *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigne char DSOLOCAL *_log_escape(apr_pool_t *p, const unsigned char *input, unsigned long int input_length, int escape_quotes, int escape_colon); +int DSOLOCAL jsdecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len); + int DSOLOCAL urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_length); int DSOLOCAL urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_length, int *invalid_count); diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 992f4835..7c19dc5f 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -251,6 +251,20 @@ static int msre_fn_replaceComments_execute(apr_pool_t *mptmp, unsigned char *inp return changed; } +/* jsDecodeUni */ + +static int msre_fn_jsDecodeUni_execute(apr_pool_t *mptmp, unsigned char *input, + long int input_len, char **rval, long int *rval_len) +{ + long int length; + + length = jsdecode_uni_nonstrict_inplace_ex(input, input_len); + *rval = (char *)input; + *rval_len = length; + + return (*rval_len == input_len ? 0 : 1); +} + /* urlDecode */ static int msre_fn_urlDecode_execute(apr_pool_t *mptmp, unsigned char *input, @@ -504,6 +518,12 @@ void msre_engine_register_default_tfns(msre_engine *engine) { msre_fn_htmlEntityDecode_execute ); + /* jsDecodeUni */ + msre_engine_tfn_register(engine, + "jsDecodeUni", + msre_fn_jsDecodeUni_execute + ); + /* length */ msre_engine_tfn_register(engine, "length", diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index a05136be..6cd716b2 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3368,6 +3368,16 @@ SecRule XML:/xq:employees/employee/name/text() Fred \
+
+ <literal>jsDecodeUni</literal> + + Decodes \uXXXX JavaScript + 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>length</literal> @@ -5156,4 +5166,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- + \ No newline at end of file From 8aa31fd0993789db318ffbf665f92aefb8d253df Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 00:19:46 +0000 Subject: [PATCH 196/372] Change jsDecodeuni to jsDecode which also decodes all the other JS escapes. See #193. --- CHANGES | 2 +- apache2/msc_util.c | 129 ++++++++++++++++++++------ apache2/msc_util.h | 4 +- apache2/re_tfns.c | 12 +-- doc/modsecurity2-apache-reference.xml | 11 +-- 5 files changed, 113 insertions(+), 45 deletions(-) diff --git a/CHANGES b/CHANGES index 4a5e7edf..a6b1a14c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,7 @@ 12 Dec 2007 - 2.5.0-rc1 ----------------------- - * Added t:jsDecodeUni to decode JavScript \uXXXX encoding. + * Added t:jsDecode to decode JavScript escape sequences. * Added IS_NEW and IS_EXPIRED built-in collection variables. diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 4b6da97b..32f5a572 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -16,10 +16,12 @@ #include #include -/* NOTE: Be careful as this can only be used on static values for X. +/* NOTE: Be careful as these can ONLY be used on static values for X. * (i.e. VALID_HEX(c++) will NOT work) */ #define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F'))) +#define ISODIGIT(X) ((X >= '0')&&(X <= '7')) + /** * @@ -68,6 +70,7 @@ int parse_name_eq_value(apr_pool_t *mp, const char *input, char **name, char **v /** * + * IMP1 Assumes NUL-terminated */ char *url_encode(apr_pool_t *mp, char *input, unsigned int input_len) { char *rval, *d; @@ -572,9 +575,11 @@ char *_log_escape(apr_pool_t *mp, const unsigned char *input, unsigned long int } /** - * JavaScript \uXXXX decoding. + * JavaScript decoding. + * IMP1 Assumes NUL-terminated */ -int jsdecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) { + +int js_decode_nonstrict_inplace(unsigned char *input, long int input_len) { unsigned char *d = (unsigned char *)input; long int i, count; @@ -585,38 +590,92 @@ int jsdecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) if (input[i] == '\\') { /* Character is an escape. */ - if ((i + 5 < input_len) && (input[i + 1] == 'u')) { - /* We have at least 4 data bytes. */ - if ( (VALID_HEX(input[i + 2])) && (VALID_HEX(input[i + 3])) - && (VALID_HEX(input[i + 4])) && (VALID_HEX(input[i + 5])) ) + if ( (i + 5 < input_len) && (input[i + 1] == 'u') + && (VALID_HEX(input[i + 2])) && (VALID_HEX(input[i + 3])) + && (VALID_HEX(input[i + 4])) && (VALID_HEX(input[i + 5])) ) + { + /* \uHHHH */ + + /* Use only the lower byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) { - /* We first make use of the lower byte here, ignoring the higher byte. */ - *d = x2c(&input[i + 4]); - - /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ - if ( (*d > 0x00) && (*d < 0x5f) - && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) - && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) - { - (*d) += 0x20; - } - - d++; - count++; - i += 6; + (*d) += 0x20; } - else { - /* Invalid data. */ - int j; - for(j = 0; (j < 6)&&(i < input_len); j++) { - *d++ = input[i++]; - count++; + d++; + count++; + i += 6; + } + else if ((i + 3 < input_len) && (input[i + 1] == 'x')) { + /* \xHH */ + *d++ = x2c(&input[i + 2]); + count++; + i += 4; + } + else if ((i + 1 < input_len) && ISODIGIT(input[i + 1])) { + /* \OOO (only one byte, \000 - \377) */ + char buf[4]; + int j = 0; + + while((i + 1 + j < input_len)&&(j < 3)) { + buf[j] = input[i + 1 + j]; + j++; + if (!ISODIGIT(input[i + 1 + j])) break; + } + buf[j] = '\0'; + + if (j > 0) { + /* Do not use 3 characters if we will be > 1 byte */ + if ((j == 3) && (buf[0] > '3')) { + j = 2; + buf[j] = '\0'; } + *d++ = strtol(buf, NULL, 8); + i += 1 + j; + count++; } } + else if (i + 1 < input_len) { + /* \C */ + unsigned char c = input[i + 1]; + switch(input[i + 1]) { + case 'a' : + c = '\a'; + break; + case 'b' : + c = '\b'; + break; + case 'f' : + c = '\f'; + break; + case 'n' : + c = '\n'; + break; + case 'r' : + c = '\r'; + break; + case 't' : + c = '\t'; + break; + case 'v' : + c = '\v'; + break; + /* The remaining (\?,\\,\',\") are just a removal + * of the escape char which is default. + */ + } + + *d++ = c; + i += 2; + count++; + } else { - /* Not enough bytes available (4 data bytes were needed). */ + /* Not enough bytes */ while(i < input_len) { *d++ = input[i++]; count++; @@ -636,6 +695,7 @@ int jsdecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) /** * + * IMP1 Assumes NUL-terminated */ int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) { unsigned char *d = input; @@ -745,6 +805,7 @@ int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) /** * + * IMP1 Assumes NUL-terminated */ int urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *invalid_count) { unsigned char *d = (unsigned char *)input; @@ -809,6 +870,7 @@ int urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_len, int /** * + * IMP1 Assumes NUL-terminated */ int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input_len) { unsigned char *d = input; @@ -884,6 +946,7 @@ int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k); /* Decode the entity. */ + /* ENH What about others? */ if (strcasecmp(x, "quot") == 0) *d++ = '"'; else if (strcasecmp(x, "amp") == 0) *d++ = '&'; @@ -923,8 +986,10 @@ int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input return count; } -#define ISODIGIT(X) ((X >= '0')&&(X <= '7')) - +/** + * + * IMP1 Assumes NUL-terminated + */ int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) { unsigned char *d = input; int i, count; @@ -1032,6 +1097,10 @@ int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) { return count; } +/** + * + * IMP1 Assumes NUL-terminated + */ int normalise_path_inplace(unsigned char *input, int input_len, int win) { unsigned char *d = input; int i, count; diff --git a/apache2/msc_util.h b/apache2/msc_util.h index a4014cb3..8ed5398b 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -35,7 +35,7 @@ int DSOLOCAL is_token_char(unsigned char c); int DSOLOCAL remove_lf_crlf_inplace(char *text); -unsigned DSOLOCAL char x2c(unsigned char *what); +unsigned char DSOLOCAL x2c(unsigned char *what); char DSOLOCAL *guess_tmp_dir(apr_pool_t *p); @@ -66,7 +66,7 @@ char DSOLOCAL *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigne char DSOLOCAL *_log_escape(apr_pool_t *p, const unsigned char *input, unsigned long int input_length, int escape_quotes, int escape_colon); -int DSOLOCAL jsdecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len); +int DSOLOCAL js_decode_nonstrict_inplace(unsigned char *input, long int input_len); int DSOLOCAL urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_length); diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 7c19dc5f..1c8a0cf1 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -251,14 +251,14 @@ static int msre_fn_replaceComments_execute(apr_pool_t *mptmp, unsigned char *inp return changed; } -/* jsDecodeUni */ +/* jsDecode */ -static int msre_fn_jsDecodeUni_execute(apr_pool_t *mptmp, unsigned char *input, +static int msre_fn_jsDecode_execute(apr_pool_t *mptmp, unsigned char *input, long int input_len, char **rval, long int *rval_len) { long int length; - length = jsdecode_uni_nonstrict_inplace_ex(input, input_len); + length = js_decode_nonstrict_inplace(input, input_len); *rval = (char *)input; *rval_len = length; @@ -518,10 +518,10 @@ void msre_engine_register_default_tfns(msre_engine *engine) { msre_fn_htmlEntityDecode_execute ); - /* jsDecodeUni */ + /* jsDecode */ msre_engine_tfn_register(engine, - "jsDecodeUni", - msre_fn_jsDecodeUni_execute + "jsDecode", + msre_fn_jsDecode_execute ); /* length */ diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 6cd716b2..121ca773 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3369,13 +3369,12 @@ SecRule XML:/xq:employees/employee/name/text() Fred \
- <literal>jsDecodeUni</literal> + <literal>jsDecode</literal> - Decodes \uXXXX JavaScript - 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. + 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.
From aa68fff10477c37e34de82a2ddd0f78a47df6952 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 00:30:25 +0000 Subject: [PATCH 197/372] Fixed decoding \9 with t:escapeSeqDecode. See #423. --- CHANGES | 4 +++- apache2/msc_util.c | 2 +- doc/modsecurity2-apache-reference.xml | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index a6b1a14c..b1d7aa27 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -12 Dec 2007 - 2.5.0-rc1 +13 Dec 2007 - 2.5.0-rc1 ----------------------- + * Fixed t:escapeSeqDecode to better follow ANSI C escapes. + * Added t:jsDecode to decode JavScript escape sequences. * Added IS_NEW and IS_EXPIRED built-in collection variables. diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 32f5a572..ca8d1385 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1054,7 +1054,7 @@ int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) { } } else - if (isdigit(input[i + 1])) { /* Octal. */ + if (ISODIGIT(input[i + 1])) { /* Octal. */ char buf[10]; int j = 0, l = 3; diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 121ca773..08217c77 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-rc1/ (December 12, 2007) + Version 2.5.0-rc1/ (December 13, 2007) 2004-2007 @@ -5165,4 +5165,4 @@ SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:l
- \ No newline at end of file + From 4c11791a9460d8874a2b40b68c94f72c0da69519 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 00:42:04 +0000 Subject: [PATCH 198/372] Escape cache value in log. --- apache2/re.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index 9ed1597a..6b97dff1 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1816,7 +1816,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { crec->path = tfnspath; crec->val = changed ? apr_pmemdup(msr->mp, var->value, var->value_len) : NULL; crec->val_len = changed ? var->value_len : 0; - msr_log(msr, 9, "CACHE: Caching %s=\"%.*s\"", tfnskey, var->value_len, var->value); + msr_log(msr, 9, "CACHE: Caching %s=\"%.*s\"", tfnskey, var->value_len, log_escape_nq_ex(mptmp, var->value, var->value_len)); apr_table_setn(cachetab, tfnskey, (void *)crec); } From 5065852dfee8a4b6ed21a928a3d0a9e450232196 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 19:53:23 +0000 Subject: [PATCH 199/372] More efficient collection persistance and deletion on retrieval. See #345 and #426. --- CHANGES | 4 +- apache2/persist_dbm.c | 248 ++++++++++++-------------- apache2/re_actions.c | 8 - doc/modsecurity2-apache-reference.xml | 7 +- 4 files changed, 118 insertions(+), 149 deletions(-) diff --git a/CHANGES b/CHANGES index b1d7aa27..f8ea58de 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ -13 Dec 2007 - 2.5.0-rc1 +14 Dec 2007 - 2.5.0-rc1 ----------------------- + * More efficient collection persistance. + * Fixed t:escapeSeqDecode to better follow ANSI C escapes. * Added t:jsDecode to decode JavScript escape sequences. diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index a92794d9..22c4b676 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -46,7 +46,7 @@ static apr_table_t *collection_unpack(modsec_rec *msr, char *blob, unsigned int blob_offset += var->value_len; var->value_len--; - if (log_vars) { + if (log_vars && (msr->txcfg->debuglog_level >= 9)) { msr_log(msr, 9, "Read variable: name \"%s\", value \"%s\".", log_escape_ex(msr->mp, var->name, var->name_len), log_escape_ex(msr->mp, var->value, var->value_len)); @@ -78,27 +78,30 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, if (msr->txcfg->data_dir == NULL) { msr_log(msr, 1, "Unable to retrieve collection (name \"%s\", key \"%s\"). Use " "SecDataDir to define data directory first.", log_escape(msr->mp, col_name), - log_escape(msr->mp, col_key)); + log_escape_ex(msr->mp, col_key, col_key_len)); return NULL; } dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL); + 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; } - key.dptr = (char *)col_key; - key.dsize = col_key_len + 1; - 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) { - apr_sdbm_close(dbm); msr_log(msr, 1, "Failed to read from DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); + apr_sdbm_close(dbm); return NULL; } @@ -107,6 +110,11 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, return NULL; } + /* ENH Need expiration (and perhaps other metadata) accessible in blob + * form so we can determine if we need to convert to a table. This will + * save some cycles. + */ + /* Transform raw data into a table. */ col = collection_unpack(msr, value->dptr, value->dsize, 1); if (col == NULL) return NULL; @@ -121,38 +129,61 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, int expiry_time = atoi(var->value); if (expiry_time <= apr_time_sec(msr->request_time)) { - // TODO Why dup this? - char *key_to_expire = apr_pstrdup(msr->mp, te[i].key); + char *key_to_expire = te[i].key; - /* Do not remove the record itself. */ - if (strcmp(te[i].key, "__expire_KEY") == 0) { + /* Done early if the col expired */ + if (strcmp(key_to_expire, "__expire_KEY") == 0) { expired = 1; - continue; } - - msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire + 9); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire + 9); + msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire); + } apr_table_unset(col, key_to_expire + 9); - msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire); apr_table_unset(col, key_to_expire); - msr_log(msr, 4, "Removed expired variable \"%s\".", key_to_expire + 9); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Removed expired variable \"%s\".", key_to_expire + 9); + } break; } } } - } while(i != arr->nelts); + } while(!expired && (i != arr->nelts)); - /* Set IS_EXPIRED if expired */ - if (expired) { - msc_string *var = (msc_string *)apr_table_get(col, "IS_EXPIRED"); - if (var == NULL) { - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "IS_EXPIRED"; - var->name_len = strlen(var->name); + /* Delete the collection if the variable "KEY" does not exist. + * + * ENH It would probably be more efficient to hold the DBM + * open until we determine if it needs deleted than to open a second + * 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 (var != NULL) { - var->value = "1"; - var->value_len = strlen(var->value); + + 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), + log_escape_ex(msr->mp, col_key, col_key_len), get_apr_error(msr->mp, rc)); + return NULL; } + + 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)); + } + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Deleted collection (name \"%s\", key \"%s\").", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); + } + return NULL; } /* Update UPDATE_RATE */ @@ -171,13 +202,12 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, } else { apr_time_t td; counter = atoi(var->value); - var = (msc_string *)apr_table_get(col, "UPDATE_RATE"); - if (var == NULL) { - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "UPDATE_RATE"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - } + + /* UPDATE_RATE is removed on store, so we add it back here */ + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "UPDATE_RATE"; + var->name_len = strlen(var->name); + apr_table_setn(col, var->name, (void *)var); /* NOTE: No rate if there has been no time elapsed */ td = (apr_time_sec(apr_time_now()) - create_time); @@ -193,10 +223,10 @@ apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, } } - apr_sdbm_close(dbm); - - msr_log(msr, 4, "Retrieved collection (name \"%s\", key \"%s\", expired \"%d\").", - log_escape(msr->mp, col_name), log_escape(msr->mp, col_key), expired); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Retrieved collection (name \"%s\", key \"%s\").", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); + } return col; } @@ -230,84 +260,17 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { if (msr->txcfg->data_dir == NULL) { msr_log(msr, 1, "Unable to store collection (name \"%s\", key \"%s\"). Use " "SecDataDir to define data directory first.", - log_escape(msr->mp, var_name->value), log_escape(msr->mp, var_key->value)); + log_escape_ex(msr->mp, var_name->value, var_name->value_len), log_escape_ex(msr->mp, var_key->value, var_key->value_len)); return -1; } dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL); - /* Remove expired variables. */ - do { - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - if (strncmp(te[i].key, "__expire_", 9) == 0) { - msc_string *var = (msc_string *)te[i].val; - int expiry_time = atoi(var->value); + /* Delete IS_NEW on store. */ + apr_table_unset(col, "IS_NEW"); - /* Do not remove the record itself. */ - if (strcmp(te[i].key, "__expire_KEY") == 0) continue; - - if (expiry_time <= apr_time_sec(msr->request_time)) { - char *key_to_expire = apr_pstrdup(msr->mp, te[i].key); - msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire + 9); - apr_table_unset(col, key_to_expire + 9); - msr_log(msr, 9, "Removing key \"%s\" from collection.", key_to_expire); - apr_table_unset(col, key_to_expire); - msr_log(msr, 4, "Removed expired variable \"%s\".", key_to_expire + 9); - break; - } - } - } - } while(i != arr->nelts); - - /* Delete the collection if the variable "KEY" does not exist. */ - 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 -1; - } - - key.dptr = var_key->value; - key.dsize = var_key->value_len + 1; - - rc = apr_sdbm_delete(dbm, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Failed deleting collection (name \"%s\", " - "key \"%s\"): %s", log_escape(msr->mp, var_name->value), - log_escape(msr->mp, var_key->value), get_apr_error(msr->mp, rc)); - apr_sdbm_close(dbm); - return -1; - } - - msr_log(msr, 4, "Deleted collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, var_name->value), log_escape(msr->mp, var_key->value)); - apr_sdbm_close(dbm); - - return 1; - } - - /* Set IS_NEW to 0 on store. */ - { - msc_string *var = (msc_string *)apr_table_get(col, "IS_NEW"); - if (var != NULL) { - var->value = "0"; - var->value_len = strlen(var->value); - } - } - - /* Set IS_EXPIRED to 0 on store. */ - { - msc_string *var = (msc_string *)apr_table_get(col, "IS_EXPIRED"); - if (var != NULL) { - var->value = "0"; - var->value_len = strlen(var->value); - } - } + /* Delete UPDATE_RATE on store to save space as it is calculated */ + apr_table_unset(col, "UPDATE_RATE"); /* Update the timeout value. */ { @@ -351,6 +314,11 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { var->value_len = strlen(var->value); } + /* ENH Make the expiration timestamp accessible in blob form so that + * it is easier/faster to determine expiration without having to + * convert back to table form + */ + /* Calculate the size first. */ blob_size = 3 + 2; arr = apr_table_elts(col); @@ -403,9 +371,11 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { blob[blob_offset + 2 + len - 1] = '\0'; blob_offset += 2 + len; - msr_log(msr, 9, "Wrote variable: name \"%s\", value \"%s\".", - log_escape_ex(msr->mp, var->name, var->name_len), - log_escape_ex(msr->mp, var->value, var->value_len)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Wrote variable: name \"%s\", value \"%s\".", + log_escape_ex(msr->mp, var->name, var->name_len), + log_escape_ex(msr->mp, var->value, var->value_len)); + } } blob[blob_offset] = 0; @@ -414,6 +384,12 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { /* And, finally, store it. */ dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL); + key.dptr = var_key->value; + key.dsize = var_key->value_len + 1; + + 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) { @@ -422,24 +398,20 @@ int collection_store(modsec_rec *msr, apr_table_t *col) { return -1; } - key.dptr = var_key->value; - key.dsize = var_key->value_len + 1; - - value.dptr = (char *)blob; - value.dsize = blob_size; - 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)); - apr_sdbm_close(dbm); return -1; } - msr_log(msr, 4, "Persisted collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, var_name->value), log_escape(msr->mp, var_key->value)); - - 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)); + } return 0; } @@ -490,14 +462,16 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { */ rc = apr_sdbm_firstkey(dbm, &key); while(rc == APR_SUCCESS) { - char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize); + char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize - 1); *(char **)apr_array_push(keys_arr) = s; rc = apr_sdbm_nextkey(dbm, &key); } apr_sdbm_unlock(dbm); - msr_log(msr, 9, "Found %d record(s) in file \"%s\".", keys_arr->nelts, - log_escape(msr->mp, dbm_filename)); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Found %d record(s) in file \"%s\".", keys_arr->nelts, + log_escape(msr->mp, dbm_filename)); + } /* Now retrieve the entires one by one. */ keys = (char **)keys_arr->elts; @@ -519,6 +493,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { col = collection_unpack(msr, value.dptr, value.dsize, 0); if (col == NULL) { + apr_sdbm_close(dbm); return -1; } @@ -526,25 +501,30 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { if (var == NULL) { msr_log(msr, 1, "Collection cleanup discovered entry with no " "__expire_KEY (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape(msr->mp, key.dptr)); + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); } else { unsigned int expiry_time = atoi(var->value); - msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.", - log_escape(msr->mp, col_name), log_escape(msr->mp, key.dptr), - expiry_time - now); + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1), + expiry_time - now); + } if (expiry_time <= now) { rc = apr_sdbm_delete(dbm, key); if (rc != APR_SUCCESS) { msr_log(msr, 1, "Failed deleting collection (name \"%s\", " "key \"%s\"): %s", log_escape(msr->mp, col_name), - log_escape(msr->mp, key.dptr), get_apr_error(msr->mp, rc)); + log_escape_ex(msr->mp, key.dptr, key.dsize - 1), get_apr_error(msr->mp, rc)); + apr_sdbm_close(dbm); return -1; } - msr_log(msr, 4, "Removed stale collection (name \"%s\", " - "key \"%s\").", log_escape(msr->mp, col_name), - log_escape(msr->mp, key.dptr)); + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Removed stale collection (name \"%s\", " + "key \"%s\").", log_escape(msr->mp, col_name), + log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); + } } } } else { diff --git a/apache2/re_actions.c b/apache2/re_actions.c index b6e569fe..873d126d 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -1330,14 +1330,6 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, var->value = "1"; var->value_len = strlen(var->value); apr_table_setn(table, var->name, (void *)var); - - /* It has not yet expired. */ - var = apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "IS_EXPIRED"; - var->name_len = strlen(var->name); - var->value = "0"; - var->value_len = strlen(var->value); - apr_table_setn(table, var->name, (void *)var); } /* Add the collection to the list. */ diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 08217c77..10dba0ae 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.5.0-rc1/ (December 13, 2007) + Version 2.5.0-rc1/ (December 14, 2007) 2004-2007 @@ -3943,11 +3943,6 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ the creation of the collection. - - IS_EXPIRED - set to 1 if - the collection is expired otherwise set to 0. - - IS_NEW - set to 1 if the collection is new (not yet persisted) otherwise set to 0. From f68f0156c3cc44bdeae10c1db68a0290ceabfa9e Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 20:20:18 +0000 Subject: [PATCH 200/372] Cleanup CHANGES. --- CHANGES | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index f8ea58de..585ef22f 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,8 @@ 14 Dec 2007 - 2.5.0-rc1 ----------------------- +Changes since 2.5.0-dev2: + * More efficient collection persistance. * Fixed t:escapeSeqDecode to better follow ANSI C escapes. @@ -9,10 +11,6 @@ * Added IS_NEW and IS_EXPIRED built-in collection variables. - * Added SecMarker directive to allow a fixed target for skipAfter. - - * The invoked rule is now logged in the debug log at level 5. - * New audit log part 'K' logs all matching rules. * Implemented SecRequestBodyNoFilesLimit. @@ -22,20 +20,24 @@ * Renamed SecGeoLookupsDb to SecGeoLookupDB. - * Implement SecComponentSignature. - - * Fix warnings on Solaris and/or 64bit builds. + * Added SecComponentSignature to allow other components the ability + to append to the logged signature. * Added skipAfter: action to allow skipping all rules until a rule with a specified ID is reached. Rule execution then continues after the specified rule. + * Added SecMarker directive to allow a fixed target for skipAfter. + * Added ctl:ruleRemoveById action to allow rule removal on a match. * Added a @containsWord operator that will match a given string anywhere in the target value, but only on word boundaries. - * New MATCHED_VAR variable to store the last matched variable name + * Added a MATCHED_VAR_NAME variable to store the last matched variable name + so that it can be more easily used by rules. + + * Added a MATCHED_VAR variable to store the last matched variable value so that it can be more easily used by rules. * Fixed expansion of macros when using relative changes with setvar. In @@ -45,8 +47,6 @@ a level 1-3 message to the debug log are now marked as 'relevant' and may generate an audit log entry. - * Do not process subrequests in phase 2-4, but do hand off the request data. - * Fixed deprecatevar:var=N/S action so that it decrements N every S seconds as documented instead of decrementing by a rate. @@ -63,15 +63,12 @@ * Fixed the base64decode transformation function to not return extra characters at the end. - * Removed potential for extra characters to be appended to the value when - using base64Decode. - * Return from the output filter with an error in addition to setting up the HTTP error status in the output data. - * Used new API calls to get the server version/banner when available. + * Used new Apache API calls to get the server version/banner when available. - * Added "logdata" meta action to allow safe logging of raw transaction data. + * Added "logdata" meta action to allow logging of raw transaction data. * Added TX_SEVERITY that keeps track of the highest severity for any matched rules so far. @@ -82,7 +79,8 @@ * Added MODSEC_BUILD variable that contains the numeric build value based on the ModSecurity version. - * Enhanced debug logging. + * Enhanced debug logging by displaying more data on rule execution. All + invoked rules are now logged in the debug log at level 5. * Cleaned up and clarified some documentation. @@ -90,9 +88,13 @@ * Stricter validation for @validateUtf8Encoding. - * Capture the match in TX:0 when using "capture" action in phrase match + * Now capture the match in TX:0 when using "capture" action in phrase match operators. + * No longer process internal subrequests. + + * Fixed warnings on Solaris and/or 64bit builds. + * Added Cygwin to the list of platforms not supporting the hidden visibility attribute. From 515290434c7985c42ede55fb33eb20b9200fe306 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 20:22:54 +0000 Subject: [PATCH 201/372] Add 2.1.4 changes to trunk CHANGES. --- CHANGES | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGES b/CHANGES index 585ef22f..9a4c876b 100644 --- a/CHANGES +++ b/CHANGES @@ -99,6 +99,21 @@ Changes since 2.5.0-dev2: visibility attribute. +27 Nov 2007 - 2.1.4 +------------------- + + * Updated included Core Ruleset to version 1.5 and noted in the docs that + XML support is required to use the rules without modification. + + * Fixed an evasion FP, mistaking a multipart non-boundary for a boundary. + + * Fixed multiple warnings on Solaris and/or 64bit builds. + + * Do not process subrequests in phase 2-4, but do hand off the request data. + + * Fixed a blocking FP in the multipart parser, which affected Safari. + + 11 Sep 2007 - 2.1.3 ------------------- From cd51a100469c394e8a6b1192ac918abafcb118e3 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 22:34:16 +0000 Subject: [PATCH 202/372] Allow all rules to run in phase 5. See #425. --- apache2/re.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/re.c b/apache2/re.c index 6b97dff1..4f878429 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -828,7 +828,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) } else if (rc == RULE_MATCH) { - if (msr->was_intercepted) { + if ((msr->phase == msr->intercept_phase) && msr->was_intercepted) { /* If the transaction was intercepted we will * go back. Do note that we are relying on the * rule to know if it is a part of a chain and From 476684e6ec779b87eef62fcfc5eb8c4921bc5cbd Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 22:45:01 +0000 Subject: [PATCH 203/372] Stricter configuration parsing. See #66 and #429. --- apache2/apache2_config.c | 24 +++++++++++++++++------- doc/modsecurity2-apache-reference.xml | 8 ++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 34cf8718..d4b2a4bd 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -554,21 +554,24 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * if (dcfg->tmp_default_actionset == NULL) return FATAL_ERROR; } - /* Merge actions with the parent. */ - rule->actionset = msre_actionset_merge(modsecurity->msre, dcfg->tmp_default_actionset, - rule->actionset, 1); + /* Check some cases prior to merging so we know where it came from */ + /* Must NOT specify a disruptive action in logging phase. */ + if (rule->actionset->phase == PHASE_LOGGING && (rule->actionset->intercept_action != ACTION_ALLOW && rule->actionset->intercept_action != ACTION_NONE)) { + return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions " + "cannot be specified in the logging phase. %d", rule->actionset->intercept_action); + } + + /* Check syntax for chained rules */ if (dcfg->tmp_chain_starter != NULL) { - /* This rule is part of a chain. */ - /* Must NOT specify a disruptive action. */ - if (rule->actionset->intercept_action == NOT_SET) { + if (rule->actionset->intercept_action != NOT_SET) { return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions can only " "be specified by chain starter rules."); } /* Must NOT specify a phase. */ - if (rule->actionset->phase == NOT_SET) { + if (rule->actionset->phase != NOT_SET) { return apr_psprintf(cmd->pool, "ModSecurity: Execution phases can only be " "specified by chain starter rules."); } @@ -593,6 +596,13 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, const char * rule->actionset->phase = rule->chain_starter->actionset->phase; } + /* Merge actions with the parent. + * + * ENH Probably do not want this done fully for chained rules. + */ + rule->actionset = msre_actionset_merge(modsecurity->msre, dcfg->tmp_default_actionset, + rule->actionset, 1); + if (rule->actionset->is_chained != 1) { /* If this rule is part of the chain but does * not want more rules to follow in the chain diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index 10dba0ae..5dadecf8 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -1090,7 +1090,9 @@ SecAuditLogStorageDir logs/audit Dependencies/Notes: Rules following a SecDefaultAction directive will inherit this setting unless a specific action is specified for an indivdual rule or until another - SecDefaultAction is specified. + SecDefaultAction is specified. Take special note that in the logging + disruptive actions are not allowed, but this can inadvertantly be + inherited using a disruptive action in SecDefaultAction. The default value is: @@ -2090,7 +2092,9 @@ SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1" + 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.
From 9136d391d3db6466a8195b9db1e915398dc32d8f Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 22:50:01 +0000 Subject: [PATCH 204/372] Forgoten CHANGES entry for last commit. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 9a4c876b..6823bcdf 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,11 @@ Changes since 2.5.0-dev2: + * Stricter configuration parsing. Disruptive actions, meta actions and + phases are no longer allowed in a chained rule. Disruptive actions, + are no longer allowed in a logging phase (phase 5) rule, including + inheriting from SecDefaultAction. + * More efficient collection persistance. * Fixed t:escapeSeqDecode to better follow ANSI C escapes. From 4602f7d908643c0860e7df8be156cd0587007deb Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 22:52:29 +0000 Subject: [PATCH 205/372] Remove tabs from CHANGES. --- CHANGES | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 6823bcdf..e03a5bff 100644 --- a/CHANGES +++ b/CHANGES @@ -5,8 +5,8 @@ Changes since 2.5.0-dev2: * Stricter configuration parsing. Disruptive actions, meta actions and phases are no longer allowed in a chained rule. Disruptive actions, - are no longer allowed in a logging phase (phase 5) rule, including - inheriting from SecDefaultAction. + are no longer allowed in a logging phase (phase 5) rule, including + inheriting from SecDefaultAction. * More efficient collection persistance. From 3c4eacf6f105c1d889732a27864eee00abdd5246 Mon Sep 17 00:00:00 2001 From: brectanus Date: Fri, 14 Dec 2007 23:23:46 +0000 Subject: [PATCH 206/372] Update Core Rules to those in 2.1.4. --- CHANGES | 3 + rules/CHANGELOG | 49 ++++- rules/LICENSE | 14 +- .../modsecurity_crs_40_generic_attacks.conf | 94 --------- rules/modsecurity_crs_10_config.conf | 8 +- ...odsecurity_crs_20_protocol_violations.conf | 42 ++-- ...modsecurity_crs_21_protocol_anomalies.conf | 36 ++-- rules/modsecurity_crs_23_request_limits.conf | 71 +++++++ rules/modsecurity_crs_30_http_policy.conf | 46 +---- rules/modsecurity_crs_35_bad_robots.conf | 16 +- rules/modsecurity_crs_40_generic_attacks.conf | 173 ++++++++++++----- rules/modsecurity_crs_45_trojans.conf | 10 +- rules/modsecurity_crs_50_outbound.conf | 42 ++-- ...odsecurity_crs_20_protocol_violations.conf | 42 ++-- ...modsecurity_crs_21_protocol_anomalies.conf | 24 +-- .../modsecurity_crs_40_generic_attacks.conf | 179 ++++++++++++++++++ .../modsecurity_crs_55_marketing.conf | 23 +++ 17 files changed, 577 insertions(+), 295 deletions(-) delete mode 100644 rules/blocking/modsecurity_crs_40_generic_attacks.conf create mode 100644 rules/modsecurity_crs_23_request_limits.conf rename rules/{blocking => optional_rules}/modsecurity_crs_20_protocol_violations.conf (68%) rename rules/{blocking => optional_rules}/modsecurity_crs_21_protocol_anomalies.conf (78%) create mode 100644 rules/optional_rules/modsecurity_crs_40_generic_attacks.conf create mode 100644 rules/optional_rules/modsecurity_crs_55_marketing.conf diff --git a/CHANGES b/CHANGES index e03a5bff..5dc22608 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,9 @@ Changes since 2.5.0-dev2: + * Updated included Core Ruleset to version 1.5 and noted in the docs that + XML support is required to use the rules without modification. + * Stricter configuration parsing. Disruptive actions, meta actions and phases are no longer allowed in a chained rule. Disruptive actions, are no longer allowed in a logging phase (phase 5) rule, including diff --git a/rules/CHANGELOG b/rules/CHANGELOG index 0dd2ae23..a94f9cb3 100644 --- a/rules/CHANGELOG +++ b/rules/CHANGELOG @@ -1,7 +1,45 @@ +------------------------ +Version 1.5 - 2007/11/23 +------------------------ --------------------------------- +New Rulesets: +- 23 - Request Limits + "Judging by appearances". This rulesets contains rules blocking based on + the size of the request, for example, a request with too many arguments + will be denied. + +Default policy changes: +- XML protection off by default +- BLOCKING dir renamed to optional_rules +- Ruleset 55 (marketing) is now optional (added to the optional_rules dir) +- Ruleset 21 - The exception for apache internal monitor will not log anymore + +New Events: +- 960912 - Invalid request body + Malformed content will not be parsed by modsecurity, but still there might + be applications that will parse it, ignoring the errors. +- 960913 - Invalid Request + Will trigger a security event when request was rejected by apache with + code 400, without going through ModSecurity rules. + +Additional rules logic: +- 950001 - New signature: delete from +- 950007 - New signature: waitfor delay + +False Positives Fixes: +- 950006 - Will not be looking for /cc pattern in User-Agent header +- 950002 - "Internet Explorer" signature removed +- Double decoding bug used to cause FPs. Some of the parameters are already + url-decoded by apache. This caused FPs when the rule performed another + url-decoding transformation. The rules have been split so that parameters + already decoded by apache will not be decoded by the rules anymore. +- 960911 - Expression is much more permissive now +- 950801 - Commented out entirely. NOTE: If your system uses UTF8 encoding, + then you should uncomment this rule (in file 20) + +-------------------------- version 1.4.3 - 2007/07/21 --------------------------------- +-------------------------- New Events: - 950012 - HTTP Request Smuggling @@ -25,6 +63,7 @@ Additional rules logic: this directives cannot be used to exclude phase 1 rules. Therefore we moved all inspection rules to phase 2. + -------------------------------- version 1.4 build 2 - 2007/05/17 -------------------------------- @@ -44,7 +83,7 @@ New Events: - 970018 - IIS installed in default location (any drive) Log once if IIS in installed in the /Inetpub directory (on any drive, not only C) - 950019 - Email Injection - Web forms used for sending mail (such as “tell a friend”) are often manipulated by spammers for sending anonymous emails + Web forms used for sending mail (such as "tell a friend") are often manipulated by spammers for sending anonymous emails Regular expressions fixes: - Further optimization of some regular expressions (using the non-greediness operator) @@ -115,8 +154,8 @@ Regular expressions fixes: - Command Injections now always require certain characters both before and after the command. Important since many are common English words (finger, mail) - The command injection wget is not searched in the UA header as it has different meaning there. - LDAP Fixed to reduce FPs: - + More accurate regular expressions - + high bit characters not accpeted between signature tokens. + + More accurate regular expressions + + high bit characters not accpeted between signature tokens. - Do not detect ',,id:'950009',severity:'2'" - -# Blind SQL injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|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)|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950007',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\b(?:benchmark|encode)\b" \ -# "chain,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950903',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "[\\(\)\%#]\|--" -SecRule REQUEST_FILENAME|ARGS|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:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950904',severity:'2'" - -# SQL injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|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|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r|autonomous_transaction|open(?:rowset|query)|1\s*=\s*1|dbms_java)\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*?\()|(?:having|or|and)\b\s+(?:\d{1,10}|[\'\"][^=]{1,10}[\'\"])\s*[=<>]+|print\b\W*?\@\@|cast\b\W*?\()|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950001',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950905',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "[\\(\)\%#]\|--" -SecRule REQUEST_FILENAME|ARGS|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:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950906',severity:'2'" -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer|!REQUEST_HEADERS:via "\b(?:coalesce\b|root\@)" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',,id:'950908',severity:'2'" - -# XSS -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|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)\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)|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*?\())|<(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\btype\b\W*?\bimage|script|meta)\b|!\[cdata\[)|(?:\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack. Matched signature <%{TX.0}>',,id:'950004',severity:'2'" - -# file injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/* "(?:\b(?:\.(?:ht(?:access|passwd|group)|www_?acl)|global\.asa|httpd\.conf|boot\.ini)\b|\/etc\/)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt. Matched signature <%{TX.0}>',,id:'950005',severity:'2'" - -# Command access -SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Access. Matched signature <%{TX.0}>',,id:'950002',severity:'2'" - -# Command injection -SecRule ARGS|ARGS_NAMES|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|c)|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|c)|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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection. Matched signature <%{TX.0}>',,id:'950006',severity:'2'" -SecRule "ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:User-Agent" \ - "\bwget\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection. Matched signature <%{TX.0}>',,id:'950907',severity:'2'" - -# Coldfusion injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags. Matched signature <%{TX.0}>',,id:'950008',severity:'2'" - -# LDAP injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|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,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack. Matched signature <%{TX.0}>',,id:'950010',severity:'2'" - -# SSI injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/* "