From 9d2b4568fcf8afb5f362dd2fdd3ca4229a0fa895 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 8 Jun 2012 16:54:15 +0000 Subject: [PATCH 01/99] 2.7.x From 51067c23dddc43f2d0d6af03ee7b3bcda283b993 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Thu, 14 Jun 2012 13:54:05 +0000 Subject: [PATCH 02/99] update CHANGES --- CHANGES | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index e9239d561b..a181be4056 100644 --- a/CHANGES +++ b/CHANGES @@ -91,9 +91,8 @@ * Added build system support for KfreeBSD and HURD. - * In 2009, Stefan Esser published an evasion technique that relies on the use of single quotes and PHP. - The trick was treating a request parameter as a file. A patch was applied into ModSecurity 2.5.11 by Brian Rectanus. - Ivan Ristic reported that the patch was imcomplete. We added extra checks for this evasion. + * Fixed a multipart bypass issue related to quote parsing + Credits to Qualys Vulnerability & Malware Research Labs (VMRL). 20 Mar 2012 - 2.6.5 ------------------- From 0ed1a1b8b411f575923ad240d9c5d17a9346bdb9 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 12:13:32 +0000 Subject: [PATCH 03/99] Fix loop into getkey --- apache2/msc_crypt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index 07ba5fd0dd..03aa33b014 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -132,7 +132,7 @@ unsigned char *getkey(apr_pool_t *mp) { srand((unsigned int) time(0)); while(length--) { - output[length-1] = (rand() % 94 + 33); + output[length] = (rand() % 94 + 33); srand(rand()); } From 517abc595b3ff6205b0d385f70d89f850a2d5f59 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 12:43:29 +0000 Subject: [PATCH 04/99] Improve random number generator --- apache2/msc_crypt.c | 10 ++++++++-- apache2/msc_crypt.h | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index 03aa33b014..89a8ed5c07 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -125,15 +125,21 @@ char *normalize_path(modsec_rec *msr, char *input) { */ unsigned char *getkey(apr_pool_t *mp) { unsigned short int length = 12; + struct glinear data; + uint64_t seed; char output[13]; char *key = NULL; output[length] = '\0'; - srand((unsigned int) time(0)); + seed = data.seed; + srand(data.seed); while(length--) { + seed *= data.mul; + seed += data.add; + data.seed = seed % data.mod; output[length] = (rand() % 94 + 33); - srand(rand()); + srand(data.seed + rand() + time(0)); } key = apr_psprintf(mp,"%s",output); diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index c5f6e8d8a7..3286fa8f83 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -27,6 +27,13 @@ #define INT32_MAX (2147483647) #endif +struct glinear { + uint32_t seed; + uint32_t mod; + uint32_t mul; + uint32_t add; +}; + char DSOLOCAL *hmac(modsec_rec *msr,const unsigned char *key, int key_len, char *msg, int msglen); unsigned char DSOLOCAL *do_hash_link(modsec_rec *msr, char *link, From 3caaf60fa89f37c3d09d7a290be612bc42f366d9 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 13:15:51 +0000 Subject: [PATCH 05/99] Improve random number generator --- apache2/msc_crypt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++- apache2/msc_crypt.h | 5 +++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index 89a8ed5c07..46b5d5916f 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -116,6 +116,58 @@ char *normalize_path(modsec_rec *msr, char *input) { return apr_pstrdup(msr->mp, input); } +/** + * \brief Create a random number + * + * \retval seed random seed + */ +unsigned long prng() { + short num_matrix1[10]; num_matrix2[10]; + unsigned long num, num1, num2; + short n, *p; + unsigned short seed_num; + unsigned long seed; + + seed_num = seed & 16BITS_MASK; + num = seed & 31BITS_MASK; + + p = num_matrix1; + + for(n = 18; n-- ; ) { + num = 30903*seed_num + (num>>16); + *p++ = seed_num = num & 16BITS_MASK; + if (n == 9) + p = num_matrix2; + } + + num_matrix1[0] &= 15BITS_MASK; + num_matrix2[0] &= 15BITS_MASK; + memcpy((char*)num_matrix1+2,(char*)num_matrix1+1,8*sizeof(short)); + memcpy((char*)num_matrix2+2,(char*)num_matrix2+1,8*sizeof(short)); + + num1 = num_matrix1[0]; + num2 = num_matrix2[0]; + + num1 += 1941 * num_matrix1[2] + 1860 * num_matrix1[3] + + 1812 * num_matrix1[4] + 1776 * num_matrix1[5] + + 1492 * num_matrix1[6] + 1215 * num_matrix1[7] + + 1066 * num_matrix1[8] + 12013 * num_matrix1[9]; + + num2 += 1111 * num_matrix2[2] + 2222 * num_matrix2[3] + + 3333 * num_matrix2[4] + 4444 * num_matrix2[5] + + 5555 * num_matrix2[6] + 6666 * num_matrix2[7] + + 7777 * num_matrix2[8] + 9272 * num_matrix2[9]; + + num_matrix1[0] = num1/16BITS_MASK; + num_matrix2[0] = num2/16BITS_MASK; + num_matrix1[1] = 16BITS_MASK&num1; + num_matrix2[1] = 16BITS_MASK&num2; + + seed = (((long)num_matrix1[1])<<16)+(long)num_matrix2[1]; + + return seed; +} + /** * \brief Create a random password * @@ -139,7 +191,7 @@ unsigned char *getkey(apr_pool_t *mp) { seed += data.add; data.seed = seed % data.mod; output[length] = (rand() % 94 + 33); - srand(data.seed + rand() + time(0)); + srand(data.seed + prng()); } key = apr_psprintf(mp,"%s",output); diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index 3286fa8f83..70192ecca6 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -34,6 +34,11 @@ struct glinear { uint32_t add; }; +#define 16BITS_MASK 65536L +#define 16BITS_MASK 0xFFFF +#define 15BITS_MASK 0x7FFF +#define 31BITS_MASK 0x7FFFFFFF + char DSOLOCAL *hmac(modsec_rec *msr,const unsigned char *key, int key_len, char *msg, int msglen); unsigned char DSOLOCAL *do_hash_link(modsec_rec *msr, char *link, From d203549171e82ceb2d9ad238654f23cbce935ffd Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 16:56:50 +0000 Subject: [PATCH 06/99] Improve random number generator --- apache2/msc_crypt.c | 18 +++++++++--------- apache2/msc_crypt.h | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index 46b5d5916f..fc02b98765 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -128,20 +128,20 @@ unsigned long prng() { unsigned short seed_num; unsigned long seed; - seed_num = seed & 16BITS_MASK; - num = seed & 31BITS_MASK; + seed_num = seed & N16BITS_MASK; + num = seed & N31BITS_MASK; p = num_matrix1; for(n = 18; n-- ; ) { num = 30903*seed_num + (num>>16); - *p++ = seed_num = num & 16BITS_MASK; + *p++ = seed_num = num & N16BITS_MASK; if (n == 9) p = num_matrix2; } - num_matrix1[0] &= 15BITS_MASK; - num_matrix2[0] &= 15BITS_MASK; + num_matrix1[0] &= N15BITS_MASK; + num_matrix2[0] &= N15BITS_MASK; memcpy((char*)num_matrix1+2,(char*)num_matrix1+1,8*sizeof(short)); memcpy((char*)num_matrix2+2,(char*)num_matrix2+1,8*sizeof(short)); @@ -158,10 +158,10 @@ unsigned long prng() { 5555 * num_matrix2[6] + 6666 * num_matrix2[7] + 7777 * num_matrix2[8] + 9272 * num_matrix2[9]; - num_matrix1[0] = num1/16BITS_MASK; - num_matrix2[0] = num2/16BITS_MASK; - num_matrix1[1] = 16BITS_MASK&num1; - num_matrix2[1] = 16BITS_MASK&num2; + num_matrix1[0] = num1/N16BITS_MASK; + num_matrix2[0] = num2/N16BITS_MASK; + num_matrix1[1] = N16BITS_MASK&num1; + num_matrix2[1] = N16BITS_MASK&num2; seed = (((long)num_matrix1[1])<<16)+(long)num_matrix2[1]; diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index 70192ecca6..d40d722567 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -34,10 +34,10 @@ struct glinear { uint32_t add; }; -#define 16BITS_MASK 65536L -#define 16BITS_MASK 0xFFFF -#define 15BITS_MASK 0x7FFF -#define 31BITS_MASK 0x7FFFFFFF +#define N16BITS_MASK 65536L +#define N16BITS_MASK 0xFFFF +#define N15BITS_MASK 0x7FFF +#define N31BITS_MASK 0x7FFFFFFF char DSOLOCAL *hmac(modsec_rec *msr,const unsigned char *key, int key_len, char *msg, int msglen); From a958dfd4957f2bd181d9d15a785fd5f317f4aea0 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 17:06:10 +0000 Subject: [PATCH 07/99] Fix PRNG code --- apache2/msc_crypt.c | 10 +++++----- apache2/msc_crypt.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index fc02b98765..e831f7292a 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -122,20 +122,20 @@ char *normalize_path(modsec_rec *msr, char *input) { * \retval seed random seed */ unsigned long prng() { - short num_matrix1[10]; num_matrix2[10]; + short num_matrix1[10], num_matrix2[10]; unsigned long num, num1, num2; short n, *p; unsigned short seed_num; unsigned long seed; - seed_num = seed & N16BITS_MASK; + seed_num = seed & N16BITS_MAX; num = seed & N31BITS_MASK; p = num_matrix1; for(n = 18; n-- ; ) { num = 30903*seed_num + (num>>16); - *p++ = seed_num = num & N16BITS_MASK; + *p++ = seed_num = num & N16BITS_MAX; if (n == 9) p = num_matrix2; } @@ -160,8 +160,8 @@ unsigned long prng() { num_matrix1[0] = num1/N16BITS_MASK; num_matrix2[0] = num2/N16BITS_MASK; - num_matrix1[1] = N16BITS_MASK&num1; - num_matrix2[1] = N16BITS_MASK&num2; + num_matrix1[1] = N16BITS_MAX & num1; + num_matrix2[1] = N16BITS_MAX & num2; seed = (((long)num_matrix1[1])<<16)+(long)num_matrix2[1]; diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index d40d722567..8bd74d6da3 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -35,7 +35,7 @@ struct glinear { }; #define N16BITS_MASK 65536L -#define N16BITS_MASK 0xFFFF +#define N16BITS_MAX 0xFFFF #define N15BITS_MASK 0x7FFF #define N31BITS_MASK 0x7FFFFFFF From 8bf46a3d87f1b9d8ea7e3de292f26e0fab968132 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 19:27:59 +0000 Subject: [PATCH 08/99] Fix PRNG code and windows compilation errors --- apache2/apache2_config.c | 5 +++-- apache2/msc_crypt.c | 20 +++----------------- apache2/msc_crypt.h | 7 ------- apache2/msc_tree.h | 33 ++++++++++++++++++++------------- 4 files changed, 26 insertions(+), 39 deletions(-) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 78f4666035..13393e5fee 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -686,6 +686,7 @@ void init_directory_config(directory_config *dcfg) /* Encryption */ if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = getkey(dcfg->mp); + if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = strlen(dcfg->crypto_key); if (dcfg->crypto_key_add == NOT_SET) dcfg->crypto_key_add = ENCRYPTION_KEYONLY; if (dcfg->crypto_param_name == NOT_SET_P) dcfg->crypto_param_name = "crypt"; if (dcfg->encryption_is_enabled == NOT_SET) dcfg->encryption_is_enabled = ENCRYPTION_DISABLED; @@ -2315,9 +2316,9 @@ static const char *cmd_encryption_key(cmd_parms *cmd, void *_dcfg, const char *_ char *p1 = NULL; if (dcfg == NULL) return NULL; + if (_p1 == NULL) return NULL; - if (p1 == NULL) return NULL; - if (strcasecmp(p1, "Rand") == 0) { + if (strcasecmp(_p1, "Rand") == 0) { p1 = apr_pstrdup(cmd->pool, getkey(cmd->pool)); dcfg->crypto_key = p1; dcfg->crypto_key_len = strlen(dcfg->crypto_key); diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index e831f7292a..1838bf9f80 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -177,24 +177,10 @@ unsigned long prng() { */ unsigned char *getkey(apr_pool_t *mp) { unsigned short int length = 12; - struct glinear data; - uint64_t seed; - char output[13]; - char *key = NULL; - - output[length] = '\0'; - - seed = data.seed; - srand(data.seed); - while(length--) { - seed *= data.mul; - seed += data.add; - data.seed = seed % data.mod; - output[length] = (rand() % 94 + 33); - srand(data.seed + prng()); - } + unsigned char *key = NULL; + unsigned long int seed = time(NULL); - key = apr_psprintf(mp,"%s",output); + key = apr_psprintf(mp,"%lu%lu",prng(),seed); return key; } diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index 8bd74d6da3..bf0cfc8ff7 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -27,13 +27,6 @@ #define INT32_MAX (2147483647) #endif -struct glinear { - uint32_t seed; - uint32_t mod; - uint32_t mul; - uint32_t add; -}; - #define N16BITS_MASK 65536L #define N16BITS_MAX 0xFFFF #define N15BITS_MASK 0x7FFF diff --git a/apache2/msc_tree.h b/apache2/msc_tree.h index 04d964449f..7c4d900905 100644 --- a/apache2/msc_tree.h +++ b/apache2/msc_tree.h @@ -16,6 +16,13 @@ #define __MSC_TREE_H__ #include "modsecurity.h" +#include + +typedef struct CPTData CPTData; +typedef struct TreePrefix TreePrefix; +typedef struct TreeNode TreeNode; +typedef struct CPTTree CPTTree; +typedef struct TreeRoot TreeRoot; #define IPV4_TREE 0x1 #define IPV6_TREE 0x2 @@ -37,36 +44,36 @@ #define NETMASK_4 0x4 #define NETMASK_2 0x2 -typedef struct CPTData_ { +struct CPTData { uint8_t netmask; - struct CPTData_ *next; -} CPTData; + struct CPTData *next; +}; -typedef struct TreePrefix_ { +struct TreePrefix { uint8_t *buffer; uint16_t bitlen; CPTData *prefix_data; -} TreePrefix; +}; -typedef struct TreeNode_ { +struct TreeNode { uint16_t bit; int count; uint8_t *netmasks; TreePrefix *prefix; - struct TreeNode_ *left, *right; - struct TreeNode_ *parent; -} TreeNode; + struct TreeNode *left, *right; + struct TreeNode *parent; +}; -typedef struct CPTTree_ { +struct CPTTree { int count; apr_pool_t *pool; TreeNode *head; -} CPTTree; +}; -typedef struct TreeRoot_ { +struct TreeRoot { CPTTree *ipv4_tree; CPTTree *ipv6_tree; -} TreeRoot; +}; DSOLOCAL CPTTree *CPTCreateRadixTree(apr_pool_t *pool); DSOLOCAL TreeNode *CPTIpMatch(modsec_rec *, uint8_t *, CPTTree *, int); From 88331d1221cadf3f00fe5fad6536e13d0e628b2c Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 19:59:16 +0000 Subject: [PATCH 09/99] Fix windows error compilation --- apache2/msc_tree.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c index 0451fab2da..3a3cded25d 100644 --- a/apache2/msc_tree.c +++ b/apache2/msc_tree.c @@ -16,7 +16,9 @@ #include #include #include +#if !defined(WIN32) || !defined(WINNT) #include +#endif #include "apr_lib.h" #include "msc_util.h" #include "msc_tree.h" From ab4d5acce61b219ffbc361d08c4d90109b608b99 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 20:27:21 +0000 Subject: [PATCH 10/99] Fix windows error compilation --- apache2/msc_crypt.c | 1 + apache2/msc_tree.h | 6 +++--- apache2/persist_dbm.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index 1838bf9f80..1ad85deeae 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -18,6 +18,7 @@ #include "acmp.h" #include "libxml/HTMLtree.h" #include "libxml/uri.h" +#include /** * \brief Normalize path in URI diff --git a/apache2/msc_tree.h b/apache2/msc_tree.h index 7c4d900905..96b9fa57d8 100644 --- a/apache2/msc_tree.h +++ b/apache2/msc_tree.h @@ -75,8 +75,8 @@ struct TreeRoot { CPTTree *ipv6_tree; }; -DSOLOCAL CPTTree *CPTCreateRadixTree(apr_pool_t *pool); -DSOLOCAL TreeNode *CPTIpMatch(modsec_rec *, uint8_t *, CPTTree *, int); -DSOLOCAL TreeNode *TreeAddIP(const char *, CPTTree *, int); +CPTTree DSOLOCAL *CPTCreateRadixTree(apr_pool_t *pool); +TreeNode DSOLOCAL *CPTIpMatch(modsec_rec *, uint8_t *, CPTTree *, int); +TreeNode DSOLOCAL *TreeAddIP(const char *, CPTTree *, int); #endif /*__MSC_TREE_H__ */ diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 215ebcda13..4bb6a3e169 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -586,7 +586,7 @@ int collections_remove_stale(modsec_rec *msr, const char *col_name) { goto error; } - if(strcasestr(col_name,"user") || strcasestr(col_name,"session") || strcasestr(col_name,"resource")) + if(strstr(col_name,"USER") || strstr(col_name,"SESSION") || strstr(col_name, "RESOURCE")) dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", msr->txcfg->webappid, "_", col_name, NULL); else dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL); From ead368268e1c6699c5d9439a5c27d7ada31f2fec Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 20:44:10 +0000 Subject: [PATCH 11/99] Add inet_pton for windows --- apache2/msc_util.c | 35 +++++++++++++++++++++++++++++++++++ apache2/msc_util.h | 4 ++++ 2 files changed, 39 insertions(+) diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 900da3635e..f8505eb8e5 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -570,6 +570,41 @@ char *file_basename(apr_pool_t *mp, const char *filename) { return d; } +#ifdef WIN32 +int inet_pton(int family, const char *src, void *dst) { + struct addrinfo addr; + struct sockaddr_in *in = NULL; + struct sockaddr_in6 *in6 = NULL; + struct addrinfo *addr_info = NULL; + + memset(&addr, 0, sizeof(struct addrinfo)); + addr.ai_family = family; + + if (getaddrinfo(src, NULL, &addr, &addr_info) != 0) + return -1; + + if (addr_info) { + if (addr_info->ai_family == AF_INET) { + in = (struct sockaddr_in*)addr_info->ai_addr; + memcpy(dst, &in->sin_addr, 4); + } + else if (addr_info->ai_family == AF_INET6) { + in6 = (struct sockaddr_in6*)addr_info->ai_addr; + memcpy(dst, &in6->sin6_addr, 16); + } + else { + freeaddrinfo(addr_info); + return -1; + } + + freeaddrinfo(addr_info); + return 1; + } + + return -1; +} +#endif + /** * */ diff --git a/apache2/msc_util.h b/apache2/msc_util.h index f32cce22bd..9a7b2fdd10 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -28,6 +28,10 @@ #include "modsecurity.h" #include "re.h" +#if WIN32 +int DSOLOCAL inet_pton(int family, const char *src, void *dst) +#endif + int DSOLOCAL normalize_path_inplace(unsigned char *input, int len, int win, int *changed); int DSOLOCAL parse_boolean(const char *input); From ad059c9e6c162fbf4826f1e3c01245fc54fd8c2f Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 20:45:54 +0000 Subject: [PATCH 12/99] Add inet_pton for windows --- apache2/msc_util.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apache2/msc_util.c b/apache2/msc_util.c index f8505eb8e5..ac4ecd4f54 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -586,11 +586,13 @@ int inet_pton(int family, const char *src, void *dst) { if (addr_info) { if (addr_info->ai_family == AF_INET) { in = (struct sockaddr_in*)addr_info->ai_addr; - memcpy(dst, &in->sin_addr, 4); + if(in != NULL) + memcpy(dst, &in->sin_addr, 4); } else if (addr_info->ai_family == AF_INET6) { in6 = (struct sockaddr_in6*)addr_info->ai_addr; - memcpy(dst, &in6->sin6_addr, 16); + if(in6 != NULL) + memcpy(dst, &in6->sin6_addr, 16); } else { freeaddrinfo(addr_info); From 6223499ddc26d316b9ab3b277044bc39713f025a Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 15 Jun 2012 20:58:35 +0000 Subject: [PATCH 13/99] Update CHANGES --- CHANGES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index a181be4056..ef30dac626 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,11 @@ +NN Jun 2012 - 2.7.0-rc2 +------------------- + + * Fix compilation errors unders Windows platform. + + * Fix SecEncryptionKey was not working as expected. + + 08 Jun 2012 - 2.7.0-rc1 ------------------- From 5b46e6c61411ce05baf75b8ddd5acb05a70c3bce Mon Sep 17 00:00:00 2001 From: brenosilva Date: Sat, 16 Jun 2012 16:31:09 +0000 Subject: [PATCH 14/99] Fix code for windows --- apache2/msc_util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 9a7b2fdd10..a7b765d51a 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -28,8 +28,8 @@ #include "modsecurity.h" #include "re.h" -#if WIN32 -int DSOLOCAL inet_pton(int family, const char *src, void *dst) +#ifdef WIN32 +int DSOLOCAL inet_pton(int family, const char *src, void *dst); #endif int DSOLOCAL normalize_path_inplace(unsigned char *input, int len, int win, int *changed); From 2791e7766ba69a0de0d524ac7a504bfe195f65a0 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Sat, 16 Jun 2012 19:33:22 +0000 Subject: [PATCH 15/99] Fix code for windows --- apache2/msc_tree.c | 64 +++++++++++++++++++++--------------------- apache2/msc_tree.h | 17 ++++++----- apache2/msc_util.c | 18 ++++++++++++ apache2/msc_util.h | 2 ++ apache2/re_operators.c | 4 +-- 5 files changed, 62 insertions(+), 43 deletions(-) diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c index 3a3cded25d..8dae4e243a 100644 --- a/apache2/msc_tree.c +++ b/apache2/msc_tree.c @@ -37,7 +37,7 @@ CPTTree *CPTCreateRadixTree(apr_pool_t *pool) { return tree; } -void ConvertIPNetmask(uint8_t *buffer, uint8_t netmask, uint16_t ip_bitmask) { +void ConvertIPNetmask(unsigned char *buffer, unsigned char netmask, unsigned int ip_bitmask) { int aux = 0, bytes = 0; int mask = 0, mask_bit = 0; @@ -72,7 +72,7 @@ TreeNode *CPTCreateNode(apr_pool_t *pool) { return node; } -CPTData *CPTCreateCPTData(uint8_t netmask, apr_pool_t *pool) { +CPTData *CPTCreateCPTData(unsigned char netmask, apr_pool_t *pool) { CPTData *prefix_data = apr_palloc(pool, sizeof(CPTData)); @@ -87,8 +87,8 @@ CPTData *CPTCreateCPTData(uint8_t netmask, apr_pool_t *pool) { return prefix_data; } -TreePrefix *InsertDataPrefix(TreePrefix *prefix, uint8_t *ipdata, uint16_t ip_bitmask, - uint8_t netmask, apr_pool_t *pool) { +TreePrefix *InsertDataPrefix(TreePrefix *prefix, unsigned char *ipdata, unsigned int ip_bitmask, + unsigned char netmask, apr_pool_t *pool) { if(prefix == NULL) return NULL; @@ -104,8 +104,8 @@ TreePrefix *InsertDataPrefix(TreePrefix *prefix, uint8_t *ipdata, uint16_t ip_bi return prefix; } -TreePrefix *CPTCreatePrefix(uint8_t *ipdata, uint16_t ip_bitmask, - uint8_t netmask, apr_pool_t *pool) { +TreePrefix *CPTCreatePrefix(unsigned char *ipdata, unsigned int ip_bitmask, + unsigned char netmask, apr_pool_t *pool) { TreePrefix *prefix = NULL; int bytes = ip_bitmask/8; @@ -162,7 +162,7 @@ void CPTAppendToCPTDataList(CPTData *new, CPTData **list) { return; } -int TreePrefixContainNetmask(TreePrefix *prefix, uint8_t netmask) { +int TreePrefixContainNetmask(TreePrefix *prefix, unsigned char netmask) { CPTData *prefix_data = NULL; if (prefix == NULL) { @@ -179,7 +179,7 @@ int TreePrefixContainNetmask(TreePrefix *prefix, uint8_t netmask) { return 0; } -int CheckBitmask(uint8_t netmask, uint16_t ip_bitmask) { +int CheckBitmask(unsigned char netmask, unsigned int ip_bitmask) { switch(netmask) { @@ -198,7 +198,7 @@ int CheckBitmask(uint8_t netmask, uint16_t ip_bitmask) { return 0; } -TreeNode *CPTCreateHead(TreePrefix *prefix, TreeNode *node, CPTTree *tree, uint8_t netmask, uint16_t ip_bitmask) { +TreeNode *CPTCreateHead(TreePrefix *prefix, TreeNode *node, CPTTree *tree, unsigned char netmask, unsigned int ip_bitmask) { if(tree == NULL) return NULL; @@ -216,7 +216,7 @@ TreeNode *CPTCreateHead(TreePrefix *prefix, TreeNode *node, CPTTree *tree, uint8 return node; node->count++; - node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(uint8_t))); + node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char))); if(node->netmasks) node->netmasks[0] = netmask; @@ -243,7 +243,7 @@ TreeNode *SetParentNode(TreeNode *node, TreeNode *new_node, CPTTree *tree) { } int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node, - CPTTree *tree, uint8_t netmask, uint8_t bitlen) { + CPTTree *tree, unsigned char netmask, unsigned char bitlen) { int i; if (netmask != NETMASK_256-1 && netmask != NETMASK_128) { @@ -258,7 +258,7 @@ int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node, } node->count++; - node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(uint8_t))); + node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char))); if(node->netmasks == NULL) return 0; @@ -286,12 +286,12 @@ int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node, return 0; } -TreeNode *CPTAddElement(uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree, uint8_t netmask) { - uint8_t *buffer = NULL; - uint8_t bitlen = 0; +TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree, unsigned char netmask) { + unsigned char *buffer = NULL; + unsigned char bitlen = 0; int bit_validation = 0, test_bit = 0; int i = 0, j = 0, temp = 0; - uint16_t x, y; + unsigned int x, y; TreeNode *node = NULL, *new_node = NULL; TreeNode *parent = NULL, *i_node = NULL; TreeNode *bottom_node = NULL; @@ -407,7 +407,7 @@ TreeNode *CPTAddElement(uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree, uin node->count++; new_node = node; - node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(uint8_t))); + node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char))); if ((node->count -1) == 0) { node->netmasks[0] = netmask; @@ -474,7 +474,7 @@ TreeNode *CPTAddElement(uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree, uin i++; } - i_node->netmasks = apr_palloc(tree->pool, (node->count - i) * sizeof(uint8_t)); + i_node->netmasks = apr_palloc(tree->pool, (node->count - i) * sizeof(unsigned char)); if(i_node->netmasks == NULL) { return NULL; @@ -514,7 +514,7 @@ TreeNode *CPTAddElement(uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree, uin return new_node; } -int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, uint16_t netmask) { +int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, unsigned int netmask) { for(prefix_data != NULL; ; prefix_data = prefix_data->next) { if (prefix_data->netmask == netmask) { @@ -525,7 +525,7 @@ int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, uint16_t netmask) return 0; } -int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, uint16_t netmask, int flag) { +int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, unsigned int netmask, int flag) { CPTData *prefix_data = NULL; int ret = 0; @@ -564,8 +564,8 @@ int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, uint16_t netmask, int return ret; } -TreeNode *CPTRetriveNode(modsec_rec *msr, uint8_t *buffer, uint16_t ip_bitmask, TreeNode *node) { - uint16_t x, y; +TreeNode *CPTRetriveNode(modsec_rec *msr, unsigned char *buffer, unsigned int ip_bitmask, TreeNode *node) { + unsigned int x, y; if(node == NULL) { if (msr->txcfg->debuglog_level >= 9) { @@ -610,7 +610,7 @@ TreeNode *CPTRetriveParentNode(TreeNode *node) { return node; } -TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, uint8_t *ipdata, uint8_t ip_bitmask, TreeNode *node) { +TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsigned char ip_bitmask, TreeNode *node) { TreeNode *netmask_node = NULL; int mask = 0, bytes = 0; int i = 0, j = 0; @@ -690,10 +690,10 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, uint8_t *ipdata, uint8_t ip_ return CPTFindElementIPNetblock(msr, ipdata, ip_bitmask, netmask_node->parent); } -TreeNode *CPTFindElement(modsec_rec *msr, uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree) { +TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree) { TreeNode *node = NULL; int mask = 0, bytes = 0; - uint8_t temp_data[NETMASK_256-1]; + unsigned char temp_data[NETMASK_256-1]; if (tree == NULL) { if (msr->txcfg->debuglog_level >= 9) { @@ -771,7 +771,7 @@ TreeNode *CPTFindElement(modsec_rec *msr, uint8_t *ipdata, uint16_t ip_bitmask, return CPTFindElementIPNetblock(msr, temp_data, ip_bitmask, node); } -TreeNode *CPTIpMatch(modsec_rec *msr, uint8_t *ipdata, CPTTree *tree, int type) { +TreeNode *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type) { if(tree == NULL) { if (msr->txcfg->debuglog_level >= 9) { @@ -807,8 +807,8 @@ TreeNode *CPTIpMatch(modsec_rec *msr, uint8_t *ipdata, CPTTree *tree, int type) } TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { - uint32_t ip, ret; - uint8_t netmask_v4 = NETMASK_32, netmask_v6 = NETMASK_128; + unsigned long ip, ret; + unsigned char netmask_v4 = NETMASK_32, netmask_v6 = NETMASK_128; char ip_strv4[NETMASK_32], ip_strv6[NETMASK_128]; struct in_addr addr4; struct in6_addr addr6; @@ -820,7 +820,7 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { switch(type) { case IPV4_TREE: - bzero(&addr4, sizeof(addr4)); + memset(&addr4, 0, sizeof(addr4)); memset(ip_strv4, 0x0, NETMASK_32); strncpy(ip_strv4, buffer, sizeof(ip_strv4) - 2); @@ -849,10 +849,10 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { tree->count++; - return CPTAddElement((uint8_t *)&ip, NETMASK_32, tree, netmask_v4); + return CPTAddElement((unsigned char *)&ip, NETMASK_32, tree, netmask_v4); case IPV6_TREE: - bzero(&addr6, sizeof(addr6)); + memset(&addr6, 0, sizeof(addr6)); memset(ip_strv6, 0x0, NETMASK_128); strncpy(ip_strv6, buffer, sizeof(ip_strv6) - 2); @@ -878,7 +878,7 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { tree->count++; - return CPTAddElement((uint8_t *)&addr6.s6_addr, NETMASK_128, tree, netmask_v6); + return CPTAddElement((unsigned char *)&addr6.s6_addr, NETMASK_128, tree, netmask_v6); default: return NULL; } diff --git a/apache2/msc_tree.h b/apache2/msc_tree.h index 96b9fa57d8..6bda085fe5 100644 --- a/apache2/msc_tree.h +++ b/apache2/msc_tree.h @@ -16,7 +16,6 @@ #define __MSC_TREE_H__ #include "modsecurity.h" -#include typedef struct CPTData CPTData; typedef struct TreePrefix TreePrefix; @@ -45,20 +44,20 @@ typedef struct TreeRoot TreeRoot; #define NETMASK_2 0x2 struct CPTData { - uint8_t netmask; + unsigned char netmask; struct CPTData *next; }; struct TreePrefix { - uint8_t *buffer; - uint16_t bitlen; + unsigned char *buffer; + unsigned int bitlen; CPTData *prefix_data; }; struct TreeNode { - uint16_t bit; + unsigned int bit; int count; - uint8_t *netmasks; + unsigned char *netmasks; TreePrefix *prefix; struct TreeNode *left, *right; struct TreeNode *parent; @@ -75,8 +74,8 @@ struct TreeRoot { CPTTree *ipv6_tree; }; -CPTTree DSOLOCAL *CPTCreateRadixTree(apr_pool_t *pool); -TreeNode DSOLOCAL *CPTIpMatch(modsec_rec *, uint8_t *, CPTTree *, int); -TreeNode DSOLOCAL *TreeAddIP(const char *, CPTTree *, int); +CPTTree DSOLOCAL *CPTCreateRadixTree(apr_pool_t *pool); +TreeNode DSOLOCAL *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type); +TreeNode DSOLOCAL *TreeAddIP(const char *buffer, CPTTree *tree, int type); #endif /*__MSC_TREE_H__ */ diff --git a/apache2/msc_util.c b/apache2/msc_util.c index ac4ecd4f54..a3ecc7010d 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -571,6 +571,24 @@ char *file_basename(apr_pool_t *mp, const char *filename) { } #ifdef WIN32 +char *strcasestr(const char *haystack, const char *needle) { + char aux, lower_aux; + int length; + + if ((aux = *needle++) != 0) { + aux = (char)tolower((unsigned char)aux); + length = strlen(needle); + do { + do { + if ((lower_aux = *haystack++) == 0) + return NULL; + } while ((char)tolower((unsigned char)lower_aux) != aux); + } while (strncasecmp(haystack, needle, length) != 0); + haystack--; + } + return ((char *)haystack); +} + int inet_pton(int family, const char *src, void *dst) { struct addrinfo addr; struct sockaddr_in *in = NULL; diff --git a/apache2/msc_util.h b/apache2/msc_util.h index a7b765d51a..80c8cadf68 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -29,7 +29,9 @@ #include "re.h" #ifdef WIN32 +#include int DSOLOCAL inet_pton(int family, const char *src, void *dst); +char DSOLOCAL *strcasestr(const char *haystack, const char *needle); #endif int DSOLOCAL normalize_path_inplace(unsigned char *input, int len, int win, int *changed); diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 86d91dc885..f12a8c9613 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -333,7 +333,7 @@ static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msr return 0; } - if (CPTIpMatch(msr, (uint8_t *)&in.s_addr, rtree->ipv4_tree, IPV4_TREE) != NULL) { + if (CPTIpMatch(msr, (unsigned char *)&in.s_addr, rtree->ipv4_tree, IPV4_TREE) != NULL) { *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name); return 1; } @@ -347,7 +347,7 @@ static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msr return 0; } - if (CPTIpMatch(msr, (uint8_t *)&in6.s6_addr, rtree->ipv6_tree, IPV6_TREE) != NULL) { + if (CPTIpMatch(msr, (unsigned char *)&in6.s6_addr, rtree->ipv6_tree, IPV6_TREE) != NULL) { *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name); return 1; } From 89fb40248a1f4b3bec4e271113874991b696ca8f Mon Sep 17 00:00:00 2001 From: brenosilva Date: Sat, 16 Jun 2012 19:41:18 +0000 Subject: [PATCH 16/99] change release to 2.7.0-rc2 --- apache2/msc_release.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/msc_release.h b/apache2/msc_release.h index b99585fc38..b2dbc9aa24 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -40,7 +40,7 @@ #define MODSEC_VERSION_MINOR "7" #define MODSEC_VERSION_MAINT "0" #define MODSEC_VERSION_TYPE "-rc" -#define MODSEC_VERSION_RELEASE "1" +#define MODSEC_VERSION_RELEASE "2" #define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE From f896591fc34b7e85477b45c54785e053638bd434 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Sun, 17 Jun 2012 13:33:07 +0000 Subject: [PATCH 17/99] Fix warnings --- apache2/modsecurity.h | 2 +- apache2/msc_crypt.c | 47 ++++++++++++++++++----------------- apache2/msc_crypt.h | 8 +++--- apache2/msc_multipart.c | 2 +- apache2/msc_release.h | 2 +- apache2/msc_tree.h | 4 +-- apache2/re.c | 4 +-- apache2/re_actions.c | 4 +++ apache2/re_operators.c | 55 ++++++++++++++++++++++++++++++++--------- 9 files changed, 82 insertions(+), 46 deletions(-) diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 30661a8fac..81317d1843 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -569,7 +569,7 @@ struct directory_config { /* Encryption */ apr_array_header_t *encryption_method; - const char *crypto_key; + const char *crypto_key; int crypto_key_len; const char *crypto_param_name; int encryption_is_enabled; diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index 1ad85deeae..49cfe51c3c 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -70,7 +70,7 @@ char *normalize_path(modsec_rec *msr, char *input) { xmlNormalizeURIPath(uri->path); Uri = apr_pstrdup(msr->mp, uri->path); - for(i = 0; i < strlen(Uri); i++) { + for(i = 0; i < (int)strlen(Uri); i++) { if(Uri[i] != '.' && Uri[i] != '/') { if (i - 1 < 0) i = 0; @@ -84,7 +84,7 @@ char *normalize_path(modsec_rec *msr, char *input) { } } - if(bytes >= strlen(uri->path)) + if(bytes >= (int)strlen(uri->path)) return NULL; content = apr_psprintf(msr->mp, "%s", uri->path+bytes); @@ -126,8 +126,8 @@ unsigned long prng() { short num_matrix1[10], num_matrix2[10]; unsigned long num, num1, num2; short n, *p; - unsigned short seed_num; - unsigned long seed; + unsigned short seed_num = 0; + unsigned long seed = 0; seed_num = seed & N16BITS_MAX; num = seed & N31BITS_MASK; @@ -176,9 +176,8 @@ unsigned long prng() { * * \retval key random key */ -unsigned char *getkey(apr_pool_t *mp) { - unsigned short int length = 12; - unsigned char *key = NULL; +char *getkey(apr_pool_t *mp) { + char *key = NULL; unsigned long int seed = time(NULL); key = apr_psprintf(mp,"%lu%lu",prng(),seed); @@ -197,8 +196,8 @@ unsigned char *getkey(apr_pool_t *mp) { * * \retval hex_digest The MAC */ -char *hmac(modsec_rec *msr, const unsigned char *key, int key_len, - char *msg, int msglen) { +char *hmac(modsec_rec *msr, const char *key, int key_len, + unsigned char *msg, int msglen) { apr_sha1_ctx_t ctx; unsigned char digest[APR_SHA1_DIGESTSIZE]; unsigned char hmac_ipad[HMAC_PAD_SIZE], hmac_opad[HMAC_PAD_SIZE]; @@ -294,7 +293,7 @@ int init_response_body_html_parser(modsec_rec *msr) { msr_log(msr, 4, "init_response_body_html_parser: assuming ISO-8859-1."); msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL, "ISO-8859-1", HTML_PARSE_RECOVER | HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, "ISO-8859-1"); + htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *) "ISO-8859-1"); } else{ charset+=8; @@ -310,7 +309,7 @@ int init_response_body_html_parser(modsec_rec *msr) { "init_response_body_html_parser: Charset[%s]",charset); msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL, charset, HTML_PARSE_RECOVER| HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, charset); + htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *)charset); if(final_charset != NULL) *final_charset=sep; } @@ -320,7 +319,7 @@ int init_response_body_html_parser(modsec_rec *msr) { msr_log(msr, 4,"init_response_body_html_parser: Enconding[%s].",msr->r->content_encoding); msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL, msr->r->content_encoding, HTML_PARSE_RECOVER | HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING); - htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, msr->r->content_encoding); + htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *)msr->r->content_encoding); } if(msr->crypto_html_tree == NULL){ if (msr->txcfg->debuglog_level >= 4) @@ -356,7 +355,6 @@ int do_encryption_method(modsec_rec *msr, char *link, int type) { char *my_error_msg = NULL; int ovector[33]; int rc; - const char *ret; if(msr == NULL) return -1; @@ -997,8 +995,7 @@ int inject_encrypted_response_body(modsec_rec *msr, int elts) { char *p = NULL; const char *ctype = NULL; const char *encoding = NULL; - char* new_ct = NULL; - int rc = 0; + char *new_ct = NULL, *content_value = NULL; if(msr == NULL) return -1; @@ -1081,7 +1078,7 @@ int inject_encrypted_response_body(modsec_rec *msr, int elts) { } msr->stream_output_length = output_buf->buffer->use; - msr->stream_output_data = (unsigned char *)malloc(msr->stream_output_length+1); + msr->stream_output_data = (char *)malloc(msr->stream_output_length+1); if (msr->stream_output_data == NULL) { xmlOutputBufferClose(output_buf); @@ -1110,7 +1107,7 @@ int inject_encrypted_response_body(modsec_rec *msr, int elts) { } msr->stream_output_length = output_buf->conv->use; - msr->stream_output_data = (unsigned char *)malloc(msr->stream_output_length+1); + msr->stream_output_data = (char *)malloc(msr->stream_output_length+1); if (msr->stream_output_data == NULL) { xmlOutputBufferClose(output_buf); @@ -1128,8 +1125,12 @@ int inject_encrypted_response_body(modsec_rec *msr, int elts) { xmlOutputBufferClose(output_buf); + content_value = (char*)apr_psprintf(msr->mp, "%"APR_SIZE_T_FMT, msr->stream_output_length); apr_table_unset(msr->r->headers_out,"Content-Length"); - apr_table_set(msr->r->headers_out, "Content-Length",(char*)apr_psprintf(msr->mp, APR_SIZE_T_FMT, msr->stream_output_length)); + + if (msr->txcfg->debuglog_level >= 4) + msr_log(msr, 4, "inject_encrypted_response_body: Setting new content value %s", content_value); + apr_table_set(msr->r->headers_out, "Content-Length", content_value); xmlFreeDoc(msr->crypto_html_tree); @@ -1149,10 +1150,10 @@ int inject_encrypted_response_body(modsec_rec *msr, int elts) { * \retval mac_link MACed link * \retval NULL on fail */ -unsigned char *do_hash_link(modsec_rec *msr, char *link, int type) { - unsigned char *mac_link = NULL; +char *do_hash_link(modsec_rec *msr, char *link, int type) { + char *mac_link = NULL; char *path_chunk = NULL; - unsigned char *hash_value = NULL; + char *hash_value = NULL; char *qm = NULL; if(msr == NULL) return NULL; @@ -1339,10 +1340,10 @@ unsigned char *do_hash_link(modsec_rec *msr, char *link, int type) { qm = strchr((char*)link,'?'); if(qm == NULL){ - mac_link= (unsigned char*)apr_psprintf(msr->mp, "%s?%s=%s", link, msr->txcfg->crypto_param_name, (char *)hash_value); + mac_link= (char*)apr_psprintf(msr->mp, "%s?%s=%s", link, msr->txcfg->crypto_param_name, (char *)hash_value); } else{ - mac_link= (unsigned char*)apr_psprintf(msr->mp, "%s&%s=%s", link, msr->txcfg->crypto_param_name, (char*)hash_value); + mac_link= (char*)apr_psprintf(msr->mp, "%s&%s=%s", link, msr->txcfg->crypto_param_name, (char*)hash_value); } return mac_link; diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index bf0cfc8ff7..94bb99319a 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -32,11 +32,11 @@ #define N15BITS_MASK 0x7FFF #define N31BITS_MASK 0x7FFFFFFF -char DSOLOCAL *hmac(modsec_rec *msr,const unsigned char *key, int key_len, - char *msg, int msglen); -unsigned char DSOLOCAL *do_hash_link(modsec_rec *msr, char *link, +char DSOLOCAL *hmac(modsec_rec *msr, const char *key, int key_len, + unsigned char *msg, int msglen); +char DSOLOCAL *do_hash_link(modsec_rec *msr, char *link, int type); -unsigned char DSOLOCAL *getkey(apr_pool_t *mp); +char DSOLOCAL *getkey(apr_pool_t *mp); int DSOLOCAL init_response_body_html_parser(modsec_rec *msr); int DSOLOCAL encrypt_response_body_links(modsec_rec *msr); diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 35eb6174c3..ae51895313 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -20,7 +20,7 @@ #include "msc_util.h" #include "msc_parsers.h" -void validate_quotes(modsec_rec *msr, unsigned char *data) { +void validate_quotes(modsec_rec *msr, char *data) { int i, len; if(msr == NULL) diff --git a/apache2/msc_release.h b/apache2/msc_release.h index b2dbc9aa24..b99585fc38 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -40,7 +40,7 @@ #define MODSEC_VERSION_MINOR "7" #define MODSEC_VERSION_MAINT "0" #define MODSEC_VERSION_TYPE "-rc" -#define MODSEC_VERSION_RELEASE "2" +#define MODSEC_VERSION_RELEASE "1" #define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE diff --git a/apache2/msc_tree.h b/apache2/msc_tree.h index 6bda085fe5..a6df488fdb 100644 --- a/apache2/msc_tree.h +++ b/apache2/msc_tree.h @@ -31,8 +31,8 @@ typedef struct TreeRoot TreeRoot; #define TREE_CHECK(x, y) ((x) & (y)) #define MASK_BITS(x) ((x + 1) * 8) -#define SHIFT_LEFT_MASK(x) (-1 << x) -#define SHIFT_RIGHT_MASK(x,y) (x >> y) +#define SHIFT_LEFT_MASK(x) ((-1) << (x)) +#define SHIFT_RIGHT_MASK(x,y) ((x) >> (y)) #define NETMASK_256 0x100 #define NETMASK_128 0x80 diff --git a/apache2/re.c b/apache2/re.c index 677bef0841..1b94fffe3f 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1459,7 +1459,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re int do_process = 1; const char *range = NULL; rule_exception *re = NULL; - char *my_error_msg, *error_msg; + char *my_error_msg; const apr_array_header_t *tag_tarr = NULL; const apr_table_entry_t *tag_telts = NULL; @@ -2165,7 +2165,7 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, int type, msre_rule *rule; char *my_error_msg; const char *argsp; - int rc, idx; + int rc; if (error_msg == NULL) return NULL; *error_msg = NULL; diff --git a/apache2/re_actions.c b/apache2/re_actions.c index 5b11490523..9225712959 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -990,6 +990,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set EncryptionEnforcement to %s.", value); } + return 1; } else if (strcasecmp(name, "EncryptionEngine") == 0) { if (strcasecmp(value, "on") == 0) { @@ -1003,6 +1004,7 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: Set EncryptionEngine to %s.", value); } + return 1; } else if (strcasecmp(name, "ruleRemoveById") == 0) { *(const char **)apr_array_push(msr->removed_rules) = (const char *)apr_pstrdup(msr->mp, value); @@ -1274,6 +1276,8 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name); return -1; } + + return -1; } /* xmlns */ diff --git a/apache2/re_operators.c b/apache2/re_operators.c index f12a8c9613..39bf5ad1c5 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -304,8 +304,6 @@ static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { TreeRoot *rtree = rule->op_param_data; - TreeNode *node; - apr_sockaddr_t *sa; struct in_addr in; struct in6_addr in6; @@ -736,7 +734,11 @@ static int msre_op_validateEncryption_param_init(msre_rule *rule, char **error_m int erroffset; msc_regex_t *regex; const char *pattern = rule->op_param; + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT int rc, jit; + #endif + #endif if (error_msg == NULL) return -1; *error_msg = NULL; @@ -795,10 +797,14 @@ static int msre_op_validateEncryption_execute(modsec_rec *msr, msre_rule *rule, unsigned int target_length; char *my_error_msg = NULL; int ovector[33]; - int rc, jit; - char *qspos = NULL; - const char *parm = NULL, *pattern = NULL; - msc_parm *mparm = NULL; + int rc; + const char *pattern = NULL; + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT + int jit; + #endif + #endif + if (error_msg == NULL) return -1; *error_msg = NULL; @@ -968,7 +974,11 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { int erroffset; msc_regex_t *regex; const char *pattern = rule->op_param; + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT int rc, jit; + #endif + #endif if (error_msg == NULL) return -1; *error_msg = NULL; @@ -1019,10 +1029,16 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c int capture = 0; int matched_bytes = 0; int matched = 0; - int rc, jit; + int rc; char *qspos = NULL; const char *parm = NULL, *pattern = NULL; msc_parm *mparm = NULL; + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT + int jit; + #endif + #endif + if (error_msg == NULL) return -1; *error_msg = NULL; @@ -2644,14 +2660,18 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * unsigned int target_length; char *my_error_msg = NULL; int ovector[33]; - int rc, jit; + int rc; int is_cc = 0; int offset; int matched_bytes = 0; char *qspos = NULL; const char *parm = NULL; msc_parm *mparm = NULL; - + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT + int jit; + #endif + #endif if (error_msg == NULL) return -1; *error_msg = NULL; @@ -2946,13 +2966,19 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var unsigned int target_length; char *my_error_msg = NULL; int ovector[33]; - int rc, jit; + int rc; int is_cpf = 0; int offset; int matched_bytes = 0; char *qspos = NULL; const char *parm = NULL; msc_parm *mparm = NULL; + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT + int jit; + #endif + #endif + if (error_msg == NULL) return -1; *error_msg = NULL; @@ -3235,13 +3261,19 @@ static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var unsigned int target_length; char *my_error_msg = NULL; int ovector[33]; - int rc, jit; + int rc; int is_ssn = 0; int offset; int matched_bytes = 0; char *qspos = NULL; const char *parm = NULL; msc_parm *mparm = NULL; + #ifdef WITH_PCRE_STUDY + #ifdef WITH_PCRE_JIT + int jit; + #endif + #endif + if (error_msg == NULL) return -1; *error_msg = NULL; @@ -3534,7 +3566,6 @@ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, unsigned int high8bits = 0; char *name_to_check = NULL; char *target = NULL; - char *target2 = NULL; apr_sockaddr_t *sa = NULL; apr_status_t rc; int capture = 0; From 027ae6eea1f3f12fea79a65297d98a76c06f4139 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Sun, 17 Jun 2012 13:49:33 +0000 Subject: [PATCH 18/99] Fix warnings --- apache2/msc_tree.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c index 8dae4e243a..6dab16fc4b 100644 --- a/apache2/msc_tree.c +++ b/apache2/msc_tree.c @@ -18,6 +18,7 @@ #include #if !defined(WIN32) || !defined(WINNT) #include +#include #endif #include "apr_lib.h" #include "msc_util.h" @@ -171,9 +172,10 @@ int TreePrefixContainNetmask(TreePrefix *prefix, unsigned char netmask) { prefix_data = prefix->prefix_data; - for(prefix_data != NULL; ; prefix_data = prefix_data->next) { + while (prefix_data != NULL) { if (prefix_data->netmask == netmask) return 1; + prefix_data = prefix_data->next; } return 0; @@ -386,9 +388,10 @@ TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree prefix_data = node->prefix->prefix_data; - for(prefix_data != NULL; ; prefix_data = prefix_data->next) { + while(prefix_data != NULL) { if (prefix_data->netmask == netmask) ++found; + prefix_data = prefix_data->next; } if (found != 0) { @@ -516,10 +519,11 @@ TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, unsigned int netmask) { - for(prefix_data != NULL; ; prefix_data = prefix_data->next) { + while(prefix_data != NULL) { if (prefix_data->netmask == netmask) { return 1; } + prefix_data = prefix_data->next; } return 0; From ef64c707037d8e19e706dca6955995f697845121 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 22 Jun 2012 03:15:49 +0000 Subject: [PATCH 19/99] Release 2.7.0-rc2 --- CHANGES | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index ef30dac626..d8f1604ad8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,9 @@ -NN Jun 2012 - 2.7.0-rc2 +22 Jun 2012 - 2.7.0-rc2 ------------------- - * Fix compilation errors unders Windows platform. - - * Fix SecEncryptionKey was not working as expected. + * Fixed compilation errors and warnings under Windows platform. + * Fixed SecEncryptionKey was not working as expected. 08 Jun 2012 - 2.7.0-rc1 ------------------- From 9c73ad68e6410f6d9c5f033fa9c94b1bf1cf598d Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 22 Jun 2012 03:16:16 +0000 Subject: [PATCH 20/99] Release 2.7.0-rc2 --- apache2/msc_release.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/msc_release.h b/apache2/msc_release.h index b99585fc38..b2dbc9aa24 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -40,7 +40,7 @@ #define MODSEC_VERSION_MINOR "7" #define MODSEC_VERSION_MAINT "0" #define MODSEC_VERSION_TYPE "-rc" -#define MODSEC_VERSION_RELEASE "1" +#define MODSEC_VERSION_RELEASE "2" #define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE From 2e055003c2a0649f48168599659587480f8e269a Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 22 Jun 2012 12:58:46 +0000 Subject: [PATCH 21/99] Fix setting key len for child conf --- apache2/apache2_config.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 13393e5fee..fb755a148e 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -136,6 +136,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) dcfg->col_timeout = NOT_SET; dcfg->crypto_key = NOT_SET_P; + dcfg->crypto_key_len = NOT_SET; dcfg->crypto_key_add = NOT_SET; dcfg->crypto_param_name = NOT_SET_P; dcfg->encryption_is_enabled = NOT_SET; @@ -554,6 +555,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) /* Encryption */ merged->crypto_key = (child->crypto_key == NOT_SET_P ? parent->crypto_key : child->crypto_key); + merged->crypto_key_len = (child->crypto_key_len == NOT_SET + ? parent->crypto_key_len : child->crypto_key_len); merged->crypto_key_add = (child->crypto_key_add == NOT_SET ? parent->crypto_key_add : child->crypto_key_add); merged->crypto_param_name = (child->crypto_param_name == NOT_SET_P From d5866ff3ec1ab240f5dfdc2b1e21dfbe24ebf6e2 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Tue, 3 Jul 2012 19:12:51 +0000 Subject: [PATCH 22/99] Fix pcre version mismatch warning --- apache2/mod_security2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index e3177079a3..275a4e333c 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -89,7 +89,7 @@ static void version(apr_pool_t *mp) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Loaded APR do not match with compiled!"); } - pcre_vrs = apr_psprintf(mp,"%d.%d", PCRE_MAJOR, PCRE_MINOR); + pcre_vrs = apr_psprintf(mp,"%d.%02d", PCRE_MAJOR, PCRE_MINOR); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "ModSecurity: PCRE compiled version=\"%s\"; " From 67cf78cd4c6d18a59304c9d677cca6961e13cdf6 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Tue, 3 Jul 2012 19:18:45 +0000 Subject: [PATCH 23/99] Fix replacing targets --- apache2/re.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/apache2/re.c b/apache2/re.c index 1b94fffe3f..a3d3d11a67 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -140,7 +140,7 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r char *name = NULL, *value = NULL; char *opt = NULL, *param = NULL; char *target_list = NULL, *replace = NULL; - int i, rc, match = 0; + int i, rc, match = 0, var_appended = 0; if(rule != NULL) { @@ -221,10 +221,14 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r strncasecmp(targets[i]->param,value,strlen(targets[i]->param)) == 0) { memset(targets[i]->name,0,strlen(targets[i]->name)); memset(targets[i]->param,0,strlen(targets[i]->param)); + targets[i]->is_counting = 0; + targets[i]->is_negated = 1; match = 1; } } else if (value == NULL && targets[i]->param == NULL){ memset(targets[i]->name,0,strlen(targets[i]->name)); + targets[i]->is_counting = 0; + targets[i]->is_negated = 1; match = 1; } else continue; @@ -251,13 +255,15 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r goto end; } if(msr) { - msr_log(msr, 9, "Successfuly replaced variable"); + msr_log(msr, 9, "Successfully replaced variable"); } #if !defined(MSC_TEST) else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Successfuly replaced variable"); + ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Successfully replaced variable"); } #endif + var_appended = 1; + } else { if(msr) { msr_log(msr, 9, "Cannot find variable to replace"); @@ -361,11 +367,12 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r } #if !defined(MSC_TEST) else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Error parseing rule targets to append variable"); + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Error parsing rule targets to append variable"); } #endif goto end; } + var_appended = 1; } else { if(msr) { msr_log(msr, 9, "Skipping variable, already appended"); @@ -381,15 +388,16 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r p = apr_strtok(NULL,",",&savedptr); } - if(match == 0) { + if(var_appended == 1) { current_targets = msre_generate_target_string(ruleset->mp, rule); rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, current_targets, NULL, NULL); + rule->p1 = apr_pstrdup(ruleset->mp, current_targets); if(msr) { - msr_log(msr, 9, "Successfuly appended variable"); + msr_log(msr, 9, "Successfully appended variable"); } #if !defined(MSC_TEST) else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Successfuly appended variable"); + ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Successfully appended variable"); } #endif } From 60960a1f0f405ac37c00132cce13fac03ed2b2cb Mon Sep 17 00:00:00 2001 From: brenosilva Date: Mon, 23 Jul 2012 18:02:29 +0000 Subject: [PATCH 24/99] Update Reference Manual --- CHANGES | 12 ++++ doc/Reference_Manual.html | 147 +++++++++++++++++++++----------------- 2 files changed, 95 insertions(+), 64 deletions(-) diff --git a/CHANGES b/CHANGES index d8f1604ad8..f1c004dfab 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,15 @@ +23 Jul 2012 - 2.6.7 +------------------- + + * Fixed PCRE mismtach version warning message (Thanks Victor Julien). + + * Fixed explicit target replacement using SecUpdateTargetById was broken. + + * The ctl:ruleUpdateTargetById is deprecated and will be removed for future versions since + there is no safe way to use it per-request. + + * Added ctl:ruleRemoveTargetById that can be used to exclude targets to be processed per-request. + 22 Jun 2012 - 2.7.0-rc2 ------------------- diff --git a/doc/Reference_Manual.html b/doc/Reference_Manual.html index c7a72c2431..ba98326ba5 100644 --- a/doc/Reference_Manual.html +++ b/doc/Reference_Manual.html @@ -9,26 +9,30 @@ - + + + +href="https://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Special:RecentChanges&feed=rss"> +href="https://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Special:RecentChanges&feed=atom"> SourceForge.net: Reference Manual - mod-security - - @@ -41,7 +45,7 @@ var wgScript = "/apps/mediawiki/mod-security/index.php"; var wgVariantArticlePath = false; var wgActionPaths = {}; - var wgServer = "http://sourceforge.net"; + var wgServer = "https://sourceforge.net"; var wgCanonicalNamespace = ""; var wgCanonicalSpecialPageName = false; var wgNamespaceNumber = 0; @@ -50,12 +54,12 @@ var wgAction = "view"; var wgArticleId = "12"; var wgIsArticle = true; - var wgUserName = null; - var wgUserGroups = null; + var wgUserName = "Brenosilva"; + var wgUserGroups = ["admin", "editor", "*", "user", "autoconfirmed"]; var wgUserLanguage = "en"; var wgContentLanguage = "en"; var wgBreakFrames = false; - var wgCurRevisionId = 502; + var wgCurRevisionId = 507; var wgVersion = "1.15.1"; var wgEnableAPI = true; var wgEnableWriteAPI = true; @@ -63,11 +67,13 @@ var wgDigitTransformTable = ["", ""]; var wgRestrictionEdit = []; var wgRestrictionMove = []; + var wgAjaxWatch = {"watchMsg": "Watch", "unwatchMsg": "Unwatch", "watchingMsg": "Watching…", "unwatchingMsg": "Unwatching…"}; /*]]>*/ + @@ -2604,6 +2610,9 @@

From mod-security

SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,t:none,nolog,pass,ctl:ruleUpdateTargetById=958895;REQUEST_URI;REQUEST_FILENAME"
 
+
Note 
This ctl is deprecated and will be removed +from the code, since we cannot use it per-transaction. +

SecRuleUpdateTargetByMsg

Description: Updates the target (variable) list of the @@ -2653,18 +2662,6 @@

From mod-security

"phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=% {tx.0}"" -

Conditionally Appending Targets -

You could also do the same by using the ctl action. This is -useful if you want to only update the targets for a particular URL -

-
SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,t:none,nolog,pass,ctl:ruleUpdateTargetByMsg='System Command Injection';!ARGS:email"
-
-

Conditionally Replacing Targets -

You could also replace targets using the ctl action. For -example, lets say you want to only inspect ARGS for a particular URL: -

-
SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,t:none,nolog,pass,ctl:ruleUpdateTargetByMsg='System Command Injection';REQUEST_URI;REQUEST_FILENAME"
-

SecRuleUpdateTargetByTag

Description: Updates the target (variable) list of the @@ -2714,18 +2711,6 @@

From mod-security

"phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=% {tx.0}"" -

Conditionally Appending Targets -

You could also do the same by using the ctl action. This is -useful if you want to only update the targets for a particular URL -

-
SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,t:none,nolog,pass,ctl:ruleUpdateTargetByMsg='WASCTC/WASC-31';!ARGS:email"
-
-

Conditionally Replacing Targets -

You could also replace targets using the ctl action. For -example, lets say you want to only inspect ARGS for a particular URL: -

-
SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,t:none,nolog,pass,ctl:ruleUpdateTargetByMsg='WASCTC/WASC-31';REQUEST_URI;REQUEST_FILENAME"
-

SecServerSignature

Description: Instructs ModSecurity to change the data @@ -2932,7 +2917,7 @@

From mod-security

Below is a diagram of the standard Apache Request Cycle. In the diagram, the 5 ModSecurity processing phases are shown.

@@ -4511,9 +4496,10 @@

From mod-security

  • ruleRemoveById - since this action us triggered at run time, it should be specified before the rule in which it is disabling. -
  • ruleUpdateTargetById -
  • ruleUpdateTargetByMsg -
  • ruleUpdateTargetByTag +
  • ruleUpdateTargetById - This is deprecated and will be +removed from the code. Use ruleRemoveTargetById for per-request +exceptions. +
  • ruleRemoveTargetById
  • ruleRemoveByMsg
  • encryptionEngine
  • encryptionEnforcement @@ -6247,16 +6233,16 @@

    From mod-security

    - + +href="https://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Reference_Manual">https://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Reference_Manual"
    @@ -6269,18 +6255,30 @@
    Views
    @@ -6290,6 +6288,24 @@
    Personal tools
    @@ -6297,7 +6313,7 @@
    Personal tools
    @@ -6306,24 +6322,24 @@
    Navigation
    @@ -6348,22 +6364,25 @@
    Toolbox
    @@ -6375,15 +6394,15 @@
    Toolbox
    src="Reference_Manual_files/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki">
      -
    • This page was last modified on 8 June 2012, at -12:36.
    • -
    • This page has been accessed 130,792 times.
    • +
    • This page was last modified on 23 July 2012, at +17:54.
    • +
    • This page has been accessed 142,275 times.
    - + - @@ -151,8 +145,8 @@

    From mod-security

    class="tocnumber">4.2 Core Rules Content
  • -
  • 5 - Installation +
  • 5 Installation for Apache
  • +
  • 6 Installation for +Microsoft IIS + +
  • 6 Configuration +class="tocnumber">7 Configuration Directives
  • 7 Processing Phases +class="tocnumber">8 Processing Phases
  • -
  • 8 +
  • 9 Variables
  • 9 Transformation +class="tocnumber">10 Transformation functions
  • -
  • 10 +
  • 11 Actions
  • -
  • 11 +
  • 12 Operators
  • 12 Macro Expansion
  • +class="tocnumber">13 Macro Expansion
  • 13 Persistant Storage
  • +class="tocnumber">14 Persistant Storage
  • 14 Miscellaneous Topics +class="tocnumber">15 Miscellaneous Topics
  • 15 A Recommended Base + class="tocnumber">16 A Recommended Base Configuration
  • @@ -1018,8 +1037,8 @@

    From mod-security

  • Trojan Protection - Detecting access to Trojans horses.
  • Error Hiding - Disguising error messages sent by the server.
  • -

    Installation

    +

    Installation for Apache

    Prerequisites

    From mod-security
    1. --enable-pcre-jit - Enables JIT support from pcre >= 8.20 that can improve regex performance. -
    2. --enable-cache-lua - Enables lua vm caching that can +
    3. --enable-lua-cache - Enables lua vm caching that can improve lua script performance. Difference just appears if ModSecurity must run more than one script per transaction.
    4. --enable-request-early - On ModSecuricy 2.6 phase one has been moved to phase 2 hook, if you want to play around it use this option.
    +

    +Installation for Microsoft IIS

    +

    The source code of ModSecurity’s IIS components is fully published +and the binary building process is described (see +mod_security/iis/winbuild/howto.txt). For quick installation it is +highly recommended to use standard MSI installer available from +SourceForge files repository of ModSecurity project or use binary +package and follow the manual installation steps. +

    +

    + Manually Installing and Troubleshooting +Setup of ModSecurity Module on IIS

    +

    Prerequisites

    +

    Before installing ModSecurity one has to install Visual Studio 2010 +Runtime: +

    + +

    Installation Steps

    +

    Download binary package and unzip the content to a separate folder: +

    + +
    The installation process of ModSecurity module on IIS consists +of three parts: +
    +


    +

    +
    1. Copying of binaries: copyfiles.bat +
    The following binary files are required by ModSecurity module +and by default should be copied to %windir%\system32\ (32-bit +binaries) and/or %windir%\SysWOW64\ (64-bit binaries): +
    +
    • libapr-1.dll +
    • libapriconv-1.dll +
    • libaprutil-1.dll +
    • libxml2.dll +
    • lua5.1.dll +
    • ModSecurityIIS.dll +
    • pcre.dll +
    • zlib1.dll +
    +
    The mlogc tool can be copied to any place, together with +libcurl.dll: +
    +
    • libcurl.dll +
    • mlogc.exe +
    +


    +

    +
    2. Registering of the module: register.bat +
    An IIS module must be properly registered before it can be +used by web applications. The following command, executed +in %windir%\system32\inetsrv, performs the registration: +
    +
    appcmd.exe install module /name:ModSecurityIIS /image:%windir%\system32\inetsrv\modsecurityiis.dll
    +
    The registration process itself is described with details in +the following articles: +
    + +


    +

    +
    3. Extending of the configuration schema. +
    The last step extends IIS configuration schema with +ModSecurity entities, using ModSecurity.xml file provided in the binary: +
    +
    iisschema.exe /install ModSecurity.xml
    +
    and iisschema.exe tool. More information about the tool and +this step is available here: +
    + +

    Configuration

    +
    After the installation the module will be running in all +websites by default. To remove it from a website add to web.config: +
    +
    <modules>
    +    <remove name="ModSecurityIIS" />
    +</modules>
    +
    To configure module in a website add to web.config: +
    +
    <?xml version="1.0" encoding="UTF-8"?>
    +<configuration>
    +    <system.webServer>
    +        <ModSecurity enabled="true" configFile="c:\inetpub\wwwroot\xss.conf" />
    +    </system.webServer>
    +</configuration>
    +
    where configFile is standard ModSecurity config file. +
    +


    +

    +
    Events from the module will show up in "Application" Windows +log. +
    +

    Common Problems

    +
    If after installation protected website responds with HTTP 503 +error and event ID 2280 keeps getting logged in the application event +log: +
    +
    Log Name:      Application
    +Source:        Microsoft-Windows-IIS-W3SVC-WP
    +Event ID:      2280
    +Task Category: None
    +Level:         Error
    +Keywords:      Classic
    +User:          N/A
    +Description:
    +The Module DLL C:\Windows\system32\inetsrv\modsecurityiis.dll failed to load.  The data is the error.
    +
    +

    most likely it means that the installation process has failed and the + ModSecurityIIS.dll module is missing one or more libraries that it +depends on. Repeating installation of the prerequisites and the module +files should fix the problem. The dependency walker tool: +

    + +

    can be used to figure out which library is missing or cannot be +loaded. +

    Configuration Directives

    The following section outlines all of the ModSecurity directives. @@ -2917,7 +3091,7 @@

    From mod-security

    Below is a diagram of the standard Apache Request Cycle. In the diagram, the 5 ModSecurity processing phases are shown.

    @@ -3203,15 +3377,15 @@

    From mod-security

    Note 
    Higher severities have a lower numeric value.
    -

    INBOUND_ERROR_DATA

    +

    INBOUND_DATA_ERROR

    This variable will be set to 1 when the request body size is above the setting configured by SecRequestBodyLimit directive. Your policies should always contain a rule to check this variable. Depending on the rate of false positives and your default policy you should decide whether to block or just warn when the rule is triggered.

    The best way to use this variable is as in the example below: -

    SecRule INBOUND_ERROR_DATA "@eq 1" +

    SecRule INBOUND_DATA_ERROR "@eq 1" "phase:1,t:none,log,pass,msg:'Request Body Larger than SecRequestBodyLimit Setting'"

    @@ -3333,6 +3507,18 @@

    From mod-security

    Change the rule from blocking to logging-only if many false positives are encountered.

    +

    OUTBOUND_DATA_ERROR

    +

    This variable will be set to 1 when the response body size is above +the setting configured by SecResponseBodyLimit directive. Your policies + should always contain a rule to check this variable. Depending on the +rate of false positives and your default policy you should decide +whether to block or just warn when the rule is triggered. +

    The best way to use this variable is as in the example below: +

    SecRule OUTBOUND_DATA_ERROR "@eq 1" +"phase:1,t:none,log,pass,msg:'Response Body Larger than +SecResponseBodyLimit Setting'" +

    PATH_INFO

    Contains the extra request URI information, also known as path info. @@ -4237,6 +4423,12 @@

    From mod-security

    urlEncode

    Encodes input string using URL encoding.

    +

    utf8toUnicode

    +

    Converts all UTF-8 characters sequences to Unicode. This help input +normalization specially for non-english languages minimizing +false-positives and false-negatives. (available with 2.7.0) +

    sha1

    Calculates a SHA1 hash from the input string. The computed hash is in a raw binary form and may need encoded into text to be printed (or @@ -4448,9 +4640,9 @@

    From mod-security

    chained rule will be triggered only if all of the variable checks return positive hits. If any one aspect of a chained rule comes back negative, then the entire rule chain will fail to match. Also note that -disruptive actions, execution phases, metadata actions (id, rev, msg), -skip, and skipAfter actions can be specified only by the chain starter -rule. +disruptive actions, execution phases, metadata actions (id, rev, msg, +tag, severity, logdata), skip, and skipAfter actions can be specified +only by the chain starter rule.

    The following directives can be used in rule chains:

    @@ -4788,10 +4980,10 @@

    From mod-security

    Description: Pauses transaction processing for the specified number of milliseconds. Starting with ModSecurity 2.7 this feature also supports macro expansion. -

    Action Group: Non-disruptive +

    Action Group: Disruptive

    Example:

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" "log,deny,status:403,pause:5000"
    +
    SecRule REQUEST_HEADERS:User-Agent "Test" "log,pause:5000"
     
    Warning 
    This feature can be of limited benefit for slowing down brute force authentication attacks, but use with care. @@ -5064,6 +5256,9 @@

    From mod-security

    Header set Set-Cookie "%{httponly_cookie}e; HTTPOnly" env=httponly_cookie
    +
    Note 
    When used in a chain this action will be +execute when an individual rule matches and not the entire chain. +

    setvar

    Description: Creates, removes, or updates a variable. Variable names are case-insensitive. @@ -5083,6 +5278,9 @@

    From mod-security

    tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score}, \ setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}" +
    Note 
    When used in a chain this action will be +execute when an individual rule matches and not the entire chain. +

    skip

    Description: Skips one or more rules (or chains) on successful match. @@ -6233,16 +6431,16 @@

    From mod-security

    - + +href="http://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Reference_Manual">http://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Reference_Manual"
    @@ -6255,30 +6453,18 @@
    Views
    @@ -6288,24 +6474,6 @@
    Personal tools
    @@ -6313,7 +6481,7 @@
    Personal tools
    @@ -6322,24 +6490,24 @@
    Navigation
    @@ -6364,25 +6532,22 @@
    Toolbox
    @@ -6394,15 +6559,15 @@
    Toolbox
    src="Reference_Manual_files/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki">
      -
    • This page was last modified on 23 July 2012, at -17:54.
    • -
    • This page has been accessed 142,275 times.
    • +
    • This page was last modified on 8 October 2012, at + 19:06.
    • +
    • This page has been accessed 163,958 times.
    - + @@ -1274,6 +1288,45 @@

    From mod-security

    has been moved to phase 2 hook, if you want to play around it use this option. +

    Installation for NGINX

    +

    The extensibility model of the nginx server does not include +dynamically loaded modules, thus ModSecurity must be compiled with the +source code of the main server. Since nginx is available on multiple +Unix-based platforms (and also on Windows), for now the recommended way +of obtaining ModSecurity for nginx is compilation in the designated +environment. +

    +

    Manually Installing ModSecurity Module on NGINX

    +

    The first step in obtaining nginx server with built-in ModSecurity +module is building of standalone library containing full ModSecurity +with a set of intermediate API (this layer is a common base for IIS +version, nginx version, and server-less command line version of +ModSecurity). It is recommended to follow the general steps of preparing + build environment for ModSecurity and then follow with two simple +commands +

    +

    Installation Steps

    +

    1 - Compile standalone module: +

    +
    ~/mod_security$ ./configure --enable-standalone-module
    +~/mod_security$ make
    +
    +

    2 - Once the standalone library is built successfully, one can follow + with building the nginx server, following the steps from the nginx +build tutorial: +

    +
    ~/nginx-1.2.0$ ./configure --add-module=../mod_security/nginx/modsecurity
    +~/nginx-1.2.0$ make
    +~/nginx-1.2.0$ sudo make install
    +
    +

    The last command performs server installation on the local machine, +which can be either customized or omitted with built binaries packaged +or moved to alternative server. +

    Installation for Microsoft IIS

    @@ -1306,7 +1359,7 @@

    From mod-security

    title="http://www.microsoft.com/en-us/download/details.aspx?id=14632" rel="nofollow">http://www.microsoft.com/en-us/download/details.aspx?id=14632 -

    Installation Steps

    Download binary package and unzip the content to a separate folder:

    @@ -3460,7 +3513,7 @@

    From mod-security

    variables is also set to 1: REQBODY_PROCESSOR_ERROR, MULTIPART_BOUNDARY_QUOTED, MULTIPART_BOUNDARY_WHITESPACE, MULTIPART_DATA_BEFORE, MULTIPART_DATA_AFTER, MULTIPART_HEADER_FOLDING, -MULTIPART_LF_LINE, MULTIPART_SEMICOLON_MISSING MULTIPART_INVALID_QUOTING +MULTIPART_LF_LINE, MULTIPART_MISSING_SEMICOLON MULTIPART_INVALID_QUOTING MULTIPART_INVALID_HEADER_FOLDING MULTIPART_FILE_LIMIT_EXCEEDED. Each of these variables covers one unusual (although sometimes legal) aspect of the request body in multipart/form-data format. Your policies should @@ -3481,7 +3534,7 @@

    From mod-security

    DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ -SM %{MULTIPART_SEMICOLON_MISSING}, \ +SM %{MULTIPART_MISSING_SEMICOLON}, \ IQ %{MULTIPART_INVALID_QUOTING}, \ IQ %{MULTIPART_INVALID_HEADER_FOLDING}, \ FE %{MULTIPART_FILE_LIMIT_EXCEEDED}'" @@ -4691,7 +4744,9 @@

    From mod-security

  • ruleUpdateTargetById - This is deprecated and will be removed from the code. Use ruleRemoveTargetById for per-request exceptions. -
  • ruleRemoveTargetById +
  • ruleRemoveTargetById - since this action is used to just + remove targets, users don't need to use the char ! before the +target list.
  • ruleRemoveByMsg
  • encryptionEngine
  • encryptionEnforcement @@ -6293,7 +6348,7 @@

    From mod-security

    DA %{MULTIPART_DATA_AFTER}, \ HF %{MULTIPART_HEADER_FOLDING}, \ LF %{MULTIPART_LF_LINE}, \ -SM %{MULTIPART_SEMICOLON_MISSING}, \ +SM %{MULTIPART_MISSING_SEMICOLON}, \ IQ %{MULTIPART_INVALID_QUOTING}, \ IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'" @@ -6431,13 +6486,13 @@

    From mod-security

    - + @@ -6547,7 +6602,7 @@
    Toolbox
    href="http://sourceforge.net/apps/mediawiki/mod-security/index.php?title=Reference_Manual&printable=yes&printable=yes" rel="alternate" title="Printable version of this page [alt-shift-p]" accesskey="p">Printable version
  • @@ -6559,15 +6614,15 @@
    Toolbox
    src="Reference_Manual_files/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki">
      -
    • This page was last modified on 8 October 2012, at - 19:06.
    • -
    • This page has been accessed 163,958 times.
    • +
    • This page was last modified on 15 October 2012, +at 13:50.
    • +
    • This page has been accessed 165,682 times.
    - + - + - - - - - - - - - - - -
    -
    -
    - -

    Reference Manual

    -
    -

    From mod-security

    -
    -
    Jump to: navigation, - search
    -

    Contents

    [hide]
    - -
    -

    ModSecurity® Reference Manual

    -

    Current as of v2.5.13 v2.6 and v2.7

    -

    Copyright © 2004-2011 Trustwave Holdings, -Inc.

    -

    Table of Contents

    -

    Introduction

    -

    ModSecurity is a web application firewall (WAF). With over 70% of -attacks now carried out over the web application level, organisations -need all the help they can get in making their systems secure. WAFs are -deployed to establish an increased external security layer to detect -and/or prevent attacks before they reach web applications. ModSecurity -provides protection from a range of attacks against web applications and - allows for HTTP traffic monitoring and real-time analysis with little -or no changes to existing infrastructure. -

    -

    HTTP Traffic Logging

    -

    Web servers are typically well-equipped to log traffic in a form -useful for marketing analyses, but fall short logging traffic to web -applications. In particular, most are not capable of logging the request - bodies. Your adversaries know this, and that is why most attacks are -now carried out via POST requests, rendering your systems blind. -ModSecurity makes full HTTP transaction logging possible, allowing -complete requests and responses to be logged. Its logging facilities -also allow fine-grained decisions to be made about exactly what is -logged and when, ensuring only the relevant data is recorded. As some of - the request and/or response may contain sensitive data in certain -fields, ModSecurity can be configured to mask these fields before they -are written to the audit log. -

    -

    Real-Time Monitoring and Attack Detection

    -

    In addition to providing logging facilities, ModSecurity can monitor -the HTTP traffic in real time in order to detect attacks. In this case, -ModSecurity operates as a web intrusion detection tool, allowing you to -react to suspicious events that take place at your web systems. -

    -

    Attack Prevention and Virtual Patching

    -

    ModSecurity can also act immediately to prevent attacks from reaching - your web applications. There are three commonly used approaches: -

    -
    1. Negative security model. A negative security model monitors -requests for anomalies, unusual behaviour, and common web application -attacks. It keeps anomaly scores for each request, IP addresses, -application sessions, and user accounts. Requests with high anomaly -scores are either logged or rejected altogether. -
    2. Positive security model. When a positive security model is -deployed, only requests that are known to be valid are accepted, with -everything else rejected. This model requires knownledge of the web -applications you are protecting. Therefore a positive security model -works best with applications that are heavily used but rarely updated so - that maintenance of the model is minimized. -
    3. Known weaknesses and vulnerabilities. Its rule language makes -ModSecurity an ideal external patching tool. External patching -(sometimes referred to as Virtual Patching) is about reducing the window - of opportunity. Time needed to patch application vulnerabilities often -runs to weeks in many organisations. With ModSecurity, applications can -be patched from the outside, without touching the application source -code (and even without any access to it), making your systems secure -until a proper patch is applied to the application. -
    -

    Flexible Rule Engine

    -

    A flexible rule engine sits in the heart of ModSecurity. It -implements the ModSecurity Rule Language, which is a specialised -programming language designed to work with HTTP transaction data. The -ModSecurity Rule Language is designed to be easy to use, yet flexible: -common operations are simple while complex operations are possible. -Certified ModSecurity Rules, included with ModSecurity, contain a -comprehensive set of rules that implement general-purpose hardening, -protocol validation and detection of common web application security -issues. Heavily commented, these rules can be used as a learning tool. -

    -

    - Embedded-mode Deployment

    -

    ModSecurity is an embeddable web application firewall, which means it - can be deployed as part of your existing web server infrastructure -provided your web servers are Apache-based. This deployment method has -certain advantages: -

    -
    1. No changes to existing network. It only takes a few minutes to -add ModSecurity to your existing web servers. And because it was -designed to be completely passive by default, you are free to deploy it -incrementally and only use the features you need. It is equally easy to -remove or deactivate it if required. -
    2. No single point of failure. Unlike with network-based -deployments, you will not be introducing a new point of failure to your -system. -
    3. Implicit load balancing and scaling. Because it works embedded -in web servers, ModSecurity will automatically take advantage of the -additional load balancing and scalability features. You will not need to - think of load balancing and scaling unless your existing system needs -them. -
    4. Minimal overhead. Because it works from inside the web server -process there is no overhead for network communication and minimal -overhead in parsing and data exchange. -
    5. No problem with encrypted or compressed content. Many IDS -systems have difficulties analysing SSL traffic. This is not a problem -for ModSecurity because it is positioned to work when the traffic is -decrypted and decompressed. -
    -

    - Network-based Deployment

    -

    ModSecurity works equally well when deployed as part of an -Apache-based reverse proxy server, and many of our customers choose to -do so. In this scenario, one installation of ModSecurity can protect any - number of web servers (even the non-Apache ones). -

    -

    Portability

    -

    ModSecurity is known to work well on a wide range of operating -systems. Our customers are successfully running it on Linux, Windows, -Solaris, FreeBSD, OpenBSD, NetBSD, AIX, Mac OS X, and HP-UX. -

    -

    -Licensing

    -

    ModSecurity is available under the Apache Software License v2 [1] -

    -
    Note 
    ModSecurity, mod_security, ModSecurity Pro, - and ModSecurity Core Rules are trademarks or registered trademarks of -Trustwave Holdings, Inc. -
    -

    OWASP ModSecurity Core Rule Set (CRS) Project

    -

    -Overview

    -

    ModSecurity is a web application firewall engine that provides very -little protection on its own. In order to become useful, ModSecurity -must be configured with rules. In order to enable users to take full -advantage of ModSecurity out of the box, Trustwave's SpiderLabs created -the OWASP ModSecurity Core Rule Set (CRS) Project. Unlike intrusion -detection and prevention systems, which rely on signatures specific to -known vulnerabilities, the CRS provide generic protection from unknown -vulnerabilities often found in web applications, which are in most cases - custom coded. The CRS is heavily commented to allow it to be used as a -step-by-step deployment guide for ModSecurity. The latest rules packages - can be found at the OWASP ModSecurity CRS Project Site. -

    -

    Core Rules Content

    -

    In order to provide generic web applications protection, the CRS use -some of the following example techniques: -

    -
    • HTTP protection - detecting violations of the HTTP protocol and a - locally defined usage policy. -
    • Common Web Attacks Protection - detecting common web -application security attack. -
    • Automation detection - Detecting bots, crawlers, scanners and -other surface malicious activity. -
    • Trojan Protection - Detecting access to Trojans horses. -
    • Error Hiding - Disguising error messages sent by the server. -
    -

    Installation for Apache

    -

    Prerequisites

    -

    ModSecurity 2.x works only with Apache 2.0.x or -higher

    -

    Version 2.2.x is highly recommended. -

    -

    mod_uniqueid

    -

    Make sure you have mod_unique_id installed. -mod_unique_id is packaged with Apache httpd. -

    -

    libapr and libapr-util

    -

    libapr and libapr-util - http://apr.apache.org/ -

    -

    -libpcre

    -

    http://www.pcre.org/ -

    -

    -libxml2

    -

    http://xmlsoft.org/downloads.html -

    -

    liblua v5.1.x

    -

    This library is optional and only needed if you will be using the new - Lua engine - http://www.lua.org/download.html -

    -
    Note 
    that ModSecurity requires the dynamic -libraries. These are not built by default in the source distribution, so - the binary distribution is recommended. -
    -

    - libcurl v7.15.1 or higher

    -

    If you will be using the ModSecurity Log Collector (mlogc) to send -audit logs to a central repository, then you will also need the curl -library. -

    http://curl.haxx.se/libcurl/ -

    -
    Note 
    Many have had issues with libcurl linked -with the GnuTLS library for SSL/TLS support. It is recommended that the -openssl library be used for SSL/TLS support in libcurl. -
    -

    Installation Methods

    -

    Before you begin with installation you will need to choose your -preferred installation method. First you need to choose whether to -install the latest version of ModSecurity directly from CVS (best -features, but possibly unstable) or use the latest stable release -(recommended). If you choose a stable release, it might be possible to -install ModSecurity from binary. It is always possible to compile it -from source code. -

    The following few pages will give you more information on -benefits of choosing one method over another. -

    -

    - SVN Access

    -

    If you want to access the latest version of the module you need to -get it from the svn repository. The list of changes made since the last -stable release is normally available on the web site (and in the file -CHANGES). The SVN repository for ModSecurity is hosted by SourceForge (http://www.sf.net). You can - access it directly or view if through web using this address: http://mod-security.svn.sourceforge.net/viewvc/mod-security/ - -

    To download the lastest TRUNK source code to your computer you -need to execute the following command: -

    git -

    -
    $git svn clone --prefix=svn/ https://mod-security.svn.sourceforge.net/svnroot/mod-security/m2/trunk
    -
    -

    svn -

    -
    svn co https://mod-security.svn.sourceforge.net/svnroot/mod-security/m2/trunk modsecurity
    -
    -

    For v2.6.0 and above, the installation process has changed. Follow -these steps: -

    -
    1. cd into the directory - $cd modsecurity -
    2. Run autogen.sh script - $./autogen.sh -
    3. Run configure script - $./configure -
    4. Run make - $make -
    5. Run make install - $make install -
    6. Copy the new mod_security2.so file into the proper Apache -modules directory - $cp -/usr/local/modsecurity/lib/mod_security2.so /usr/local/apache/modules/ -
    -

    Stable Release Download

    -

    To download the stable release go to http://www.modsecurity.org/download/. - Binary distributions are sometimes available. If they are, they are -listed on the download page. If not download the source code -distribution. -

    -

    Installation Steps

    -
    • Stop Apache httpd -
    • Unpack the ModSecurity archive -
    • Build -
    -

    Building differs for UNIX (or UNIX-like) operating systems and -Windows. -

    -

    UNIX

    -

    Run the configure script to generate a Makefile. Typically no options - are needed. -

    -
    ./configure
    -

    Options are available for more customization (use ./configure --help -for a full list), but typically you will only need to specify the -location of the apxs command installed by Apache httpd with the ---with-apxs option. -

    -
    ./configure --with-apxs=/path/to/httpd-2.x.y/bin/apxs
    -
    Note 
    There are certain configure options that -are meant for debugging an other development use. If enabled, these -options can substantially impact performance. These options include all ---debug-* options as well as the --enable-performance-measurements -options. -
    -

    Compile with: -

    -
    make
    -

    Optionally test with: -

    -
    make CFLAGS=-DMSC_TEST test
    -
    Note 
    This is step is still a bit experimental. -If you have problems, please send the full output and error from the -build to the support list. Most common issues are related to not finding - the required headers and/or libraries. -
    -

    Optionally build the ModSecurity Log Collector with: -

    -
    make mlogc
    -

    Optionally install mlogc: Review the INSTALL file included in the -apache2/mlogc-src directory in the distribution. -Install the ModSecurity module with: -

    -
    make install
    -

    - Windows (MS VC++ 8)

    -

    Edit Makefile.win to configure the Apache base and library paths. -Compile with: nmake -f Makefile.win -Install the ModSecurity module with: nmake -f Makefile.win install -Copy the libxml2.dll and lua5.1.dll to the Apache bin directory. -Alternatively you can follow the step below for using LoadFile to load -these libraries. -

    -
    Note 
    Users should follow the steps present in -README_WINDOWS.txt into ModSecurity tarball. -
    -

    - Edit the main Apache httpd config file -(usually httpd.conf)

    -

    On UNIX (and Windows if you did not copy the DLLs as stated above) -you must load libxml2 and lua5.1 before ModSecurity with something like -this: -

    -
    LoadFile /usr/lib/libxml2.so
    -LoadFile /usr/lib/liblua5.1.so
    -
    -

    Load the ModSecurity module with: -

    -
    LoadModule security2_module modules/mod_security2.so
    -
    -

    Configure ModSecurity

    -

    Start Apache httpd

    -

    You should now have ModSecurity 2.x up and running. -

    -
    Note 
    If you have compiled Apache yourself you -might experience problems compiling ModSecurity against PCRE. This is -because Apache bundles PCRE but this library is also typically provided -by the operating system. I would expect most (all) vendor-packaged -Apache distributions to be configured to use an external PCRE library -(so this should not be a problem). -
    -
    You want to avoid Apache using the bundled PCRE library and -ModSecurity linking against the one provided by the operating system. -The easiest way to do this is to compile Apache against the PCRE library - provided by the operating system (or you can compile it against the -latest PCRE version you downloaded from the main PCRE distribution -site). You can do this at configure time using the --with-pcre switch. -If you are not in a position to recompile Apache, then, to compile -ModSecurity successfully, you'd still need to have access to the bundled - PCRE headers (they are available only in the Apache source code) and -change the include path for ModSecurity (as you did in step 7 above) to -point to them (via the --with-pcre ModSecurity configure option). -
    -
    Do note that if your Apache is using an external PCRE library -you can compile ModSecurity with WITH_PCRE_STUDY defined,which would -possibly give you a slight performance edge in regular expression -processing. -
    -
    Non-gcc compilers may have problems running out-of-the-box as -the current build system was designed around the gcc compiler and some -compiler/linker flags may differ. To use a non-gcc compiler you may need - some manual Makefile tweaks if issues cannot be solved by exporting -custom CFLAGS and CPPFLAGS environment variables. -
    -
    If you are upgrading from ModSecurity 1.x, please refer to the -migration matrix at http://www.modsecurity.org/documentation/ModSecurity-Migration-Matrix.pdf -
    -
    Starting with ModSecurity 2.7.0 there are a few important -configuration options -
    -
    1. --enable-pcre-jit - Enables JIT support from pcre >= -8.20 that can improve regex performance. -
    2. --enable-lua-cache - Enables lua vm caching that can -improve lua script performance. Difference just appears if ModSecurity -must run more than one script per transaction. -
    3. --enable-request-early - On ModSecuricy 2.6 phase one -has been moved to phase 2 hook, if you want to play around it use this -option. -
    -

    Installation for NGINX

    -

    The extensibility model of the nginx server does not include -dynamically loaded modules, thus ModSecurity must be compiled with the -source code of the main server. Since nginx is available on multiple -Unix-based platforms (and also on Windows), for now the recommended way -of obtaining ModSecurity for nginx is compilation in the designated -environment. -

    -

    Manually Installing ModSecurity Module on NGINX

    -

    The first step in obtaining nginx server with built-in ModSecurity -module is building of standalone library containing full ModSecurity -with a set of intermediate API (this layer is a common base for IIS -version, nginx version, and server-less command line version of -ModSecurity). It is recommended to follow the general steps of preparing - build environment for ModSecurity and then follow with two simple -commands -

    -

    Installation Steps

    -

    1 - Compile standalone module: -

    -
    ~/mod_security$ ./configure --enable-standalone-module
    -~/mod_security$ make
    -
    -

    2 - Once the standalone library is built successfully, one can follow - with building the nginx server, following the steps from the nginx -build tutorial: -

    -
    ~/nginx-1.2.0$ ./configure --add-module=../mod_security/nginx/modsecurity
    -~/nginx-1.2.0$ make
    -~/nginx-1.2.0$ sudo make install
    -
    -

    The last command performs server installation on the local machine, -which can be either customized or omitted with built binaries packaged -or moved to alternative server. -

    -

    -Installation for Microsoft IIS

    -

    The source code of ModSecurity’s IIS components is fully published -and the binary building process is described (see -mod_security/iis/winbuild/howto.txt). For quick installation it is -highly recommended to use standard MSI installer available from -SourceForge files repository of ModSecurity project or use binary -package and follow the manual installation steps. -

    -

    - Manually Installing and Troubleshooting -Setup of ModSecurity Module on IIS

    -

    Prerequisites

    -

    Before installing ModSecurity one has to install Visual Studio 2010 -Runtime: -

    - -

    Installation Steps

    -

    Download binary package and unzip the content to a separate folder: -

    - -
    The installation process of ModSecurity module on IIS consists -of three parts: -
    -


    -

    -
    1. Copying of binaries: copyfiles.bat -
    The following binary files are required by ModSecurity module -and by default should be copied to %windir%\system32\ (32-bit -binaries) and/or %windir%\SysWOW64\ (64-bit binaries): -
    -
    • libapr-1.dll -
    • libapriconv-1.dll -
    • libaprutil-1.dll -
    • libxml2.dll -
    • lua5.1.dll -
    • ModSecurityIIS.dll -
    • pcre.dll -
    • zlib1.dll -
    -
    The mlogc tool can be copied to any place, together with -libcurl.dll: -
    -
    • libcurl.dll -
    • mlogc.exe -
    -


    -

    -
    2. Registering of the module: register.bat -
    An IIS module must be properly registered before it can be -used by web applications. The following command, executed -in %windir%\system32\inetsrv, performs the registration: -
    -
    appcmd.exe install module /name:ModSecurityIIS /image:%windir%\system32\inetsrv\modsecurityiis.dll
    -
    The registration process itself is described with details in -the following articles: -
    - -


    -

    -
    3. Extending of the configuration schema. -
    The last step extends IIS configuration schema with -ModSecurity entities, using ModSecurity.xml file provided in the binary: -
    -
    iisschema.exe /install ModSecurity.xml
    -
    and iisschema.exe tool. More information about the tool and -this step is available here: -
    - -

    Configuration

    -
    After the installation the module will be running in all -websites by default. To remove it from a website add to web.config: -
    -
    <modules>
    -    <remove name="ModSecurityIIS" />
    -</modules>
    -
    To configure module in a website add to web.config: -
    -
    <?xml version="1.0" encoding="UTF-8"?>
    -<configuration>
    -    <system.webServer>
    -        <ModSecurity enabled="true" configFile="c:\inetpub\wwwroot\xss.conf" />
    -    </system.webServer>
    -</configuration>
    -
    where configFile is standard ModSecurity config file. -
    -


    -

    -
    Events from the module will show up in "Application" Windows -log. -
    -

    Common Problems

    -
    If after installation protected website responds with HTTP 503 -error and event ID 2280 keeps getting logged in the application event -log: -
    -
    Log Name:      Application
    -Source:        Microsoft-Windows-IIS-W3SVC-WP
    -Event ID:      2280
    -Task Category: None
    -Level:         Error
    -Keywords:      Classic
    -User:          N/A
    -Description:
    -The Module DLL C:\Windows\system32\inetsrv\modsecurityiis.dll failed to load.  The data is the error.
    -
    -

    most likely it means that the installation process has failed and the - ModSecurityIIS.dll module is missing one or more libraries that it -depends on. Repeating installation of the prerequisites and the module -files should fix the problem. The dependency walker tool: -

    - -

    can be used to figure out which library is missing or cannot be -loaded. -

    -

    - Configuration Directives

    -

    The following section outlines all of the ModSecurity directives. -Most of the ModSecurity directives can be used inside the various Apache - Scope Directives such as VirtualHost, Location, LocationMatch, -Directory, etc... There are others, however, that can only be used once -in the main configuration file. This information is specified in the -Scope sections below. The first version to use a given directive is -given in the Version sections below. -

    These rules, along with the Core rules files, should be contained - is files outside of the httpd.conf file and called up with Apache -"Include" directives. This allows for easier updating/migration of the -rules. If you create your own custom rules that you would like to use -with the Core rules, you should create a file called - -modsecurity_crs_15_customrules.conf and place it in the same directory -as the Core rules files. By using this file name, your custom rules will - be called up after the standard ModSecurity Core rules configuration -file but before the other Core rules. This allows your rules to be -evaluated first which can be useful if you need to implement specific -"allow" rules or to correct any false positives in the Core rules as -they are applied to your site. -

    -
    Note 
    It is highly encouraged that you do not -edit the Core rules files themselves but rather place all changes (such -as SecRuleRemoveByID, etc...) in your custom rules file. This will allow - for easier upgrading as newer Core rules are released by Breach -Security on the ModSecurity website. -
    -

    -SecAction

    -

    Description: Unconditionally processes the action list it -receives as the first and only parameter. The syntax of the parameter is - identical to that of the third parameter of SecRule. -

    Syntax: SecAction "action1,action2,action3,...“ -

    Scope: Any -

    Version: 2.0.0 -

    This directive is commonly used to set variables and initialize -persistent collections using the initcol action. For example: -

    -
    SecAction nolog,phase:1,initcol:RESOURCE=%{REQUEST_FILENAME}
    -

    SecArgumentSeparator

    -

    Description: Specifies which character to use as the separator - for application/x-www-form- urlencoded content. -

    Syntax: SecArgumentSeparator character -

    Default: & -

    Scope: Main(< 2.7.0), Any(2.7.0) -

    Version: 2.0.0 -

    This directive is needed if a backend web application is using a -nonstandard argument separator. Applications are sometimes (very rarely) - written to use a semicolon separator. You should not change the default - setting unless you establish that the application you are working with -requires a different separator. If this directive is not set properly -for each web application, then ModSecurity will not be able to parse the - arguments appropriately and the effectiveness of the rule matching will - be significantly decreased. -

    -

    SecAuditEngine

    -

    Description: Configures the audit logging engine. -

    Syntax: SecAuditEngine RelevantOnly -

    Default: Off -

    Scope: Any -

    Version: 2.0.0 -

    The SecAuditEngine directive is used to configure the audit -engine, which logs complete transactions. ModSecurity is currently able -to log most, but not all transactions. Transactions involving errors -(e.g., 400 and 404 transactions) use a different execution path, which -ModSecurity does not support. -

    The possible values for the audit log engine are as follows: -

    -
    • On: log all transactions -
    • Off: do not log any transactions -
    • RelevantOnly: only the log transactions that have -triggered a warning or an error, or have a status code that is -considered to be relevant (as determined by the -SecAuditLogRelevantStatus directive) -
    -
    Note 
    If you need to change the audit log engine -configuration on a per-transaction basis (e.g., in response to some -transaction data), use the ctl action. -
    -

    The following example demonstrates how SecAuditEngine is used: -

    -
    SecAuditEngine RelevantOnly
    -SecAuditLog logs/audit/audit.log
    -SecAuditLogParts ABCFHZ 
    -SecAuditLogType concurrent 
    -SecAuditLogStorageDir logs/audit 
    -SecAuditLogRelevantStatus ^(?:5|4(?!04))
    -
    -

    SecAuditLog

    -

    Description: Defines the path to the main audit log file -(serial logging format) or the concurrent logging index file (concurrent - logging format). When used in combination with mlogc (only possible -with concurrent logging), this directive defines the mlogc location and -command line. -

    Syntax: SecAuditLog /path/to/audit.log -

    Scope: Any Version: 2.0.0 -

    This file will be used to store the audit log entries if serial -audit logging format is used. If concurrent audit logging format is used - this file will be used as an index, and contain a record of all audit -log files created. If you are planning to use concurrent audit logging -to send your audit log data off to a remote server you will need to -deploy the ModSecurity Log Collector (mlogc), like this: -

    -
    SecAuditLog "|/path/to/mlogc /path/to/mlogc.conf"
    -
    -
    Note 
    This audit log file is opened 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. -
    -

    SecAuditLog2

    -

    Description: Defines the path to the secondary audit log index - file when concurrent logging is enabled. See SecAuditLog for more -details. -

    Syntax: SecAuditLog2 /path/to/audit.log -

    Scope: Any -

    Version: 2.1.2 -

    The purpose of SecAuditLog2 is to make logging to two remote -servers possible, which is typically achieved by running two instances -of the mlogc tool, each with a different configuration (in addition, one - of the instances will need to be instructed not to delete the files it -submits). This directive can be used only if SecAuditLog was previously -configured and only if concurrent logging format is used. -

    -

    SecAuditLogDirMode

    -

    Description: Configures the mode (permissions) of any -directories created for the concurrent audit logs, using an octal mode -value as parameter (as used in chmod). -

    Syntax: SecAuditLogDirMode octal_mode|"default" - -

    Default: 0600 -

    Scope: Any -

    Version: 2.5.10 -

    The default mode for new audit log directories (0600) only grants - read/write access to the owner (typically the account under which -Apache is running, for example apache). If access from other accounts is - needed (e.g., for use with mpm-itk), then you may use this directive to - grant additional read and/or write privileges. You should use this -directive with caution to avoid exposing potentially sensitive data to -unauthorized users. Using the value default as parameter reverts the -configuration back to the default setting. This feature is not available - on operating systems not supporting octal file modes. -

    Example: -

    -
    SecAuditLogDirMode 02750
    -
    Note 
    The process umask may still limit the mode -if it is being more restrictive than the mode set using this directive. -
    -

    SecAuditLogFileMode

    -

    Description: Configures the mode (permissions) of any files -created for concurrent audit logs using an octal mode (as used in -chmod). See SecAuditLogDirMode for controlling the mode of created audit - log directories. -

    Syntax: SecAuditLogFileMode octal_mode|"default" - -

    Default: 0600 -

    Scope: Any -

    Version: 2.5.10 -

    Example Usage: SecAuditLogFileMode 00640 -

    This feature is not available on operating systems not supporting - octal file modes. The default mode (0600) only grants read/write access - to the account writing the file. If access from another account is -needed (using mpm-itk is a good example), then this directive may be -required. However, use this directive with caution to avoid exposing -potentially sensitive data to unauthorized users. Using the value -“default” will revert back to the default setting. -

    -
    Note 
    The process umask may still limit the mode -if it is being more restrictive than the mode set using this directive. -
    -

    SecAuditLogParts

    -

    Description: Defines which parts of each transaction are going - to be recorded in the audit log. Each part is assigned a single letter; - when a letter appears in the list then the equivalent part will be -recorded. See below for the list of all parts. -

    Syntax: SecAuditLogParts PARTLETTERS -

    Example Usage: SecAuditLogParts ABCFHZ -

    Scope: Any Version: 2.0.0 -

    Default: ABCFHZ Note -

    The format of the audit log format is documented in detail in the - Audit Log Data Format Documentation. -

    Available audit log parts: -

    -
    • A: Audit log header (mandatory). -
    • B: Request headers. -
    • C: Request body (present only if the request body exists and -ModSecurity is configured to intercept it). -
    • D: Reserved for intermediary response headers; not implemented -yet. -
    • E: Intermediary response body (present only if ModSecurity is -configured to intercept response bodies, and if the audit log engine is -configured to record it). Intermediary response body is the same as the -actual response body unless ModSecurity intercepts the intermediary -response body, in which case the actual response body will contain the -error message (either the Apache default error message, or the -ErrorDocument page). -
    • F: Final response headers (excluding the Date and Server -headers, which are always added by Apache in the late stage of content -delivery). -
    • G: Reserved for the actual response body; not implemented yet. -
    • H: Audit log trailer. -
    • I: This part is a replacement for part C. It will log the same -data as C in all cases except when multipart/form-data encoding in used. - In this case, it will log a fake application/x-www-form-urlencoded body - that contains the information about parameters but not about the files. - This is handy if you don’t want to have (often large) files stored in -your audit logs. -
    • J: This part contains information about the files uploaded -using multipart/form-data encoding. -
    • K: This part contains a full list of every rule that matched -(one per line) in the order they were matched. The rules are fully -qualified and will thus show inherited actions and default operators. -Supported as of v2.5.0. -
    • Z: Final boundary, signifies the end of the entry (mandatory). -
    -

    - SecAuditLogRelevantStatus

    -

    Description: Configures which response status code is to be -considered relevant for the purpose of audit logging. -

    Syntax: SecAuditLogRelevantStatus REGEX -

    Example Usage: SecAuditLogRelevantStatus -"^(?:5|4(?!04))" -

    Scope: Any -

    Version: 2.0.0 -

    Dependencies/Notes: Must have SecAuditEngine set to -RelevantOnly. -

    The main purpose of this directive is to allow you to configure -audit logging for only the transactions that have the status code that -matches the supplied regular expression. The example provided would log -all 5xx and 4xx level status codes, except for 404s. Although you could -achieve the same effect with a rule in phase 5, -SecAuditLogRelevantStatus is sometimes better, because it continues to -work even when SecRuleEngine is disabled. -

    -

    SecAuditLogStorageDir

    -

    Description: Configures the directory where concurrent audit -log entries are to be stored. -

    Syntax: SecAuditLogStorageDir /path/to/storage/dir -

    Example Usage: SecAuditLogStorageDir -/usr/local/apache/logs/audit -

    Scope: Any -

    Version: 2.0.0 -

    This directive is only needed when concurrent audit logging is -used. The directory must already exist and must be writable by the web -server user. Audit log entries are created at runtime, after Apache -switches to a non-root account. -As with all logging mechanisms, ensure that you specify a file system -location that has adequate disk space and is not on the main system -partition. -

    -

    SecAuditLogType

    -

    Description: Configures the type of audit logging mechanism to - be used. -

    Syntax: SecAuditLogType Serial|Concurrent -

    Example Usage: SecAuditLogType Serial -

    Scope: Any -

    Version: 2.0.0 -

    The possible values are: -

    -
    Serial 
    Audit log entries will be stored in a -single file, specified by SecAuditLog. This is conve- nient for casual -use, but it can slow down the server, because only one audit log entry -can be written to the file at any one time. -
    Concurrent 
    One file per transaction is used for - audit logging. This approach is more scalable when heavy logging is -required (multiple transactions can be recorded in parallel). It is also - the only choice if you need to use remote logging. -
    -

    SecCacheTransformations

    -

    Description: Controls the caching of transformations, which -may speed up the processing of complex rule sets. Caching is off by -default starting with 2.5.6, when it was deprecated and downgraded back -to experimental. -

    Syntax: SecCacheTransformations On|Off [options] -

    Example Usage: SecCacheTransformations On -"minlen:64,maxlen:0" -

    Scope: Any -

    Version: 2.5.0; deprecated in 2.5.6. -

    The first directive parameter can be one of the following: -

    -
    • On: Cache transformations (per transaction, per phase) -allowing identical transforma- tions to be performed only once. -
    • Off: Do not cache any transformations, leaving all -transformations to be performed every time they are needed. -
    -

    The following options are allowed (multiple options must be -comma-separated): -

    -
    • incremental:on|off: Enabling this option will cache every - transformation instead of just the final transformation. The default is - off. -
    • maxitems:N: Do not allow more than N transformations to -be cached. Cache will be disabled once this number is reached. A zero -value is interpreted as unlimited. This option may be useful to limit -caching for a form with a large number of variables. The default value -is 512. -
    • minlen:N: Do not cache the transformation if the -variable’s length is less than N bytes. The default setting is 32. -
    • maxlen:N: Do not cache the transformation if the -variable’s length is more than N bytes. A zero value is interpreted as -unlimited. The default setting is 1024. -
    -

    SecChrootDir

    -

    Description: Configures the directory path that will be used -to jail the web server process. -

    Syntax: SecChrootDir /path/to/chroot/dir -

    Example Usage: SecChrootDir /chroot -

    Scope: Main -

    Version: 2.0.0 -

    This feature is not available on Windows builds. The internal -chroot functionality provided by ModSecurity works great for simple -setups. One example of a simple setup is Apache serving only static -files, or running applications using built-in modules. Some problems you - might encounter with more complex setups: -

    -
    1. DNS lookups do not work (this is because this feature requires a - shared library that is loaded on demand, after chroot takes place). -
    2. You cannot send email from PHP, because it wants to use -sendmail and sendmail re- sides outside the jail. -
    3. In some cases, when you separate Apache from its configuration, - restarts and graceful reloads no longer work. -
    -

    The best way to use SecChrootDir is the following: -

    -
    1. Create /chroot to be your main jail directory. -
    2. Create /chroot/opt/apache inside jail. -
    3. Create a symlink from /opt/apache to /chroot/opt/apache. -
    4. Now install Apache into /chroot/opt/apache. -
    -

    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. -

    -

    SecComponentSignature

    -

    Description: Appends component signature to the ModSecurity -signature. -

    Syntax: SecComponentSignature "COMPONENT_NAME/X.Y.Z -(COMMENT)" -

    Example usage: SecComponentSignature "core -ruleset/2.1.3" -

    Scope: Main -

    Version: 2.5.0 -

    This directive should be used to make the presence of significant - rule sets known. The entire signature will be recorded in the -transaction audit log. -

    -

    SecContentInjection

    -

    Description: Enables content injection using actions append -and prepend. -

    Syntax: SecContentInjection On|Off -

    Example Usage: SecContentInjection On -

    Scope: Any -

    Version: 2.5.0 -

    This directive provides an easy way to control content injection, - no matter what the rules want to do. It is not necessary to have -response body buffering enabled in order to use content injection. -

    -
    Note 
    This directive must ben enabled if you want - to use @rsub + the STREAM_ variables to manipulate live transactional -data. -
    -

    SecCookieFormat

    -

    Description: Selects the cookie format that will be used in -the current configuration context. -

    Syntax: SecCookieFormat 0|1 -

    Example Usage: SecCookieFormat 0 -

    Scope: Any -

    Version: 2.0.0 -

    The possible values are: -

    -
    • 0: Use version 0 (Netscape) cookies. This is what most -applications use. It is the default value. -
    • 1: Use version 1 cookies. -
    -

    - SecDataDir

    -

    Description: Path where persistent data (e.g., IP address -data, session data, and so on) is to be stored. -

    Syntax: SecDataDir /path/to/dir -

    Example Usage: SecDataDir -/usr/local/apache/logs/data -

    Scope: Main -

    Version: 2.0.0 -

    This directive must be provided before initcol, setsid, and -setuid can be used. The directory to which the directive points must be -writable by the web server user. -

    -

    SecDebugLog

    -

    Description: Path to the ModSecurity debug log file. -

    Syntax: SecDebugLog /path/to/modsec-debug.log -

    Example Usage: SecDebugLog -/usr/local/apache/logs/modsec-debug.log -

    Scope: Any -

    Version: 2.0.0 -

    -

    SecDebugLogLevel

    -

    Description: Configures the verboseness of the debug log data. - -

    Syntax: SecDebugLogLevel 0|1|2|3|4|5|6|7|8|9 -

    Example Usage: SecDebugLogLevel 4 -

    Scope: Any -

    Version: 2.0.0 -

    Messages at levels 1–3 are always copied to the Apache error log. - Therefore you can always use level 0 as the default logging level in -production if you are very concerned with performance. Having said that, - the best value to use is 3. Higher logging levels are not recommended -in production, because the heavy logging affects performance adversely. -

    The possible values for the debug log level are: -

    -
    • 0: no logging -
    • 1: errors (intercepted requests) only -
    • 2: warnings -
    • 3: notices -
    • 4: details of how transactions are handled -
    • 5: as above, but including information about each piece of -information handled -
    • 9: log everything, including very detailed debugging -information -
    -

    SecDefaultAction

    -

    Description: Defines the default list of actions, which will -be inherited by the rules in the same configuration context. -

    Syntax: SecDefaultAction "action1,action2,action3“ -

    Example Usage: SecDefaultAction -"phase:2,log,auditlog,deny,status:403“ -

    Scope: Any -

    Version: 2.0.0 -

    Default: phase:2,log,auditlog,pass -

    Every rule following a previous SecDefaultAction -directive in the same configuration context will inherit its settings -unless more specific actions are used. Every SecDefaultAction - directive must specify a disruptive action and a processing phase and -cannot contain metadata actions. -

    -
    Warning 
    SecDefaultAction is not -inherited across configuration contexts. (For an example of why this may - be a problem, read the following ModSecurity Blog entry http://blog.spiderlabs.com/2008/07/three-modsecurity-rule-language-annoyances.html.) -
    -

    - SecDisableBackendCompression

    -

    Description: Disables backend compression while leaving the -frontend compression enabled. -

    Syntax: SecDisableBackendCompression On|Off -

    Scope: Any -

    Version: 2.6.0 -

    Default: Off -

    This directive is necessary in reverse proxy mode when the -backend servers support response compression, but you wish to inspect -response bodies. Unless you disable backend compression, ModSecurity -will only see compressed content, which is not very useful. This -directive is not necessary in embedded mode, because ModSecurity -performs inspection before response compression takes place. -

    -

    SecEncryptionEngine

    -

    Description: Configures the encryption engine. -

    Syntax: SecEncryptionEngine On|Off -

    Example Usage: SecEncryptionEngine On -

    Scope: Any -

    Version: 2.7 -

    Default: Off -

    The possible values are: -

    -
    • On: Encryption engine can process the request/response -data. -
    • Off: Encryption engine will not process any data. -
    -
    Note 
    Users must enable stream output variables -and content injection. -
    -

    SecEncryptionKey

    -

    Description: Define the key that will be used by HMAC. -

    Syntax: SecEncryptionKey rand|TEXT -KeyOnly|SessionID|RemoteIP -

    Example Usage: SecEncryptionKey "this_is_my_key" -KeyOnly -

    Scope: Any -

    Version: 2.7 -

    ModSecurity encryption engine will append, if specified, the -user's session id or remote ip to the key before the MAC operation. If -the first parameter is "rand" then a random key will be generated and -used by the engine. -


    -

    -

    SecEncryptionParam

    -

    Description: Define the parameter name that will receive the -MAC hash. -

    Syntax: SecEncryptionParam TEXT -

    Example Usage: SecEncryptionKey "hmac" -

    Scope: Any -

    Version: 2.7 -

    ModSecurity encryption engine will add a new parameter to -protected HTML elements containing the MAC hash. -

    -

    SecEncryptionMethodRx

    -

    Description: Configures what kind of HTML data the encryption -engine should sign based on regular expression. -

    Syntax: SecEncryptionMethodRx TYPE REGEX -

    Example Usage: SecEncryptionMethodRx HashHref -"product_info|list_product" -

    Scope: Any -

    Version: 2.7.0 -

    As a initial support is possible to protect HREF, FRAME, IFRAME -and FORM ACTION html elements as well response Location header when http - redirect code are sent. -

    The possible values for TYPE are: -

    -
    • HashHref: Used to sign href= html elements -
    • HashFormAction: Used to sign form action= html elements -
    • HashIframeSrc: Used to sign iframe src= html elements -
    • HashframeSrc: Used to sign frame src= html elements -
    • HashLocation: Used to sign Location response header -
    -
    Note 
    This directive is used to sign the elements - however user must use the @validateEncryption operator to enforce data -integrity. -
    -


    -

    -

    SecEncryptionMethodPm

    -

    Description: Configures what kind of HTML data the encryption -engine should sign based on string search algoritm. -

    Syntax: SecEncryptionMethodRx TYPE "string1 string2 -string3..." -

    Example Usage: SecEncryptionMethodRx HashHref -"product_info list_product" -

    Scope: Any -

    Version: 2.7.0 -

    As a initial support is possible to protect HREF, FRAME, IFRAME -and FORM ACTION html elements as well response Location header when http - redirect code are sent. -

    The possible values for TYPE are: -

    -
    • HashHref: Used to sign href= html elements -
    • HashFormAction: Used to sign form action= html elements -
    • HashIframeSrc: Used to sign iframe src= html elements -
    • HashframeSrc: Used to sign frame src= html elements -
    • HashLocation: Used to sign Location response header -
    -
    Note 
    This directive is used to sign the elements - however user must use the @validateEncryption operator to enforce data -integrity. -
    -

    SecGeoLookupDb

    -

    Description: Defines the path to the database that will be -used for geolocation lookups. -

    Syntax: SecGeoLookupDb /path/to/db -

    Example Usage: SecGeoLookupDb -/path/to/GeoLiteCity.dat -

    Scope: Any -

    Version: 2.5.0 -

    ModSecurity relies on the free geolocation databases (GeoLite -City and GeoLite Country) that can be obtained from MaxMind [2]. -

    -

    SecGsbLookupDb

    -

    Description: Defines the path to the database that will be -used for Google Safe Browsing (GSB) lookups. -

    Syntax: SecGsbLookupDb /path/to/db -

    Example Usage: SecGsbLookupDb -/path/to/GsbMalware.dat -

    Scope: Any -

    Version: 2.6.0 -

    ModSecurity relies on the free Google Safe Browsing database that - can be obtained from the Google GSB API [3]. -

    -
    Note 
    Deprecated in 2.7.0 after Google dev team -decided to not allow the database download anymore. After registering -and obtaining a Safe Browsing API key, you can automatically download -the GSB using a tool like wget (where KEY is your own API -key): -
    -

    wget http://sb.google.com/safebrowsing/update?client=api&apikey=KEY&version=goog-malware-hash:1:-1 -

    -

    SecGuardianLog

    -

    Description: Configures an external program that will receive -the information about every transaction via piped logging. -

    Syntax: SecGuardianLog |/path/to/httpd-guardian -

    Example Usage: SecGuardianLog -|/usr/local/apache/bin/httpd-guardian -

    Scope: Main -

    Version: 2.0.0 -

    Guardian logging is designed to send the information about every -request to an external program. Because Apache is typically deployed in a - multiprocess fashion, which makes information sharing between processes - difficult, the idea is to deploy a single external process to observe -all requests in a stateful manner, providing additional protection. -

    Currently the only tool known to work with guardian logging is -httpd-guardian, which is part of the Apache httpd tools project [4]. The httpd-guardian tool is designed to defend -against denial of service attacks. It uses the blacklist tool (from the -same project) to interact with an iptables-based (on a Linux system) or -pf-based (on a BSD system) firewall, dynamically blacklisting the -offending IP addresses. It can also interact with SnortSam [5]. Assuming -httpd-guardian is already configured (look into the source code for the -detailed instructions), you only need to add one line to your Apache -configuration to deploy it: -

    -
    SecGuardianLog |/path/to/httpd-guardian
    -

    SecHttpBlKey

    -

    Description: Configures the user's registered Honeypot Project - HTTP BL API Key to use with @rbl. -

    Syntax: SecHttpBlKey [12 char access key] -

    Example Usage: SecHttpBlKey whdkfieyhtnf -

    Scope: Main -

    Version: 2.7.0 -

    If the @rbl operator uses the dnsbl.httpbl.org RBL (http://www.projecthoneypot.org/httpbl_api.php) you -must provide an API key. This key is registered to individual users and - is included within the RBL DNS requests. -

    -

    SecInterceptOnError

    -

    Description: Configures how to respond when rule processing -fails. -

    Syntax: SecInterceptOnError On|Off -

    Example Usage: SecInterceptOnError On -

    Scope: Main -

    Version: 2.6 -

    When an operator execution fails (ie returns <0), this -directive configures how to react. When set to "Off", the rule is just -ignored and the engine will continue executing the rules in phase. When - set to "On", the rule will be just dropped and no more rules will be -executed in the same phase, also no interception is made. -

    -

    -SecMarker

    -

    Description: Adds a fixed rule marker that can be used as a -target in a skipAfter action. A SecMarker directive essentially creates a - rule that does nothing and whose only purpose is to carry the given ID. -

    Syntax: SecMarker ID|TEXT -

    Example Usage: SecMarker 9999 -

    Scope: Any -

    Version: 2.5.0 -

    The value can be either a number or a text string. The SecMarker - directive is available to allow you to choose the best way to implement - a skip-over. Here is an example used from the Core Rule Set: -

    -
    SecMarker BEGIN_HOST_CHECK
    -
    -        SecRule &REQUEST_HEADERS:Host "@eq 0" \
    -                "skipAfter:END_HOST_CHECK,phase:2,rev:'2.1.1',t:none,block,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER_HOST',tag:'WASCTC/WASC-21',tag:'OWASP_TOP_10/A7',tag:'PCI/6.5.10',severity:'5',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score},setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/MISSING_HEADER-%{matched_var_name}=%{matched_var}"
    -        SecRule REQUEST_HEADERS:Host "^$" \
    -                "phase:2,rev:'2.1.1',t:none,block,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER_HOST',tag:'WASCTC/WASC-21',tag:'OWASP_TOP_10/A7',tag:'PCI/6.5.10',severity:'5',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score},setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/MISSING_HEADER-%{matched_var_name}=%{matched_var}"
    -
    -SecMarker END_HOST_CHECK
    -
    -

    SecPcreMatchLimit

    -

    Description: Sets the match limit in the PCRE library. -

    Syntax: SecPcreMatchLimit value -

    Example Usage: SecPcreMatchLimit 1500 -

    Scope: Main -

    Version: 2.5.12 -

    Default: 1500 -

    The default can be changed when ModSecurity is prepared for -compilation: the --enable-pcre-match-limit=val configure option will set - a custom default and the --disable-pcre-match-limit option will revert -back to the default of the PCRE library. -For more information, refer to the pcre_extra field in the pcreapi man -page. -

    -

    - SecPcreMatchLimitRecursion

    -

    Description: Sets the match limit recursion in the PCRE -library. -

    Syntax: SecPcreMatchLimitRecursion value -

    Example Usage: SecPcreMatchLimitRecursion 1500 -

    Scope: Main -

    Version: 2.5.12 -

    Default: 1500 -

    The default can be changed when ModSecurity is prepared for -compilation: the --enable-pcre-match-limit-recursion=val configure -option will set a custom default and the ---disable-pcre-match-limit-recursion option will revert back to the -default of the PCRE library. -For more information, refer to the pcre_extra field in the pcreapi man -page. -

    -

    SecPdfProtect

    -

    Description: Enables the PDF XSS protection functionality. -

    Syntax: SecPdfProtect On|Off -

    Example Usage: SecPdfProtect On -

    Scope: Any -

    Version: 2.5.0; removed from trunk -

    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. -

    -

    SecPdfProtectMethod

    -

    Description: Configure desired protection method to be used -when requests for PDF files are detected. -

    Syntax: SecPdfProtectMethod method -

    Example Usage: SecPdfProtectMethod TokenRedirection -

    Scope: Any -

    Version: 2.5.0; removed from trunk -

    Default: TokenRedirection -

    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 -works only 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!”). -

    -

    SecPdfProtectSecret

    -

    Description: Defines the secret that will be used to construct - one-time tokens. -

    Syntax: SecPdfProtectSecret secret -

    Example Usage: SecPdfProtectSecret -MyRandomSecretString -

    Scope: Any -

    Version: 2.5.0; removed from trunk -

    You should use a reasonably long value for the secret (e.g., 16 -characters is good). Once selected, the secret should not be changed, as - it will break the tokens that were sent prior to change. But it’s not a - big deal even if you change it. It will just force download of PDF -files with tokens that were issued in the last few seconds. -

    -

    SecPdfProtectTimeout

    -

    Description: Defines the token timeout. -

    Syntax: SecPdfProtectTimeout timeout -

    Example Usage: SecPdfProtectTimeout 10 -

    Scope: Any -

    Version: 2.5.0; removed from trunk -

    Default: 10 -

    After token expires, it can no longer be used to allow access to a - PDF file. Request will be allowed through but the PDF will be delivered - as an attachment. -

    -

    SecPdfProtectTokenName

    -

    Description: Defines the name of the token. -

    Syntax: SecPdfProtectTokenName name -

    Example Usage: SecPdfProtectTokenName PDFTOKEN -

    Scope: Any -

    Version: 2.5.0; removed from trunk -

    Default: PDFTOKEN -

    The only reason you would want to change the name of the token is - if you wanted to hide the fact that 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. -

    -

    SecReadStateLimit

    -

    Description: Establishes a per-IP address limit of how many -connections are allowed to be in SERVER_BUSY_READ state. -

    Syntax: SecReadStateLimit LIMIT -

    Example Usage: SecReadStateLimit 50 -

    Scope: Main -

    Version: 2.5.13 -

    Default: 0 (no limit) -

    This measure is effective against Slowloris-style attacks from a -single IP address, but it may not be as good against modified attacks -that work by slowly sending request body content. This is because Apache - to switches state to SERVER_BUSY_WRITE once request headers have been -read. As an alternative, consider mod_reqtimeout (part of Apache as of -2.2.15), which is expected be effective against both attack types. See -Blog post on mitigating slow DoS attacks - http://blog.spiderlabs.com/2010/11/advanced-topic-of-the-week-mitigating-slow-http-dos-attacks.html -

    -

    SecSensorId

    -

    Description: Define a sensor ID that will be present into log -part H. -

    Syntax: SecSensorId TEXT -

    Example Usage: SecSensorId WAFSensor01 -

    Scope: Main -

    Version: 2.7.0 -

    -

    SecWriteStateLimit

    -

    Description: Establishes a per-IP address limit of how many -connections are allowed to be in SERVER_BUSY_WRITE state. -

    Syntax: SecWriteStateLimit LIMIT -

    Example Usage: SecWriteStateLimit 50 -

    Scope: Main -

    Version: 2.6.0 -

    Default: 0 (no limit) -

    This measure is effective against Slow DoS request body attacks. -

    -

    SecRequestBodyAccess

    -

    Description: Configures whether request bodies will be -buffered and processed by ModSecurity. -

    Syntax: SecRequestBodyAccess On|Off -

    Example Usage: SecRequestBodyAccess On -

    Scope: Any -

    Version: 2.0.0 -

    This directive is required if you want to inspect the data -transported request bodies (e.g., POST parameters). Request buffering is - also required in order to make reliable blocking possible. The -possible values are: -

    -
    • On: buffer request bodies -
    • Off: do not buffer request bodies -
    -

    - SecRequestBodyInMemoryLimit

    -

    Description: Configures the maximum request body size that -ModSecurity will store in mem- ory. -

    Syntax: SecRequestBodyInMemoryLimit LIMIT_IN_BYTES -

    Example Usage: SecRequestBodyInMemoryLimit 131072 -

    Scope: Any -

    Version: 2.0.0 -

    Default: 131072 (128 KB) -

    When a multipart/form-data request is being processed, once the -in-memory limit is reached, the request body will start to be streamed -into a temporary file on disk. -

    -

    SecRequestBodyLimit

    -

    Description: Configures the maximum request body size -ModSecurity will accept for buffering. -

    Syntax: SecRequestBodyLimit LIMIT_IN_BYTES -

    Example Usage: SecRequestBodyLimit 134217728 -

    Scope: Any -

    Version: 2.0.0 -

    Default: 134217728 (131072 KB) -

    Anything over the limit will be rejected with status code 413 -(Request Entity Too Large). There is a hard limit of 1 GB. -

    -
    Note 
    In ModSecurity 2.5.x and earlier, -SecRequestBodyLimit works only when used in the main server -configuration, or a VirtualHost container. In these versions, request -body limit is enforced immediately after phase 1, but before phase 2 -configuration (i.e. whatever is placed in a Location container) is -resolved. You can work around this limitation by using a phase 1 rule -that changes the request body limit dynamically, using the -ctl:requestBodyLimit action. ModSecurity 2.6.x (currently in the trunk -only) and better do not have this limitation. -
    -

    - SecRequestBodyNoFilesLimit

    -

    Description: Configures the maximum request body size -ModSecurity will accept for buffering, excluding the size of any files -being transported in the request. This directive is useful to reduce -susceptibility to DoS attacks when someone is sending request bodies of -very large sizes. Web applications that require file uploads must -configure SecRequestBodyLimit to a high value, but because large files -are streamed to disk, file uploads will not increase memory consumption. - However, it’s still possible for someone to take advantage of a large -request body limit and send non-upload requests with large body sizes. -This directive eliminates that loophole. -

    Syntax: SecRequestBodyNoFilesLimit NUMBER_IN_BYTES -

    Example Usage: SecRequestBodyLimit 131072 -

    Scope: Any -

    Version: 2.5.0 -

    Default: 1048576 (1 MB) -

    Generally speaking, the default value is not small enough. 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. -

    -

    - SecRequestBodyLimitAction

    -

    Description: Controls what happens once a request body limit, -configured with SecRequestBodyLimit, is encountered -

    Syntax: SecRequestBodyLimitAction -Reject|ProcessPartial -

    Example Usage: SecRequestBodyLimitAction -ProcessPartial -

    Scope: Any -

    Version: 2.6.0 -

    By default, ModSecurity will reject a request body that is longer - than specified. This is problematic especially when ModSecurity is -being run in DetectionOnly mode and the intent is to be totally passive -and not take any disruptive actions against the transaction. With the -ability to choose what happens once a limit is reached, site -administrators can choose to inspect only the first part of the request, - the part that can fit into the desired limit, and let the rest through. - This is not ideal from a possible evasion issue perspective, however -it may be acceptable under certain circumstances. -

    -
    Note 
    When the SecRuleEngine is set to -DetectionOnly, SecRequestBodyLimitAction is automatically set to -ProcessPartial in order to not cause any disruptions. If you want to -know if/when a request body size is over your limit, you can create a -rule to check for the existence of the INBOUND_ERROR_DATA variable. -
    -

    SecResponseBodyLimit

    -

    Description: Configures the maximum response body size that -will be accepted for buffering. -

    Syntax: SecResponseBodyLimit LIMIT_IN_BYTES -

    Example Usage: SecResponseBodyLimit 524228 -

    Scope: Any -

    Version: 2.0.0 -

    Default: 524288 (512 KB) -

    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 selected for buffering. There is a hard limit -of 1 GB. -

    -

    - SecResponseBodyLimitAction

    -

    Description: Controls what happens once a response body limit, - configured with SecResponseBodyLimit, is encountered. -

    Syntax: SecResponseBodyLimitAction -Reject|ProcessPartial -

    Example Usage: SecResponseBodyLimitAction -ProcessPartial -

    Scope: Any -

    Version: 2.5.0 -

    By default, ModSecurity will reject a response body that is -longer than specified. Some web sites, however, will produce very long -responses, making it difficult to come up with a reasonable limit. Such -sites would have to raise the limit significantly to function properly, -defying the purpose of having the limit in the first place (to control -memory consumption). With the ability to choose what happens once a -limit is reached, site administrators can choose to inspect only the -first part of the response, the part that can fit into the desired -limit, and let the rest through. Some could argue that allowing parts of - responses to go uninspected is a weakness. This is true in theory, but -applies only to cases in which 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. -

    -

    SecResponseBodyMimeType

    -

    Description: Configures which MIME types are to be considered -for response body buffering. -

    Syntax: SecResponseBodyMimeType MIMETYPE MIMETYPE -... -

    Example Usage: SecResponseBodyMimeType text/plain -text/html text/xml -

    Scope: Any -

    Version: 2.0.0 -

    Default: text/plain text/html -

    Multiple SecResponseBodyMimeType directives can be used to add -MIME types. Use SecResponseBodyMimeTypesClear to clear previously -configured MIME types and start over. -

    -

    -SecResponseBodyMimeTypesClear

    -

    Description: Clears the list of MIME types considered for -response body buffering, allowing you to start populating the list from -scratch. -

    Syntax: SecResponseBodyMimeTypesClear -

    Example Usage: SecResponseBodyMimeTypesClear -

    Scope: Any -

    Version: 2.0.0 -

    -

    SecResponseBodyAccess

    -

    Description: Configures whether response bodies are to be -buffered. -

    Syntax: SecResponseBodyAccess On|Off -

    Example Usage: SecResponseBodyAccess On -

    Scope: Any -

    Version: 2.0.0 -

    Default: Off -

    This directive is required if you plan to inspect HTML responses -and implement response blocking. Possible values are: -

    -
    • On: buffer response bodies (but only if the response MIME type -matches the list configured with SecResponseBodyMimeType). -
    • Off: do not buffer response bodies. -
    -

    -SecRule

    -

    Description: Creates a rule that will analyze the selected -variables using the selected operator. -

    Syntax: SecRule VARIABLES OPERATOR [ACTIONS] -

    Example Usage: SecRule ARGS "@rx attack" -"phase:1,log,deny,id:1" -

    Scope: Any -

    Version: 2.0.0 -

    Every rule must provide one or more variables along with the -operator that should be used to inspect them. If no actions are -provided, the default list will be used. (There is always a default -list, even if one was not explicitly set with SecDefaultAction.) If -there are actions specified in a rule, they will be merged with the -default list to form the final actions that will be used. (The actions -in the rule will overwrite those in the default list.) Refer to -SecDefaultAction for more information. -

    -

    SecRuleInheritance

    -

    Description: Configures whether the current context will -inherit the rules from the parent context. -

    Syntax: SecRuleInheritance On|Off -

    Example Usage: SecRuleInheritance Off -

    Scope: Any -

    Version: 2.0.0 -

    Default: On -

    Sometimes when you create a more specific configuration context -(for example using the <Location> container), you may wish to use a - different set of rules than those used in the parent context. By -setting SecRuleInheritance to Off, you prevent the parent rules to be -inherited, which allows you to start from scratch. In ModSecurity 2.5.x -it is not possible to override phase 1 rules from a <Location> -configuration context. There are no limitations in that respect in the -current development version (and there won’t be in the next major -version). -

    The possible values are: -

    -
    • On: inherit rules from the parent context -
    • Off: do not inherit rules from the parent context -
    -
    Note 
    Configuration contexts are an Apache -concept. Directives <Directory>, <Files>, <Location>, -and <VirtualHost> are all used to create configuration contexts. -For more information, please go to the Apache documentation, under -Configuration Sections [6]. This directive does not affect how configuration - options are inherited. -
    -

    SecRuleEngine

    -

    Description: Configures the rules engine. -

    Syntax: SecRuleEngine On|Off|DetectionOnly -

    Example Usage: SecRuleEngine On -

    Scope: Any -

    Version: 2.0.0 -

    Default: Off -

    The possible values are: -

    -
    • On: process rules -
    • Off: do not process rules -
    • DetectionOnly: process rules but never executes any -disruptive actions (block, deny, drop, allow, proxy and redirect) -
    -

    SecRulePerfTime

    -

    Description: Set a performance threshold for rules. Rules that - spends too much time will be logged into audit log Part H in the format - id=usec. -

    Syntax: SecRulePerfTime USECS -

    Example Usage: SecRulePerfTime 1000 -

    Scope: Any -

    Version: 2.7 -

    -

    SecRuleRemoveById

    -

    Description: Removes the matching rules from the current -configuration context. -

    Syntax: SecRuleRemoveById ID ID RANGE ... -

    Example Usage: SecRuleRemoveByID 1 2 "9000-9010" -

    Scope: Any -

    Version: 2.0.0 -

    This directive supports multiple parameters, each of which can be - a rule ID or a range. Parameters that contain spaces must be delimited -using double quotes. -

    -
    Note 
    This directive must be specified after -the rule in which it is disabling. This should be used within local - custom rule files that are processed after third party rule sets. -Example file - modsecurity_crs_60_customrules.conf. -
    -

    SecRuleRemoveByMsg

    -

    Description: Removes the matching rules from the current -configuration context. -

    Syntax: SecRuleRemoveByMsg REGEX -

    Example Usage: SecRuleRemoveByMsg "FAIL" -

    Scope: Any -

    Version: 2.0.0 -

    Normally, you would use SecRuleRemoveById to remove rules, but -that requires the rules to have IDs defined. If they don’t, then you can - remove them with SecRuleRemoveByMsg, which matches a regular expression - against rule messages. -

    -
    Note 
    This directive must be specified after the -rule in which it is disabling. This should be used within local custom -rule files that are processed after third party rule sets. Example file - - modsecurity_crs_60_customrules.conf. -
    -

    SecRuleRemoveByTag

    -

    Description: Removes the matching rules from the current -configuration context. -

    Syntax: SecRuleRemoveByTab REGEX -

    Example Usage: SecRuleRemoveByTag "WEB_ATTACK/XSS" -

    Scope: Any -

    Version: 2.6 -

    Normally, you would use SecRuleRemoveById to remove rules, but -that requires the rules to have IDs defined. If they don’t, then you can - remove them with SecRuleRemoveByTag, which matches a regular expression - against rule tag data. This is useful if you want to disable entire -groups of rules based on tag data. Example tags used in the OWASP -ModSecurity CRS include: -

    -
    • AUTOMATION/MALICIOUS -
    • AUTOMATION/MISC -
    • AUTOMATION/SECURITY_SCANNER -
    • LEAKAGE/SOURCE_CODE_ASP_JSP -
    • LEAKAGE/SOURCE_CODE_CF -
    • LEAKAGE/SOURCE_CODE_PHP -
    • WEB_ATTACK/CF_INJECTION -
    • WEB_ATTACK/COMMAND_INJECTION -
    • WEB_ATTACK/FILE_INJECTION -
    • WEB_ATTACK/HTTP_RESPONSE_SPLITTING -
    • WEB_ATTACK/LDAP_INJECTION -
    • WEB_ATTACK/PHP_INJECTION -
    • WEB_ATTACK/REQUEST_SMUGGLING -
    • WEB_ATTACK/SESSION_FIXATION -
    • WEB_ATTACK/SQL_INJECTION -
    • WEB_ATTACK/SSI_INJECTION -
    • WEB_ATTACK/XSS -
    -
    Note 
    This directive must be specified after the -rule in which it is disabling. This should be used within local custom -rule files that are processed after third party rule sets. Example file - - modsecurity_crs_60_customrules.conf. -
    -

    SecRuleScript

    -

    Description: This directive creates a special rule that executes a -Lua script to decide whether to match or not. The main difference from -SecRule is that there are no targets nor operators. The script can fetch - any variable from the ModSecurity context and use any (Lua) operator to - test them. The second optional parameter is the list of actions whose -meaning is identical to that of SecRule. -

    Syntax: SecRuleScript /path/to/script.lua [ACTIONS] -

    Example Usage: SecRuleScript "/path/to/file.lua" -"block" -

    Scope: Any -

    Version: 2.5.0 -

    -
    Note 
    All Lua scripts are compiled at -configuration time and cached in memory. To reload scripts you must -reload the entire ModSecurity configuration by restarting Apache. -
    -

    Example script: -

    -
    -- Your script must define the main entry
    --- point, as below.
    -function main()
    -    -- Log something at level 1. Normally you shouldn't be
    -    -- logging anything, especially not at level 1, but this is
    -    -- just to show you can. Useful for debugging.
    -    m.log(1, "Hello world!");
    -
    -    -- Retrieve one variable.
    -    local var1 = m.getvar("REMOTE_ADDR");
    -
    -    -- Retrieve one variable, applying one transformation function.
    -    -- The second parameter is a string.
    -    local var2 = m.getvar("ARGS", "lowercase");
    -
    -    -- Retrieve one variable, applying several transformation functions.
    -    -- The second parameter is now a list. You should note that m.getvar()
    -    -- requires the use of comma to separate collection names from
    -    -- variable names. This is because only one variable is returned.
    -    local var3 = m.getvar("ARGS.p", { "lowercase", "compressWhitespace" } );
    -
    -    -- If you want this rule to match return a string
    -    -- containing the error message. The message must contain the name
    -    -- of the variable where the problem is located.
    -    -- return "Variable ARGS:p looks suspicious!"
    -
    -    -- Otherwise, simply return nil.
    -    return nil;
    -end
    -
    -

    In this first example we were only retrieving one variable at the -time. In this case the name of the variable is known to you. In many -cases, however, you will want to examine variables whose names you won't - know in advance, for example script parameters. -

    Example showing use of m.getvars() to retrieve many variables at -once: -

    -
    function main()
    -    -- Retrieve script parameters.
    -    local d = m.getvars("ARGS", { "lowercase", "htmlEntityDecode" } );
    -
    -    -- Loop through the paramters.
    -    for i = 1, #d do
    -        -- Examine parameter value.
    -        if (string.find(d[i].value, "<script")) then
    -            -- Always specify the name of the variable where the
    -            -- problem is located in the error message.
    -            return ("Suspected XSS in variable " .. d[i].name .. ".");
    -        end
    -    end
    -
    -    -- Nothing wrong found.
    -    return nil;
    -end
    -
    -
    Note 
    Go to http://www.lua.org/ - to find more about the Lua programming language. The reference manual -too is available online, at http://www.lua.org/manual/5.1/. -
    -
    Note 
    Lua support is marked as experimental as -the way the progamming interface may continue to evolve while we are -working for the best implementation style. Any user input into the -programming interface is appreciated. -
    -

    SecRuleUpdateActionById

    -

    Description: Updates the action list of the specified rule. -

    Syntax: SecRuleUpdateActionById RULEID[:offset] -ACTIONLIST -

    Example Usage: SecRuleUpdateActionById 12345 -"deny,status:403" -

    Scope: Any -

    Version: 2.6.0 -

    This directive will overwrite the action list of the specified -rule with the actions provided in the second parameter. It has two -limitations: it cannot be used to change the ID or phase of a rule. Only - the actions that can appear only once are overwritten. The actions that - are allowed to appear multiple times in a list, will be appended to the - end of the list. -

    -
    SecRule ARGS attack "phase:2,id:12345,t:lowercase,log,pass,msg:'Message text'"
    -SecRuleUpdateActionById 12345 "t:none,t:compressWhitespace,deny,status:403,msg:'New message text'"
    -
    -

    The effective resulting rule in the previous example will be as -follows: -

    -
    SecRule ARGS attack "phase:2,id:12345,t:lowercase,t:none,t:compressWhitespace,deny,status:403,msg:'New Message text'"
    -
    -

    The addition of t:none will neutralize any previous transformation -functions specified (t:lowercase, in the example). -

    -
    Note 
    If the target rule is a chained rule, you -must currently specify chain in the SecRuleUpdateActionById action list -as well. This will be fixed in a future version. -
    -

    SecRuleUpdateTargetById

    -

    Description: Updates the target (variable) list of the -specified rule. -

    Syntax: SecRuleUpdateTargetById RULEID -TARGET1[,TARGET2,TARGET3] REPLACED_TARGET -

    Example Usage: SecRuleUpdateTargetById 12345 -"!ARGS:foo" -

    Scope: Any -

    Version: 2.6 -

    This directive will append (or replace) variables to the current -target list of the specified rule with the targets provided in the -second parameter. Starting with 2.7.0 this feature supports id range. -

    Explicitly Appending Targets -

    This is useful for implementing exceptions where you want to -externally update a target list to exclude inspection of specific -variable(s). -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}"
    -
    -SecRuleUpdateTargetById 958895 !ARGS:email
    -
    -

    The effective resulting rule in the previous example will append the -target to the end of the variable list as follows: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/*|!ARGS:email "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}""
    -
    -

    Explicitly Replacing Targets -

    You can also entirely replace the target list to something more -appropriate for your environment. For example, lets say you want to -inspect REQUEST_URI instead of REQUEST_FILENAME, you could do this: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}"
    -
    -SecRuleUpdateTargetById 958895 REQUEST_URI REQUEST_FILENAME
    -
    -

    The effective resulting rule in the previous example will append the -target to the end of the variable list as follows: -

    -
    SecRule REQUEST_URI|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}""
    -
    -

    Conditionally Appending Targets -

    You could also do the same by using the ctl action. This is -useful if you want to only update the targets for a particular URL -

    -
    SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,id:2,t:none,nolog,pass,ctl:ruleUpdateTargetById=958895;!ARGS:email"
    -
    -

    Conditionally Replacing Targets -

    You could also replace targets using the ctl action. For -example, lets say you want to only inspect ARGS for a particular URL: -

    -
    SecRule REQUEST_FILENAME "@streq /path/to/file.php" "phase:1,id:3,t:none,nolog,pass,ctl:ruleUpdateTargetById=958895;REQUEST_URI;REQUEST_FILENAME"
    -
    -
    Note 
    This ctl is deprecated and will be removed -from the code, since we cannot use it per-transaction. Please use the -new ruleRemoveTargetById ctl option if you want to remove a target or -target list per-transaction. -
    -

    - SecRuleUpdateTargetByMsg

    -

    Description: Updates the target (variable) list of the -specified rule by rule message. -

    Syntax: SecRuleUpdateTargetByMsg TEXT -TARGET1[,TARGET2,TARGET3] REPLACED_TARGET -

    Example Usage: SecRuleUpdateTargetByMsg "Cross-site -Scripting (XSS) Attack" "!ARGS:foo" -

    Scope: Any -

    Version: 2.7 -

    This directive will append (or replace) variables to the current -target list of the specified rule with the targets provided in the -second parameter. -

    Explicitly Appending Targets -

    This is useful for implementing exceptions where you want to -externally update a target list to exclude inspection of specific -variable(s). -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}"
    -
    -SecRuleUpdateTargetByMsg "System Command Injection" !ARGS:email
    -
    -

    The effective resulting rule in the previous example will append the -target to the end of the variable list as follows: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/*|!ARGS:email "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}""
    -
    -

    Explicitly Replacing Targets -

    You can also entirely replace the target list to something more -appropriate for your environment. For example, lets say you want to -inspect REQUEST_URI instead of REQUEST_FILENAME, you could do this: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}"
    -
    -SecRuleUpdateTargetByMsg "System Command Injection" REQUEST_URI REQUEST_FILENAME
    -
    -

    The effective resulting rule in the previous example will append the -target to the end of the variable list as follows: -

    -
    SecRule REQUEST_URI|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}""
    -
    -

    - SecRuleUpdateTargetByTag

    -

    Description: Updates the target (variable) list of the -specified rule by rule tag. -

    Syntax: SecRuleUpdateTargetByTag TEXT -TARGET1[,TARGET2,TARGET3] REPLACED_TARGET -

    Example Usage: SecRuleUpdateTargetByTag -"WEB_ATTACK/XSS" "!ARGS:foo" -

    Scope: Any -

    Version: 2.7 -

    This directive will append (or replace) variables to the current -target list of the specified rule with the targets provided in the -second parameter. -

    Explicitly Appending Targets -

    This is useful for implementing exceptions where you want to -externally update a target list to exclude inspection of specific -variable(s). -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}"
    -
    -SecRuleUpdateTargetByTag "WASCTC/WASC-31" !ARGS:email
    -
    -

    The effective resulting rule in the previous example will append the -target to the end of the variable list as follows: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/*|!ARGS:email "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}""
    -
    -

    Explicitly Replacing Targets -

    You can also entirely replace the target list to something more -appropriate for your environment. For example, lets say you want to -inspect REQUEST_URI instead of REQUEST_FILENAME, you could do this: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}"
    -
    -SecRuleUpdateTargetByTag "WASCTC/WASC-31" REQUEST_URI REQUEST_FILENAME
    -
    -

    The effective resulting rule in the previous example will append the -target to the end of the variable list as follows: -

    -
    SecRule REQUEST_URI|ARGS_NAMES|ARGS|XML:/* "[\;\|\`]\W*?\bmail\b" \
    -     "phase:2,rev:'2.1.1',capture,t:none,t:htmlEntityDecode,t:compressWhitespace,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'958895',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%
    -{tx.0}""
    -
    -

    SecServerSignature

    -

    Description: Instructs ModSecurity to change the data -presented in the "Server:" response header token. -

    Syntax: SecServerSignature "WEB SERVER SOFTWARE" -

    Example Usage: SecServerSignature -"Microsoft-IIS/6.0" -

    Scope: Main -

    Version: 2.0.0 -

    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. -

    -

    - SecStreamInBodyInspection

    -

    Description: Configures the ability to use stream inspection -for inbound request data in a re-allocable buffer. For security reasons -we are still buffering the stream. -

    Syntax: SecStreamInBodyInspection On|Off -

    Example Usage: SecStreamInBodyInspection On -

    Scope: Any -

    Version: 2.6.0 -

    Default: Off -

    This feature enables the creation of the STREAM_INPUT_BODY -variable and is useful for data modification or to match data in raw -data for any content-types. -

    -
    Note 
    This directive provides full access to -REQUEST_BODY payload data. It does not include REQUEST_URI or -REQUEST_HEADER data. Also it provides data to all kind of content types, - different than REQUEST_BODY. -
    -

    - SecStreamOutBodyInspection

    -

    Description: Configures the ability to use stream inspection -for outbound request data in a re-allocable buffer. For security -reasons we are still buffering the stream. -

    Syntax: SecStreamOutBodyInspection On|Off -

    Example Usage: SecStreamOutBodyInspection On -

    Scope: Any -

    Version: 2.6.0 -

    Default: Off -

    This feature enables the creation of the STREAM_OUTPUT_BODY -variable and is useful when you need to do data modification into -response body. -

    -
    Note 
    This directive provides access to -RESPONSE_BODY payload data. It does not include RESPONSE_HEADER data. -
    -

    -SecTmpDir

    -

    Description: Configures the directory where temporary files -will be created. -

    Syntax: SecTmpDir /path/to/dir -

    Example Usage: SecTmpDir /tmp -

    Scope: Any -

    Version: 2.0.0 -

    The location specified needs to be writable by the Apache user -process. This is the directory location where ModSecurity will swap data - to disk if it runs out of memory (more data than what was specified in -the SecRequestBodyInMemoryLimit directive) during inspection. -

    -

    SecUnicodeMapFile

    -

    Description: Defines the path to the file that will be used by - the urlDecodeUni transformation function to map Unicode code points -during normalization. -

    Syntax: SecUnicodeMapFile /path/to/unicode.mapping -

    Example Usage: SecUnicodeMapFile -/usr/local/apache/conf/crs/unicode.mapping -

    Scope: Any -

    Version: 2.6.1 -

    -

    SecUnicodeCodePage

    -

    Description: Defines which Unicode code point will be used by -the urlDecodeUni transformation function during normalization. -

    Syntax: SecUnicodeCodePage XXXXX -

    Example Usage: SecUnicodeCodePage 20127 -

    Scope: Any -

    Version: 2.6.1 -

    -

    SecUploadDir

    -

    Description: Configures the directory where intercepted files -will be stored. -

    Syntax: SecUploadDir /path/to/dir -

    Example Usage: SecUploadDir /tmp -

    Scope: Any -

    Version: 2.0.0 -

    This directory must be on the same filesystem as the temporary -directory defined with SecTmpDir. This directive is used with -SecUploadKeepFiles. -

    -

    SecUploadFileLimit

    -

    Description: Configures the maximum number of file uploads -processed in a multipart POST. -

    Syntax: SecUploadFileLimit number -

    Example Usage: SecUploadFileLimit 10 -

    Scope: Any -

    Version: 2.5.12 -

    The default is set to 100 files, but you are encouraged to reduce - this value. Any file over the limit will not be extracted and the -MULTIPART_FILE_LIMIT_EXCEEDED and MULTIPART_STRICT_ERROR flags will be -set. To prevent bypassing any file checks, you must check for one of -these flags. -

    -
    Note 
    If the limit is exceeded, the part name and - file name will still be recorded in FILES_NAME and FILES, the file size - will be recorded in FILES_SIZES, but there will be no record in -FILES_TMPNAMES as a temporary file was not created. -
    -

    SecUploadFileMode

    -

    Description: Configures the mode (permissions) of any uploaded - files using an octal mode (as used in chmod). -

    Syntax: SecUploadFileMode octal_mode|"default" -

    Example Usage: SecUploadFileMode 0640 -

    Scope: Any -

    Version: 2.1.6 -

    This feature is not available on operating systems not supporting - octal file modes. The default mode (0600) only grants read/write access - to the account writing the file. If access from another account is -needed (using clamd is a good example), then this directive may be -required. However, use this directive with caution to avoid exposing -potentially sensitive data to unauthorized users. Using the value -"default" will revert back to the default setting. -

    -
    Note 
    The process umask may still limit the mode -if it is being more restrictive than the mode set using this directive. -
    -

    SecUploadKeepFiles

    -

    Description: Configures whether or not the intercepted files -will be kept after transaction is processed. -

    Syntax: SecUploadKeepFiles On|Off|RelevantOnly -

    Example Usage: SecUploadKeepFiles On -

    Scope: Any -

    Version: 2.0.0 -

    This directive requires the storage directory to be defined -(using SecUploadDir). -

    Possible values are: -

    -
    • On - Keep uploaded files. -
    • Off - Do not keep uploaded files. -
    • RelevantOnly - This will keep only those files that -belong to requests that are deemed relevant. -
    -

    SecWebAppId

    -

    Description: Creates an application namespace, allowing for -separate persistent session and user storage. -

    Syntax: SecWebAppId "NAME" -

    Example Usage: SecWebAppId "WebApp1" -

    Scope: Any -

    Version: 2.0.0 -

    Default: default -

    Application namespaces are used to avoid collisions between -session IDs and user IDs when multiple applications are deployed on the -same server. If it isn’t used, a collision between session IDs might -occur. -

    -
    <VirtualHost *:80> 
    -ServerName app1.example.com 
    -SecWebAppId "App1" ...
    -</VirtualHost>
    -
    -<VirtualHost *:80> 
    -ServerName app2.example.com 
    -SecWebAppId "App2" ...
    -</VirtualHost>
    -
    -

    In the two examples configurations shown, SecWebAppId is being used -in conjunction with the Apache VirtualHost directives. Applications -namespace information is also recorded in the audit logs (using the -WebApp-Info header of the H part). -

    This directive is used to set collections timeout. For example: -

    -
    SecCollectionTimeout 500
    -

    SecCollectionTimeout

    -

    Description: Specifies the collections timeout. Default is -3600 seconds. -

    Syntax: SecCollectionTimeout seconds -

    Default: 3600 -

    Scope: Any -

    Version: 2.6.3 -

    -

    Processing Phases

    -

    ModSecurity 2.x allows rules to be placed in one of the following -five phases of the Apache request cycle: -

    -
    • Request headers (REQUEST_HEADERS) -
    • Request body (REQUEST_BODY) -
    • Response headers (RESPONSE_HEADERS) -
    • Response body (RESPONSE_BODY) -
    • Logging (LOGGING) -
    -

    Below is a diagram of the standard Apache Request Cycle. In the -diagram, the 5 ModSecurity processing phases are shown. -

    -

    In order to select the phase a rule executes during, use the -phase action either directly in the rule or in using the -SecDefaultAction directive: -

    -
    SecDefaultAction "log,pass,phase:2,id:4"
    -SecRule REQUEST_HEADERS:Host "!^$" "deny,phase:1,id:5"
    -
    -
    Note 
    The data available in each phase is -cumulative. This means that as you move onto later phases, you have -access to more and more data from the transaction. -
    Note 
    Keep in mind that rules are executed -according to phases, so even if two rules are adjacent in a -configuration file, but are set to execute in different phases, they -would not happen one after the other. The order of rules in the -configuration file is important only within the rules of each phase. -This is especially important when using the skip and skipAfter actions. -
    -
    Note 
    The LOGGING phase is special. It is -executed at the end of each transaction no matter what happened in the -previous phases. This means it will be processed even if the request was - intercepted or the allow action was used to pass the transaction -through. -
    -

    Phase Request Headers

    -

    Rules in this phase are processed immediately after Apache completes -reading the request headers (post-read-request phase). At this point the - request body has not been read yet, meaning not all request arguments -are available. Rules should be placed in this phase if you need to have -them run early (before Apache does something with the request), to do -something before the request body has been read, determine whether or -not the request body should be buffered, or decide how you want the -request body to be processed (e.g. whether to parse it as XML or not). -

    -
    Note 
    Rules in this phase can not leverage Apache - scope directives (Directory, Location, LocationMatch, etc...) as the -post-read-request hook does not have this information yet. The exception - here is the VirtualHost directive. If you want to use ModSecurity rules - inside Apache locations, then they should run in Phase 2. Refer to the -Apache Request Cycle/ModSecurity Processing Phases diagram. -
    -

    Phase Request Body

    -

    This is the general-purpose input analysis phase. Most of the -application-oriented rules should go here. In this phase you are -guaranteed to have received the request arguments (provided the request -body has been read). ModSecurity supports three encoding types for the -request body phase: -

    -
    • application/x-www-form-urlencoded - used to transfer form - data -
    • multipart/form-data - used for file transfers -
    • text/xml - used for passing XML data -
    -

    Other encodings are not used by most web applications. -

    -
    Note 
    In order to access the Request Body phase -data, you must have SecRequestBodyAccess set to On. -
    -

    Phase Response Headers

    -

    This phase takes place just before response headers are sent back to -the client. Run here if you want to observe the response before that -happens, and if you want to use the response headers to determine if you - want to buffer the response body. Note that some response status codes -(such as 404) are handled earlier in the request cycle by Apache and my -not be able to be triggered as expected. Additionally, there are some -response headers that are added by Apache at a later hook (such as Date, - Server and Connection) that we would not be able to trigger on or -sanitize. This should work appropriately in a proxy setup or within -phase:5 (logging). -

    -

    Phase Response Body

    -

    This is the general-purpose output analysis phase. At this point you -can run rules against the response body (provided it was buffered, of -course). This is the phase where you would want to inspect the outbound -HTML for information disclosure, error messages or failed authentication - text. -

    -
    Note 
    In order to access the Response Body phase -data, you must have SecResponseBodyAccess set to On -
    -

    Phase Logging

    -

    This phase is run just before logging takes place. The rules placed -into this phase can only affect how the logging is performed. This phase - can be used to inspect the error messages logged by Apache. You cannot -deny/block connections in this phase as it is too late. This phase also -allows for inspection of other response headers that weren't available -during phase:3 or phase:4. Note that you must be careful not to inherit a - disruptive action into a rule in this phase as this is a configuration -error in ModSecurity 2.5.0 and later versions -

    -

    -Variables

    -

    The following variables are supported in ModSecurity 2.x: -

    -

    ARGS

    -

    ARGS is a collection and can be used on its own (means all arguments -including the POST Payload), with a static parameter (matches arguments -with that name), or with a regular expression (matches all arguments -with name that matches the regular expression). To look at only the -query string or body arguments, see the ARGS_GET and ARGS_POST -collections. -

    Some variables are actually collections, which are expanded into -more variables at runtime. The following example will examine all -request arguments: -

    SecRule ARGS dirty "id:7" -

    Sometimes, however, you will want to look only at parts of a -collection. This can be achieved with the help of the selection -operator(colon). The following example will only look at the arguments -named p (do note that, in general, requests can contain multiple -arguments with the same name): -

    SecRule ARGS:p dirty "id:8" -

    It is also possible to specify exclusions. The following will -examine all request arguments for the word dirty, except the ones named z - (again, there can be zero or more arguments named z): -

    SecRule ARGS|!ARGS:z dirty "id:9" -

    There is a special operator that allows you to count how many -variables there are in a collection. The following rule will trigger if -there is more than zero arguments in the request (ignore the second -parameter for the time being): -

    SecRule &ARGS !^0$ "id:10" -

    And sometimes you need to look at an array of parameters, each -with a slightly different name. In this case you can specify a regular -expression in the selection operator itself. The following rule will -look into all arguments whose names begin with id_: -

    SecRule ARGS:/^id_/ dirty "id:11" -

    -
    Note 
    Using ARGS:p will not result in any -invocations against the operator if argument p does not exist. -
    -

    In ModSecurity 1.X, the ARGS variable stood for QUERY_STRING + -POST_PAYLOAD, whereas now it expands to individual variables. -

    -

    ARGS_COMBINED_SIZE

    -

    Contains the combined size of all request parameters. Files are -excluded from the calculation. This variable can be useful, for example, - to create a rule to ensure that the total size of the argument data is -below a certain threshold. The following rule detects a request whose -para- meters are more than 2500 bytes long: -

    SecRule ARGS_COMBINED_SIZE "@gt 2500" "id:12" -

    -

    -ARGS_GET

    -

    ARGS_GET is similar to ARGS, but contains only query string -parameters. -

    -

    ARGS_GET_NAMES

    -

    ARGS_GET_NAMES is similar to ARGS_NAMES, but contains only the names -of query string parameters. -

    -

    - ARGS_NAMES

    -

    Contains all request parameter names. You can search for specific -parameter names that you want to inspect. In a positive policy scenario, - you can also whitelist (using an inverted rule with the exclamation -mark) only the authorized argument names. -This example rule allows only two argument names: p and a: -

    SecRule ARGS_NAMES "!^(p|a)$" "id:13" -

    -

    -ARGS_POST

    -

    ARGS_POST is similar to ARGS, but only contains arguments from the -POST body. -

    -

    ARGS_POST_NAMES

    -

    ARGS_POST_NAMES is similar to ARGS_NAMES, but contains only the names - of request body parameters. -

    -

    -AUTH_TYPE

    -

    This variable holds the authentication method used to validate a -user, if any of the methods built into HTTP are used. In a reverse-proxy - deployment, this information will not be available if the -authentication is handled in the backend web server. -

    SecRule AUTH_TYPE "Basic" "id:14" -

    -

    -DURATION

    -

    Contains the number of milliseconds elapsed since the beginning of -the current transaction. Available starting with 2.6.0. -

    -
    Note 
    Starting with ModSecurity 2.7.0 the time is - microseconds. -
    -

    ENV

    -

    Collection that provides access to environment variables set by -ModSecurity. Requires a single parameter to specify the name of the -desired variable. -

    -
    # Set environment variable 
    -SecRule REQUEST_FILENAME "printenv" \
    -"phase:2,id:15,pass,setenv:tag=suspicious" 
    -
    -# Inspect environment variable
    -SecRule ENV:tag "suspicious" "id:16"
    -
    -
    Note 
    Use setenv to set environment variables to -be accessed by Apache. -
    -

    FILES

    -

    Contains a collection of original file names (as they were called on -the remote user’s filesys- tem). Available only on inspected -multipart/form-data requests. -

    SecRule FILES "@rx \.conf$" "id:17" -

    -
    Note 
    Only available if files were extracted from - the request body. -
    -

    FILES_COMBINED_SIZE

    -

    Contains the total size of the files transported in request body. -Available only on inspected multipart/form-data requests. -

    SecRule FILES_COMBINED_SIZE "@gt 100000" "id:18" -

    -

    FILES_NAMES

    -

    Contains a list of form fields that were used for file upload. -Available only on inspected multipart/form-data requests. -

    SecRule FILES_NAMES "^upfile$" "id:19" -

    -

    FILES_SIZES

    -

    Contains a list of individual file sizes. Useful for implementing a -size limitation on individual uploaded files. Available only on -inspected multipart/form-data requests. -

    SecRule FILES_SIZES "@gt 100" "id:20" -

    -

    FILES_TMPNAMES

    -

    Contains a list of temporary files’ names on the disk. Useful when -used together with @inspectFile. Available only on inspected -multipart/form-data requests. -

    SecRule FILES_TMPNAMES "@inspectFile -/path/to/inspect_script.pl" "id:21" -

    -

    GEO

    -

    GEO is a collection populated by the results of the last @geoLookup -operator. The collection can be used to match geographical fields looked - from an IP address or hostname. -

    Available since ModSecurity 2.5.0. -

    Fields: -

    -
    • COUNTRY_CODE: Two character country code. EX: US, GB, etc. -
    • COUNTRY_CODE3: Up to three character country code. -
    • COUNTRY_NAME: The full country name. -
    • COUNTRY_CONTINENT: The two character continent that the country - is located. EX: EU -
    • REGION: The two character region. For US, this is state. For -Canada, providence, etc. -
    • CITY: The city name if supported by the database. -
    • POSTAL_CODE: The postal code if supported by the database. -
    • LATITUDE: The latitude if supported by the database. -
    • LONGITUDE: The longitude if supported by the database. -
    • DMA_CODE: The metropolitan area code if supported by the -database. (US only) -
    • AREA_CODE: The phone system area code. (US only) -
    -

    Example: -

    -
    SecGeoLookupDb /usr/local/geo/data/GeoLiteCity.dat
    -...
    -SecRule REMOTE_ADDR "@geoLookup" "chain,id:22,drop,msg:'Non-GB IP address'"
    -SecRule GEO:COUNTRY_CODE "!@streq GB"
    -
    -

    HIGHEST_SEVERITY

    -

    This variable holds the highest severity of any rules that have -matched so far. Severities are numeric values and thus can be used with -comparison operators such as @lt, and so on. A value of 255 indicates -that no severity has been set. -

    SecRule HIGHEST_SEVERITY "@le 2" -"phase:2,id:23,deny,status:500,msg:'severity %{HIGHEST_SEVERITY}'" -

    -
    Note 
    Higher severities have a lower numeric -value. -
    -

    INBOUND_DATA_ERROR

    -

    This variable will be set to 1 when the request body size is above -the setting configured by SecRequestBodyLimit directive. Your policies -should always contain a rule to check this variable. Depending on the -rate of false positives and your default policy you should decide -whether to block or just warn when the rule is triggered. -

    The best way to use this variable is as in the example below: -

    SecRule INBOUND_DATA_ERROR "@eq 1" -"phase:1,id:24,t:none,log,pass,msg:'Request Body Larger than -SecRequestBodyLimit Setting'" -

    -

    MATCHED_VAR

    -

    This variable holds the value of the most-recently matched variable. -It is similar to the TX:0, but it is automatically supported by all -operators and there is no need to specify the capture action. -

    -
    SecRule ARGS pattern chain,deny,id:25
    -  SecRule MATCHED_VAR "further scrutiny"
    -
    -
    Note 
    Be aware that this variable holds data for -the last operator match. This means that if there are -more than one matches, only the last one will be populated. Use -MATCHED_VARS variable if you want all matches. -
    -

    MATCHED_VARS

    -

    Similar to MATCHED_VAR except that it is a collection of all -matches for the current operator check. -

    -
    SecRule ARGS pattern "chain,deny,id:26"
    -  SecRule MATCHED_VARS "@eq ARGS:param"
    -
    -

    MATCHED_VAR_NAME

    -

    This variable holds the full name of the variable that was matched -against. -

    -
    SecRule ARGS pattern "chain,deny,id:27"
    -  SecRule MATCHED_VAR_NAME "@eq ARGS:param"
    -
    -
    Note 
    Be aware that this variable holds data for -the last operator match. This means that if there are -more than one matches, only the last one will be populated. Use -MATCHED_VARS_NAMES variable if you want all matches. -
    -

    MATCHED_VARS_NAMES

    -

    Similar to MATCHED_VAR_NAME except that it is a collection of all - matches for the current operator check. -

    -
    SecRule ARGS pattern "chain,deny,id:28"
    -  SecRule MATCHED_VARS_NAMES "@eq ARGS:param"
    -
    -

    MODSEC_BUILD

    -

    This variable holds the ModSecurity build number. This variable is -intended to be used to check the build number prior to using a feature -that is available only in a certain build. Example: -

    -
    SecRule MODSEC_BUILD "!@ge 02050102" "skipAfter:12345,id:29"
    -SecRule ARGS "@pm some key words" "id:12345,deny,status:500"
    -
    -

    MULTIPART_CRLF_LF_LINES

    -

    This flag variable will be set to 1 whenever a multi-part request -uses mixed line terminators. The multipart/form-data RFC requires CRLF -sequence to be used to terminate lines. Since some client -implementations use only LF to terminate lines you might want to allow -them to proceed under certain circumstances (if you want to do this you -will need to stop using MULTIPART_STRICT_ERROR and check each multi-part - flag variable individually, avoiding MULTIPART_LF_LINE). However, -mixing CRLF and LF line terminators is dangerous as it can allow for -evasion. Therefore, in such cases, you will have to add a check for -MULTIPART_CRLF_LF_LINES. -

    -

    MULTIPART_STRICT_ERROR

    -

    MULTIPART_STRICT_ERROR will be set to 1 when any of the following -variables is also set to 1: REQBODY_PROCESSOR_ERROR, -MULTIPART_BOUNDARY_QUOTED, MULTIPART_BOUNDARY_WHITESPACE, -MULTIPART_DATA_BEFORE, MULTIPART_DATA_AFTER, MULTIPART_HEADER_FOLDING, -MULTIPART_LF_LINE, MULTIPART_MISSING_SEMICOLON MULTIPART_INVALID_QUOTING - MULTIPART_INVALID_HEADER_FOLDING MULTIPART_FILE_LIMIT_EXCEEDED. Each of - these variables covers one unusual (although sometimes legal) aspect of - the request body in multipart/form-data format. Your policies should -always contain a rule to check either this variable (easier) or one or -more individual variables (if you know exactly what you want to -accomplish). Depending on the rate of false positives and your default -policy you should decide whether to block or just warn when the rule is -triggered. -

    The best way to use this variable is as in the example below: -

    -
    SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
    -"phase:2,id:30,t:none,log,deny,msg:'Multipart request body \
    -failed strict validation: \
    -PE %{REQBODY_PROCESSOR_ERROR}, \
    -BQ %{MULTIPART_BOUNDARY_QUOTED}, \
    -BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
    -DB %{MULTIPART_DATA_BEFORE}, \
    -DA %{MULTIPART_DATA_AFTER}, \
    -HF %{MULTIPART_HEADER_FOLDING}, \
    -LF %{MULTIPART_LF_LINE}, \
    -SM %{MULTIPART_MISSING_SEMICOLON}, \
    -IQ %{MULTIPART_INVALID_QUOTING}, \
    -IQ %{MULTIPART_INVALID_HEADER_FOLDING}, \
    -FE %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
    -
    -

    The multipart/form-data parser was upgraded in ModSecurity v2.1.3 to -actively look for signs of evasion. Many variables (as listed above) -were added to expose various facts discovered during the parsing -process. The MULTIPART_STRICT_ERROR variable is handy to check on all -abnormalities at once. The individual variables allow detection to be -fine-tuned according to your circumstances in order to reduce the number - of false positives. -

    -

    - MULTIPART_UNMATCHED_BOUNDARY

    -

    Set to 1 when, during the parsing phase of a multipart/request-body, -ModSecurity encounters what feels like a boundary but it is not. Such an - event may occur when evasion of ModSecurity is attempted. -

    The best way to use this variable is as in the example below: -

    -
    SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
    -"phase:2,id:31,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
    -
    -

    Change the rule from blocking to logging-only if many false positives - are encountered. -

    -

    OUTBOUND_DATA_ERROR

    -

    This variable will be set to 1 when the response body size is above -the setting configured by SecResponseBodyLimit directive. Your policies - should always contain a rule to check this variable. Depending on the -rate of false positives and your default policy you should decide -whether to block or just warn when the rule is triggered. -

    The best way to use this variable is as in the example below: -

    SecRule OUTBOUND_DATA_ERROR "@eq 1" -"phase:1,id:32,t:none,log,pass,msg:'Response Body Larger than -SecResponseBodyLimit Setting'" -

    -

    -PATH_INFO

    -

    Contains the extra request URI information, also known as path info. -(For example, in the URI /index.php/123, /123 is the path info.) -Available only in embedded deployments. -

    SecRule PATH_INFO "^/(bin|etc|sbin|opt|usr)" "id:33" -

    -

    PERF_COMBINED

    -

    Contains the time, in microseconds, spent in ModSecurity during the -current transaction. The value in this variable is arrived to by adding -all the performance variables except PERF_SREAD (the time spent reading -from persistent storage is already included in the phase measurements). -Available starting with 2.6. -

    -

    -PERF_GC

    -

    Contains the time, in microseconds, spent performing garbage -collection. Available starting with 2.6. -

    -

    PERF_LOGGING

    -

    Contains the time, in microseconds, spent in audit logging. This -value is known only after the handling of a transaction is finalized, -which means that it can only be logged using mod_log_config and -the %{VARNAME}M syntax. Available starting with 2.6. -

    -

    PERF_PHASE1

    -

    Contains the time, in microseconds, spent processing phase 1. -Available starting with 2.6. -

    -

    PERF_PHASE2

    -

    Contains the time, in microseconds, spent processing phase 2. -Available starting with 2.6. -

    -

    PERF_PHASE3

    -

    Contains the time, in microseconds, spent processing phase 3. -Available starting with 2.6. -

    -

    PERF_PHASE4

    -

    Contains the time, in microseconds, spent processing phase 4. -Available starting with 2.6. -

    -

    PERF_PHASE5

    -

    Contains the time, in microseconds, spent processing phase 5. -Available starting with 2.6. -

    -

    - PERF_RULES

    -

    Contains the time of rules, in microseconds. Available starting with -2.7. -

    SecRule PERF_RULES "@gt 1000" "id:12345,phase:5" -

    -

    - PERF_SREAD

    -

    Contains the time, in microseconds, spent reading from persistent -storage. Available starting with 2.6. -

    -

    PERF_SWRITE

    -

    Contains the time, in microseconds, spent writing to persistent -storage. Available starting with 2.6. -

    -

    QUERY_STRING

    -

    Contains the query string part of a request URI. The value in -QUERY_STRING is always provided raw, without URL decoding taking place. -

    SecRule QUERY_STRING "attack" "id:34" -

    -

    REMOTE_ADDR

    -

    This variable holds the IP address of the remote client. -

    SecRule REMOTE_ADDR "@ipMatch 192.168.1.101" "id:35" -

    -

    REMOTE_HOST

    -

    If the Apache directive HostnameLookups is set to On, then this -variable will hold the remote hostname resolved through DNS. If the -directive is set to Off, this variable it will hold the remote IP -address (same as REMOTE_ADDR). Possible uses for this variable would be -to deny known bad client hosts or network blocks, or conversely, to -allow in authorized hosts. -

    SecRule REMOTE_HOST "\.evil\.network\org$" "id:36" -

    -

    REMOTE_PORT

    -

    This variable holds information on the source port that the client -used when initiating the connection to our web server. -

    In the following example, we are evaluating to see whether the -REMOTE_PORT is less than 1024, which would indicate that the user is a -privileged user: -

    SecRule REMOTE_PORT "@lt 1024" "id:37" -

    -

    REMOTE_USER

    -

    This variable holds the username of the authenticated user. If there -are no password access controls in place (Basic or Digest -authentication), then this variable will be empty. -

    SecRule REMOTE_USER "^admin$" "id:38" -

    -
    Note 
    In a reverse-proxy deployment, this -information will not be available if the authentication is -
    -

    handled in the backend web server. -

    -

    REQBODY_ERROR

    -

    Contains the status of the request body processor used for request -body parsing. The values can be 0 (no error) or 1 (error). This variable - will be set by request body processors (typically the -multipart/request-data parser or the XML parser) when they fail to do -their work. -

    SecRule REQBODY_ERROR "@eq 1" deny,phase:2,id:39 -

    -
    Note 
    Your policies must have a rule to check for - request body processor errors at the very beginning of phase 2. Failure - to do so will leave the door open for impedance mismatch attacks. It is - possible, for example, that a payload that cannot be parsed by -ModSecurity can be successfully parsed by more tolerant parser operating - in the application. If your policy dictates blocking, then you should -reject the request if error is detected. When operating in -detection-only mode, your rule should alert with high severity when -request body processing fails. -
    -

    REQBODY_ERROR_MSG

    -

    If there’s been an error during request body parsing, the variable -will contain the following error message: -

    SecRule REQBODY_ERROR_MSG "failed to parse" "id:40" -

    -

    REQBODY_PROCESSOR

    -

    Contains the name of the currently used request body processor. The -possible values are URLENCODED, MULTIPART, and XML. -

    -
    SecRule REQBODY_PROCESSOR "^XML$ chain,id:41 
    -  SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
    -
    -

    REQUEST_BASENAME

    -

    This variable holds just the filename part of REQUEST_FILENAME (e.g., - index.php). -

    SecRule REQUEST_BASENAME "^login\.php$" -phase:2,id:42,t:none,t:lowercase -

    -
    Note 
    Please note that anti-evasion -transformations are not applied to this variable by default. -REQUEST_BASENAME will recognise both / and \ as path separators. You -should understand that the value of this variable depends on what was -provided in request, and that it does not have to correspond to the -resource (on disk) that will be used by the web server. -
    -

    REQUEST_BODY

    -

    Holds the raw request body. This variable is available only if the -URLENCODED request body processor was used, which will occur by default -when the application/x-www-form-urlencoded content type is detected, or -if the use of the URLENCODED request body parser was forced. -

    SecRule REQUEST_BODY -"^username=\w{25,}\&password=\w{25,}\&Submit\=login$" "id:43" -

    As of 2.5.7, it is possible to force the presence of the -REQUEST_BODY variable, but only when there is no request body processor -defined using the ctl:forceRequestBodyVariable option in the -REQUEST_HEADERS phase. -

    -

    REQUEST_BODY_LENGTH

    -

    Contains the number of bytes read from a request body. Available -starting with v2.6 -

    -

    REQUEST_COOKIES

    -

    This variable is a collection of all of request cookies (values -only). Example: the following example is using the Ampersand special -operator to count how many variables are in the collection. In this -rule, it would trigger if the request does not include any Cookie -headers. -

    SecRule &REQUEST_COOKIES "@eq 0" "id:44" -

    -

    REQUEST_COOKIES_NAMES

    -

    This variable is a collection of the names of all request cookies. -For example, the following rule will trigger if the JSESSIONID cookie is - not present: -

    SecRule &REQUEST_COOKIES_NAMES:JSESSIONID "@eq 0" -"id:45" -

    -

    REQUEST_FILENAME

    -

    This variable holds the relative request URL without the query string - part (e.g., /index.php). -

    SecRule REQUEST_FILENAME "^/cgi-bin/login\.php$" -phase:2,id:46,t:none,t:normalizePath -

    -
    Note 
    Please note that anti-evasion -transformations are not used on REQUEST_FILENAME, which means that you -will have to specify them in the rules that use this variable. -
    -

    REQUEST_HEADERS

    -

    This variable can be used as either a collection of all of the -request headers or can be used to inspect selected headers (by using the - REQUEST_HEADERS:Header-Name syntax). -

    SecRule REQUEST_HEADERS:Host "^[\d\.]+$" -"deny,id:47,log,status:400,msg:'Host header is a numeric IP address'" -

    -

    REQUEST_HEADERS_NAMES

    -

    This variable is a collection of the names of all of the request -headers. -

    SecRule REQUEST_HEADERS_NAMES "^x-forwarded-for" -"log,deny,id:48,status:403,t:lowercase,msg:'Proxy Server Used'" -

    -

    REQUEST_LINE

    -

    This variable holds the complete request line sent to the server -(including the request method and HTTP version information). -

    -
    # Allow only POST, GET and HEAD request methods, as well as only
    -# the valid protocol versions 
    -SecRule REQUEST_LINE "!(^((?:(?:POS|GE)T|HEAD))|HTTP/(0\.9|1\.0|1\.1)$)" "phase:1,id:49,log,block,t:none"
    -
    -

    REQUEST_METHOD

    -

    This variable holds the request method used in the transaction. -

    SecRule REQUEST_METHOD "^(?:CONNECT|TRACE)$" "id:50,t:none" -

    -

    REQUEST_PROTOCOL

    -

    This variable holds the request protocol version information. -

    SecRule REQUEST_PROTOCOL "!^HTTP/(0\.9|1\.0|1\.1)$" "id:51" -

    -

    REQUEST_URI

    -

    This variable holds the full request URL including the query string -data (e.g., /index.php? p=X). However, it will never contain a domain -name, even if it was provided on the request line. -

    SecRule REQUEST_URI "attack" -"phase:1,id:52,t:none,t:urlDecode,t:lowercase,t:normalizePath" -

    -
    Note 
    Please note that anti-evasion -transformations are not used on REQUEST_URI, which means that you will -have to specify them in the rules that use this variable. -
    -

    REQUEST_URI_RAW

    -

    Same as REQUEST_URI but will contain the domain name if it was -provided on the request line (e.g., http://www.example.com/index.php?p=X). -

    SecRule REQUEST_URI_RAW "http:/" -"phase:1,id:53,t:none,t:urlDecode,t:lowercase,t:normalizePath" -

    -
    Note 
    Please note that anti-evasion -transformations are not used on REQUEST_URI_RAW, which means that you -will have to specify them in the rules that use this variable. -
    -

    RESPONSE_BODY

    -

    This variable holds the data for the response body, but only when -response body buffering is enabled. -

    SecRule RESPONSE_BODY "ODBC Error Code" -"phase:4,id:54,t:none" -

    -

    RESPONSE_CONTENT_LENGTH

    -

    Response body length in bytes. Can be available starting with phase -3, but it does not have to be (as the length of response body is not -always known in advance). If the size is not known, this variable will -contain a zero. If RESPONSE_CONTENT_LENGTH contains a zero in phase 5 -that means the actual size of the response body was 0. The value of this - variable can change between phases if the body is modified. For -example, in embedded mode, mod_deflate can compress the response body -between phases 4 and 5. -

    -

    RESPONSE_CONTENT_TYPE

    -

    Response content type. Available only starting with phase 3. The -value available in this variable is taken directly from the internal -structures of Apache, which means that it may contain the information -that is not yet available in response headers. In embedded deployments, -you should always refer to this variable, rather than to -RESPONSE_HEADERS:Content-Type. -

    -

    RESPONSE_HEADERS

    -

    This variable refers to response headers, in the same way as -REQUEST_HEADERS does to request headers. -

    SecRule RESPONSE_HEADERS:X-Cache "MISS" "id:55" -

    This variable may not have access to some headers when running in - embedded mode. Headers such as Server, Date, Connection, and -Content-Type could be added just prior to sending the data to the -client. This data should be available in phase 5 or when deployed in -proxy mode. -

    -

    RESPONSE_HEADERS_NAMES

    -

    This variable is a collection of the response header names. -

    SecRule RESPONSE_HEADERS_NAMES "Set-Cookie" -"phase:3,id:56,t:none" -

    The same limitations apply as the ones discussed in -RESPONSE_HEADERS. -

    -

    RESPONSE_PROTOCOL

    -

    This variable holds the HTTP response protocol information. -

    SecRule RESPONSE_PROTOCOL "^HTTP\/0\.9" -"phase:3,id:57,t:none" -

    -

    RESPONSE_STATUS

    -

    This variable holds the HTTP response status code: -

    SecRule RESPONSE_STATUS "^[45]" "phase:3,id:58,t:none" -

    This variable may not work as expected in embedded mode, as -Apache sometimes handles certain requests differently, and without -invoking ModSecurity (all other modules). -

    -

    RULE

    -

    This is a special collection that provides access to the id, rev, -severity, logdata, and msg fields of the rule that triggered the action. - It can be used to refer to only the same rule in which it resides. -

    SecRule &REQUEST_HEADERS:Host "@eq 0" -"log,deny,id:59,setvar:tx.varname=%{RULE.id}" -

    -

    SCRIPT_BASENAME

    -

    This variable holds just the local filename part of SCRIPT_FILENAME. -

    SecRule SCRIPT_BASENAME "^login\.php$" "id:60" -

    -
    Note 
    Not available in proxy mode. -
    -

    SCRIPT_FILENAME

    -

    This variable holds the full internal path to the script that will be - used to serve the request. -

    SecRule SCRIPT_FILENAME -"^/usr/local/apache/cgi-bin/login\.php$" "id:61" -

    -
    Note 
    Not available in proxy mode. -
    -

    - SCRIPT_GID

    -

    This variable holds the numerical identifier of the group owner of -the script. -

    SecRule SCRIPT_GID "!^46$" "id:62" -

    -
    Note 
    Not available in proxy mode. -
    -

    SCRIPT_GROUPNAME

    -

    This variable holds the name of the group owner of the script. -

    SecRule SCRIPT_GROUPNAME "!^apache$" "id:63" -

    -
    Note 
    Not available in proxy mode. -
    -

    SCRIPT_MODE

    -

    This variable holds the script’s permissions mode data (e.g., 644). -

    -
    # Do not allow scripts that can be written to
    -SecRule SCRIPT_MODE "^(2|3|6|7)$" "id:64"
    -
    -
    Note 
    Not available in proxy mode. -
    -

    - SCRIPT_UID

    -

    This variable holds the numerical identifier of the owner of the -script. -

    -
    # Do not run any scripts that are owned 
    -# by Apache (Apache's user id is 46) 
    -SecRule SCRIPT_UID "!^46$" "id:65"
    -
    -
    Note 
    Not available in proxy mode. -
    -

    SCRIPT_USERNAME

    -

    This variable holds the username of the owner of the script. -

    -
    # Do not run any scripts owned by Apache SecRule 
    -SCRIPT_USERNAME "^apache$" "id:66"
    -
    -
    Note 
    Not available in proxy mode. -
    -

    SERVER_ADDR

    -

    This variable contains the IP address of the server. -

    SecRule SERVER_ADDR "@ipMatch 192.168.1.100" "id:67" -

    -

    SERVER_NAME

    -

    This variable contains the transaction’s hostname or IP address, -taken from the request itself (which means that, in principle, it should - not be trusted). -

    SecRule SERVER_NAME "hostname\.com$" "id:68" -

    -

    SERVER_PORT

    -

    This variable contains the local port that the web server (or reverse - proxy) is listening on. -

    SecRule SERVER_PORT "^80$" "id:69" -

    -

    -SESSION

    -

    This variable is a collection that contains session information. It -becomes available only after setsid is executed. -

    The following example shows how to initialize SESSION using -setsid, how to use setvar to increase the SESSION.score values, how to -set the SESSION.blocked variable, and finally, how to deny the -connection based on the SESSION:blocked value: -

    -
    # Initialize session storage 
    -SecRule REQUEST_COOKIES:PHPSESSID !^$ "phase:2,id:70,nolog,pass,setsid:%{REQUEST_COOKIES.PHPSESSID}"
    -
    -# Increment session score on attack 
    -SecRule REQUEST_URI "^/cgi-bin/finger$" "phase:2,id:71,t:none,t:lowercase,t:normalizePath,pass,setvar:SESSION.score=+10" 
    -
    -# Detect too many attacks in a session
    -SecRule SESSION:score "@gt 50" "phase:2,id:72,pass,setvar:SESSION.blocked=1"
    -
    -# Enforce session block 
    -SecRule SESSION:blocked "@eq 1" "phase:2,id:73,deny,status:403"
    -
    -

    -SESSIONID

    -

    This variable contains the value set with setsid. See SESSION (above) - for a complete example. -

    -

    STREAM_INPUT_BODY

    -

    This variable give access to the raw request body content. This -variable is best used for two use-cases: -

    -
    1. For fast pattern matching - using @pm/@pmf to prequalify large -text strings against any kind of content-type data. This is more -performant vs. using REQUEST_BODY/ARGS_POST/ARGS_POST_NAMES as it -happens before ModSecurity parsing in phase:2 variable population. -
    2. For data substitution - using @rsub against this variable -allows you to manipulate live request body data. Example - to remove -offending payloads or to substitute benign data. -
    -
    Note 
    You must enable the -SecStreamInBodyInspection directive -
    -

    STREAM_OUTPUT_BODY

    -

    This variable give access to the raw response body content. This -variable is best used for case: -

    -
    1. For data substitution - using @rsub against this variable allows - you to manipulate live request body data. Example - to remove -offending payloads or to substitute benign data. -
    -
    Note 
    You must enable the -SecStreamOutBodyInspection directive -
    -

    TIME

    -

    This variable holds a formatted string representing the time -(hour:minute:second). -

    SecRule TIME "^(([1](8|9))|([2](0|1|2|3))):\d{2}:\d{2}$" -"id:74" -

    -

    -TIME_DAY

    -

    This variable holds the current date (1–31). The following rule -triggers on a transaction that’s happening anytime between the 10th and -20th in a month: -

    SecRule TIME_DAY "^(([1](0|1|2|3|4|5|6|7|8|9))|20)$" -"id:75" -

    -

    - TIME_EPOCH

    -

    This variable holds the time in seconds since 1970. -

    -

    -TIME_HOUR

    -

    This variable holds the current hour value (0–23). The following rule - triggers when a request is made “off hours”: -

    SecRule TIME_HOUR "^(0|1|2|3|4|5|6|[1](8|9)|[2](0|1|2|3))$" - "id:76" -

    -

    -TIME_MIN

    -

    This variable holds the current minute value (0–59). The following -rule triggers during the last half hour of every hour: -

    SecRule TIME_MIN "^(3|4|5)" "id:77" -

    -

    -TIME_MON

    -

    This variable holds the current month value (0–11). The following -rule matches if the month is either November (value 10) or December -(value 11): -

    SecRule TIME_MON "^1" "id:78" -

    -

    -TIME_SEC

    -

    This variable holds the current second value (0–59). -

    SecRule TIME_SEC "@gt 30" "id:79" -

    -

    -TIME_WDAY

    -

    This variable holds the current weekday value (0–6). The following -rule triggers only on Satur- day and Sunday: -

    SecRule TIME_WDAY "^(0|6)$" "id:80" -

    -

    -TIME_YEAR

    -

    This variable holds the current four-digit year value. -

    SecRule TIME_YEAR "^2006$" "id:81" -

    -

    TX

    -

    This is the transient transaction collection, which is used to store -pieces of data, create a transaction anomaly score, and so on. The -variables placed into this collection are available only until the -transaction is complete. -

    -
    # Increment transaction attack score on attack 
    -SecRule ARGS attack "phase:2,id:82,nolog,pass,setvar:TX.score=+5"
    -
    -# Block the transactions whose scores are too high 
    -SecRule TX:SCORE "@gt 20" "phase:2,id:83,log,deny"
    -
    -

    Some variable names in the TX collection are reserved and cannot be -used: -

    -
    • 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:MSC_.*: ModSecurity processing flags -
    • MSC_PCRE_LIMITS_EXCEEDED: Set to nonzero if PCRE match limits -are exceeded. See SecPcreMatchLimit and SecPcreMatchLimitRecursion for -more information. -
    -

    -UNIQUE_ID

    -

    This variable holds the data created by mod_unique_id [7]. This module provides a magic token for each -request which is guaranteed to be unique across "all" requests under -very specific conditions. The unique identifier is even unique across -multiple machines in a properly configured cluster of machines. The -environment variable UNIQUE_ID is set to the identifier for each -request. The UNIQUE_ID environment variable is constructed by encoding -the 112-bit (32-bit IP address, 32 bit pid, 32 bit time stamp, 16 bit -counter) quadruple using the alphabet [A-Za-z0-9@-] in a manner similar -to MIME base64 encoding, producing 19 characters. -

    -

    URLENCODED_ERROR

    -

    This variable is created when an invalid URL encoding is encountered -during the parsing of a query string (on every request) or during the -parsing of an application/x-www-form-urlencoded request body (only on -the requests that use the URLENCODED request body processor). -

    -

    USERID

    -

    This variable contains the value set with setuid. -

    -
    # Initialize user tracking
    -SecAction "nolog,id:84,pass,setuid:%{REMOTE_USER}" 
    -
    -# Is the current user the administrator?
    -SecRule USERID "admin" "id:85"
    -
    -

    USERAGENT_IP

    -

    This variable is created when running modsecurity with apache2.4 and -will contains the client ip address set by mod_remoteip in proxied -connections. -

    -

    -WEBAPPID

    -

    This variable contains the current application name, which is set in -configuration using SecWebAppId. -

    -

    WEBSERVER_ERROR_LOG

    -

    Contains zero or more error messages produced by the web server. This - variable is best accessed from phase 5 (logging). -

    SecRule WEBSERVER_ERROR_LOG "File does not exist" -"phase:5,id:86,t:none,nolog,pass,setvar:TX.score=+5" -

    -

    XML

    -

    Special collection used to interact with the XML parser. It can be -used standalone as a target for the validateDTD and validateSchema -operator. Otherwise, it must contain a valid XPath expression, which -will then be evaluated against a previously parsed XML DOM tree. -

    -
    SecDefaultAction log,deny,status:403,phase:2,id:90
    -SecRule REQUEST_HEADERS:Content-Type ^text/xml$ "phase:1,id:87,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML"
    -SecRule REQBODY_PROCESSOR "!^XML$" skipAfter:12345,id:88
    -
    -SecRule XML:/employees/employee/name/text() Fred "id:89"
    -SecRule XML:/xq:employees/employee/name/text() Fred "id:12345,xmlns:xq=http://www.example.com/employees"
    -
    -

    The first XPath expression does not use namespaces. It would match -against payload such as this one: -

    -
    <employees>
    -    <employee>
    -        <name>Fred Jones</name>
    -        <address location="home">
    -            <street>900 Aurora Ave.</street>
    -            <city>Seattle</city>
    -            <state>WA</state>
    -            <zip>98115</zip>
    -        </address>
    -        <address location="work">
    -            <street>2011 152nd Avenue NE</street>
    -            <city>Redmond</city>
    -            <state>WA</state>
    -            <zip>98052</zip>
    -        </address>
    -        <phone location="work">(425)555-5665</phone>
    -        <phone location="home">(206)555-5555</phone>
    -        <phone location="mobile">(206)555-4321</phone>
    -    </employee>
    -</employees>
    -
    -

    The second XPath expression does use namespaces. It would match the -following payload: -

    -
    <xq:employees xmlns:xq="http://www.example.com/employees">
    -    <employee>
    -        <name>Fred Jones</name>
    -        <address location="home">
    -            <street>900 Aurora Ave.</street>
    -            <city>Seattle</city>
    -            <state>WA</state>
    -            <zip>98115</zip>
    -        </address>
    -        <address location="work">
    -            <street>2011 152nd Avenue NE</street>
    -            <city>Redmond</city>
    -            <state>WA</state>
    -            <zip>98052</zip>
    -        </address>
    -        <phone location="work">(425)555-5665</phone>
    -        <phone location="home">(206)555-5555</phone>
    -        <phone location="mobile">(206)555-4321</phone>
    -    </employee>
    -</xq:employees>
    -
    -

    Note the different namespace used in the second example. -

    -

    - Transformation functions

    -

    Transformation functions are used to alter input data before it is -used in matching (i.e., operator execution). The input data is never -modified, actually—whenever you request a transformation function to be -used, ModSecurity will create a copy of the data, transform it, and then - run the operator against the result. -

    -
    Note 
    There are no default transformation -functions, as there were in the first generation of ModSecurity (1.x). -
    -

    In the following example, the request parameter values are converted -to lowercase before matching: -

    SecRule ARGS "xp_cmdshell" "t:lowercase,id:91" -

    Multiple transformation actions can be used in the same rule, -forming a transformation pipeline. The transformations will be performed - in the order in which they appear in the rule. -

    In most cases, the order in which transformations are performed -is very important. In the following example, a series of transformation -functions is performed to counter evasion. Performing the -transformations in any other order would allow a skillful attacker to -evade detection: -

    SecRule ARGS -"(asfunction|javascript|vbscript|data|mocha|livescript):" -"id:92,t:none,t:htmlEntityDecode,t:lowercase,t:removeNulls,t:removeWhitespace" -

    -
    Warning 
    It is currently possible to use -SecDefaultAction to specify a default list of transformation functions, -which will be applied to all rules that follow the SecDefaultAction -directive. However, this practice is not recommended, because it means -that mistakes are very easy to make. It is recommended that you always -specify the transformation functions that are needed by a particular -rule, starting the list with t:none (which clears the possibly inherited - transformation functions). -
    -

    The remainder of this section documents the transformation functions -currently available in ModSecurity. -

    -

    base64Decode

    -

    Decodes a Base64-encoded string. -

    -
    SecRule REQUEST_HEADERS:Authorization "^Basic ([a-zA-Z0-9]+=*)$" "phase:1,id:93,capture,chain,logdata:%{TX.1}"
    -  SecRule TX:1 ^(\w+): t:base64Decode,capture,chain
    -    SecRule TX:1 ^(admin|root|backup)$ 
    -
    -

    sqlHexDecode

    -

    Decode sql hex data. Example (0x414243) will be decoded to (ABC). -Available as of 2.6.3 -

    -

    base64DecodeExt

    -

    Decodes a Base64-encoded string. Unlike base64Decode, this version -uses a forgiving implementation, which ignores invalid characters. -Available as of 2.5.13. -

    See blog post on Base64Decoding evasion issues on PHP sites - http://blog.spiderlabs.com/2010/04/impedance-mismatch-and-base64.html -

    -

    base64Encode

    -

    Encodes input string using Base64 encoding. -

    -

    -cmdLine

    -
    Note 
    This is a community contribution developed -by Marc Stern [8] -
    -

    In Windows and Unix, commands may be escaped by different means, such - as: -

    -
    • c^ommand /c ... -
    • "command" /c ... -
    • command,/c ... -
    • backslash in the middle of a Unix command -
    -

    The cmdLine transformation function avoids this problem by -manipulating the variable contend in the following ways: -

    -
    • deleting all backslashes [\] -
    • deleting all double quotes ["] -
    • deleting all sigle quotes ['] -
    • deleting all carets [^] -
    • deleting spaces before a slash [/] -
    • deleting spaces before an open parentesis [(] -
    • replacing all commas [,] and semicolon [;] into a space -
    • replacing all multiple spaces (including tab, newline, etc.) -into one space -
    • transform all characters to lowercase -
    -

    Example Usage: -

    -
    SecRule ARGS "(?:command(?:.com)?|cmd(?:.exe)?)(?:/.*)?/[ck]" "phase:2,id:94,t:none, t:cmdLine"
    -
    -

    compressWhitespace

    -

    Converts any of the whitespace characters (0x20, \f, \t, \n, \r, \v, -0xa0) to spaces (ASCII 0x20), compressing multiple consecutive space -characters into one. -

    -

    -cssDecode

    -

    Decodes characters encoded using the CSS 2.x escape rules syndata.html#characters. - This function uses only up to two bytes in the decoding process, -meaning that it is useful to uncover ASCII characters encoded using CSS -encoding (that wouldn’t normally be encoded), or to counter evasion, -which is a combination of a backslash and non-hexadecimal characters -(e.g., ja\vascript is equivalent to javascript). -

    -

    escapeSeqDecode

    -

    Decodes ANSI C escape sequences: \a, \b, \f, \n, \r, \t, \v, \\, \?, -\', \", \xHH (hexadecimal), \0OOO (octal). Invalid encodings are left in - the output. -

    -

    -hexDecode

    -

    Decodes a string that has been encoded using the same algorithm as -the one used in hexEncode (see following entry). -

    -

    -hexEncode

    -

    Encodes string (possibly containing binary characters) by replacing -each input byte with two hexadecimal characters. For example, xyz is -encoded as 78797a. -

    -

    htmlEntityDecode

    -

    Decodes the characters encoded as HTML entities. The following -variants are supported: -

    -
    • &#xHH and &#xHH; (where H is any hexadecimal number) -
    • &#DDD and &#DDD; (where D is any decimal number) -
    • &quotand" -
    • &nbspand  -
    • &ltand< -
    • &gtand> -
    -

    This function always converts one HTML entity into one byte, possibly - resulting in a loss of information (if the entity refers to a character - that cannot be represented with the single byte). It is thus useful to -uncover bytes that would otherwise not need to be encoded, but it cannot - do anything meaningful with the characters from the range above 0xff. -

    -

    -jsDecode

    -

    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 (leading to possible loss of -information). -

    -

    length

    -

    Looks up the length of the input string in bytes, placing it (as -string) in output. For example, if it gets ABCDE on input, this -transformation function will return 5 on output. -

    -

    -lowercase

    -

    Converts all characters to lowercase using the current C locale. -

    -

    md5

    -

    Calculates an MD5 hash from the data in input. The computed hash is -in a raw binary form and may need encoded into text to be printed (or -logged). Hash functions are commonly used in combination with hexEncode -(for example: t:md5,t:hexEncode). -

    -

    none

    -

    Not an actual transformation function, but an instruction to -ModSecurity to remove all transformation functions associated with the -current rule. -

    -

    normalisePath

    -

    Removes multiple slashes, directory self-references, and directory -back-references (except when at the beginning of the input) from input -string. -

    -

    normalisePathWin

    -

    Same as normalisePath, but first converts backslash characters to -forward slashes. -

    -

    parityEven7bit

    -

    Calculates even parity of 7-bit data replacing the 8th bit of each -target byte with the calculated parity bit. -

    -

    parityOdd7bit

    -

    Calculates odd parity of 7-bit data replacing the 8th bit of each -target byte with the calculated parity bit. -

    -

    parityZero7bit

    -

    Calculates zero parity of 7-bit data replacing the 8th bit of each -target byte with a zero-parity bit, which allows inspection of even/odd -parity 7-bit data as ASCII7 data. -

    -

    removeNulls

    -

    Removes all NUL bytes from input. -

    -

    removeWhitespace

    -

    Removes all whitespace characters from input. -

    -

    replaceComments

    -

    Replaces each occurrence of a C-style comment (/* ... */) with a -single space (multiple consecutive occurrences of which will not be -compressed). Unterminated comments will also be replaced with a space -(ASCII 0x20). However, a standalone termination of a comment (*/) will -not be acted upon. -

    -

    removeCommentsChar

    -

    Removes common comments chars (/*, */, --, #). -

    -

    removeComments

    -

    Removes each occurrence of comment (/* ... */, --, #). Multiple -consecutive occurrences of which will not be compressed. -

    -

    replaceNulls

    -

    Replaces NUL bytes in input with space characters (ASCII 0x20). -

    -

    -urlDecode

    -

    Decodes a URL-encoded input string. Invalid encodings (i.e., the ones - that use non-hexadecimal characters, or the ones that are at the end of - string and have one or two bytes missing) are not converted, but no -error is raised. To detect invalid encodings, use the -@validateUrlEncoding operator on the input data first. The -transformation function should not be used against variables that have -already been URL-decoded (such as request parameters) unless it is your -intention to perform URL decoding twice! -

    -

    urlDecodeUni

    -

    Like urlDecode, but with support for the Microsoft-specific %u -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. -

    -

    -urlEncode

    -

    Encodes input string using URL encoding. -

    -

    utf8toUnicode

    -

    Converts all UTF-8 characters sequences to Unicode. This help input -normalization specially for non-english languages minimizing -false-positives and false-negatives. (available with 2.7.0) -

    -

    sha1

    -

    Calculates a SHA1 hash from the input string. The computed hash is in - a raw binary form and may need encoded into text to be printed (or -logged). Hash functions are commonly used in combination with hexEncode -(for example, t:sha1,t:hexEncode). -

    -

    -trimLeft

    -

    Removes whitespace from the left side of the input string. -

    -

    -trimRight

    -

    Removes whitespace from the right side of the input string. -

    -

    trim

    -

    Removes whitespace from both the left and right sides of the input -string. -

    -

    -Actions

    -

    Each action belongs to one of five groups: -

    -
    • Disruptive actions - Cause ModSecurity to do something. -In many cases something means block transaction, but not in all. For -example, the allow action is classified as a disruptive action, but it -does the opposite of blocking. There can only be one disruptive action -per rule (if there are multiple disruptive actions present, or -inherited, only the last one will take effect), or rule chain (in a -chain, a disruptive action can only appear in the first rule). -
    -
    Note 
    Disruptive actions will NOT be executed -if the SecRuleEngine is set to DetectionOnly. If you are creating -exception/whitelisting rules that use the allow action, you should also -add the ctl:ruleEngine=On action to execute the action. -
    -
    • Non-disruptive actions - Do something, but that -something does not and cannot affect the rule processing flow. Setting a - variable, or changing its value is an example of a non-disruptive -action. Non-disruptive action can appear in any rule, including each -rule belonging to a chain. -
    • Flow actions - These actions affect the rule flow (for -example skip or skipAfter). -
    • Meta-data actions - Meta-data actions are used to -provide more information about rules. Examples include id, rev, severity - and msg. -
    • Data actions - Not really actions, these are mere -containers that hold data used by other actions. For example, the status - action holds the status that will be used for blocking (if it takes -place). -
    -

    -accuracy

    -

    Description: Specifies the relative accuracy level of the rule - related to false positives/negatives. The value is a string based on a - numeric scale (1-9 where 9 is very strong and 1 has many false -positives). -

    Action Group: Meta-data -

    Version: 2.7 -

    Example: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \
    -	"phase:2,ver:'CRS/2.2.4,accuracy:'9',maturity:'9',capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \
    -{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}"
    -
    -

    allow

    -

    Description: Stops rule processing on a successful match and -allows the transaction to proceed. -

    Action Group: Disruptive -

    Example: -

    -
    # Allow unrestricted access from 192.168.1.100 
    -SecRule REMOTE_ADDR "^192\.168\.1\.100$" phase:1,id:95,nolog,allow
    -
    -


    -Prior to ModSecurity 2.5 the allow action would only affect the current -phase. An allow in phase 1 would skip processing the remaining rules in -phase 1 but the rules from phase 2 would execute. Starting with v2.5.0 -allow was enhanced to allow for fine-grained control of what is done. -The following rules now apply: -

    -
    1. If used one its own, like in the example above, allow will -affect the entire transaction, stopping processing of the current phase -but also skipping over all other phases apart from the logging phase. -(The logging phase is special; it is designed to always execute.) -
    2. If used with parameter "phase", allow will cause the engine to -stop processing the current phase. Other phases will continue as normal. -
    3. If used with parameter "request", allow will cause the engine -to stop processing the current phase. The next phase to be processed -will be phase RESPONSE_HEADERS. -
    -

    Examples: -

    -
    # Do not process request but process response.
    -SecAction phase:1,allow:request,id:96
    -
    -# Do not process transaction (request and response).
    -SecAction phase:1,allow,id:97
    -
    -

    If you want to allow a response through, put a rule in phase -RESPONSE_HEADERS and simply use allow on its own: -

    -
    # Allow response through.
    -SecAction phase:3,allow,id:98
    -
    -

    append

    -

    Description: Appends text given as parameter to the end of -response body. Content injection must be en- abled (using the -SecContentInjection directive). No content type checks are made, which -means that before using any of the content injection actions, you must -check whether the content type of the response is adequate for -injection. -

    Action Group: Non-disruptive -

    Processing Phases: 3 and 4. -

    Example: -

    -
    SecRule RESPONSE_CONTENT_TYPE "^text/html" "nolog,id:99,pass,append:'<hr>Footer'"
    -
    Warning 
    Although macro expansion is allowed in -the additional content, you are strongly cau- tioned against inserting -user-defined data fields into output. Doing so would create a cross-site - scripting vulnerability. -
    -

    -auditlog

    -

    Description: Marks the transaction for logging in the audit -log. -

    Action Group: Non-disruptive -

    Example: -

    SecRule REMOTE_ADDR "^192\.168\.1\.100$" -auditlog,phase:1,id:100,allow -

    -
    Note 
    The auditlog action is now explicit if log -is already specified. -
    -

    block

    -

    Description: Performs the disruptive action defined by the -previous SecDefaultAction. -

    Action Group: Disruptive -

    This action is essentially a placeholder that is intended to be -used by rule writers to request a blocking action, but without -specifying how the blocking is to be done. The idea is that such -decisions are best left to rule users, as well as to allow users, to -override blocking if they so desire. -In future versions of ModSecurity, more control and functionality will -be added to define "how" to block. -

    Examples: -

    -
    # Specify how blocking is to be done 
    -SecDefaultAction phase:2,deny,id:101,status:403,log,auditlog
    -
    -# Detect attacks where we want to block 
    -SecRule ARGS attack1 phase:2,block,id:102
    -
    -# Detect attacks where we want only to warn 
    -SecRule ARGS attack2 phase:2,pass,id:103
    -
    -

    It is possible to use the SecRuleUpdateActionById directive to -override how a rule handles blocking. This is useful in three cases: -

    -
    1. If a rule has blocking hard-coded, and you want it to use the -policy you determine -
    2. If a rule was written to block, but you want it to only warn -
    3. If a rule was written to only warn, but you want it to block -
    -

    The following example demonstrates the first case, in which the -hard-coded block is removed in favor of the user-controllable block: -

    -
    # Specify how blocking is to be done 
    -SecDefaultAction phase:2,deny,status:403,log,auditlog,id:104
    -
    -# Detect attacks and block 
    -SecRule ARGS attack1 phase:2,id:1,deny
    -
    -# Change how rule ID 1 blocks 
    -SecRuleUpdateActionById 1 block
    -
    -

    -capture

    -

    Description: When used together with the regular expression -operator (@rx), the capture action will create copies of the regular -expression captures and place them into the transaction variable -collection. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule REQUEST_BODY "^username=(\w{25,})" phase:2,capture,t:none,chain,id:105
    -  SecRule TX:1 "(?:(?:a(dmin|nonymous)))"
    -
    -

    Up to 10 captures will be copied on a successful pattern match, each -with a name consisting of a digit from 0 to 9. The TX.0 variable always -contains the entire area that the regular expression matched. All the -other variables contain the captured values, in the order in which the -capturing parentheses appear in the regular expression. -

    -

    chain

    -

    Description: Chains the current rule with the rule that -immediately follows it, creating a rule chain. Chained rules allow for -more complex processing logic. -

    Action Group: Flow -

    Example: -

    -
    # Refuse to accept POST requests that do not contain Content-Length header. 
    -# (Do note that this rule should be preceded by a rule 
    -# that verifies only valid request methods are used.) 
    -SecRule REQUEST_METHOD "^POST$" phase:1,chain,t:none,id:105
    -  SecRule &REQUEST_HEADERS:Content-Length "@eq 0" t:none
    -
    -
    Note 
    Rule chains allow you to simulate logical -AND. The disruptive actions specified in the first portion of the -chained rule will be triggered only if all of the variable checks return - positive hits. If any one aspect of a chained rule comes back negative, - then the entire rule chain will fail to match. Also note that -disruptive actions, execution phases, metadata actions (id, rev, msg, -tag, severity, logdata), skip, and skipAfter actions can be specified -only by the chain starter rule. -
    -

    The following directives can be used in rule chains: -

    -
    • SecAction -
    • SecRule -
    • SecRuleScript -
    -

    Special rules control the usage of actions in chained rules: -

    -
    • Any actions that affect the rule flow (i.e., the disruptive -actions, skip and skipAfter) can be used only in the chain starter. They - will be executed only if the entire chain matches. -
    • Non-disruptive rules can be used in any rule; they will be -executed if the rule that contains them matches and not only when the -entire chain matches. -
    • The metadata actions (e.g., id, rev, msg) can be used only in -the chain starter. -
    -

    ctl

    -

    Description: Changes ModSecurity configuration on transient, -per-transaction basis. Any changes made using this action will affect -only the transaction in which the action is executed. The default -configuration, as well as the other transactions running in parallel, -will be unaffected. -

    Action Group: Non-disruptive -

    Example: -

    -
    # Parse requests with Content-Type "text/xml" as XML 
    -SecRule REQUEST_CONTENT_TYPE ^text/xml "nolog,pass,id:106,ctl:requestBodyProcessor=XML"
    -
    -

    The following configuration options are supported: -

    -
    1. auditEngine -
    2. auditLogParts -
    3. debugLogLevel -
    4. forceRequestBodyVariable -
    5. requestBodyAccess -
    6. requestBodyLimit -
    7. requestBodyProcessor -
    8. responseBodyAccess -
    9. responseBodyLimit -
    10. ruleEngine -
    11. ruleRemoveById - since this action us triggered at run -time, it should be specified before the rule in which it is -disabling. -
    12. ruleUpdateTargetById - This is deprecated and will be -removed from the code. Use ruleRemoveTargetById for per-request -exceptions. -
    13. ruleRemoveTargetById - since this action is used to just - remove targets, users don't need to use the char ! before the -target list. -
    14. ruleRemoveByMsg -
    15. encryptionEngine -
    16. encryptionEnforcement -
    -

    With the exception of the requestBodyProcessor and -forceRequestBodyVariable settings, each configuration option corresponds - to one configuration directive and the usage is identical. -

    The requestBodyProcessor option allows you to configure the -request body processor. By default, ModSecurity will use the URLENCODED -and MULTIPART processors to process an application/x-www-form-urlencoded - and a multipart/form-data body, respectively. A third processor, XML, -is also supported, but it is never used implicitly. Instead, you must -tell ModSecurity to use it by placing a few rules in the REQUEST_HEADERS - processing phase. After the request body is processed as XML, you will -be able to use the XML-related features to inspect it. -

    Request body processors will not interrupt a transaction if an -error occurs during parsing. Instead, they will set the variables -REQBODY_PROCESSOR_ERROR and REQBODY_PROCESSOR_ERROR_MSG. These variables - should be inspected in the REQUEST_BODY phase and an appropriate action - taken. -The forceRequestBodyVariable option allows you to configure the -REQUEST_BODY variable to be set when there is no request body processor -configured. This allows for inspection of request bodies of unknown -types. -

    -

    deny

    -

    Description: Stops rule processing and intercepts transaction. -

    Action Group: Disruptive -

    Example: -SecRule REQUEST_HEADERS:User-Agent "nikto" -"log,deny,id:107,msg:'Nikto Scanners Identified'" -

    -

    deprecatevar

    -

    Description: Decrements numerical value over time, which makes - sense only applied to the variables stored in persistent storage. -

    Action Group: Non-Disruptive -

    Example: The following example will decrement the counter by 60 -every 300 seconds. -

    -
    SecAction phase:5,id:108,nolog,pass,deprecatevar:SESSION.score=60/300
    -
    -

    Counter values are always positive, meaning that the value will never - go below zero. Unlike expirevar, the deprecate action must be executed -on every request. -

    -

    drop

    -

    Description: Initiates an immediate close of the TCP -connection by sending a FIN packet. -

    Action Group: Disruptive -

    Example: The following example initiates an IP collection -for tracking Basic Authentication attempts. If the client goes over the -threshold of more than 25 attempts in 2 minutes, it will DROP subsequent - connections. -

    -
    SecAction phase:1,id:109,initcol:ip=%{REMOTE_ADDR},nolog
    -SecRule ARGS:login "!^$" "nolog,phase:1,id:110,setvar:ip.auth_attempt=+1,deprecatevar:ip.auth_attempt=20/120"
    -SecRule IP:AUTH_ATTEMPT "@gt 25" "log,drop,phase:1,id:111,msg:'Possible Brute Force Attack'"
    -
    -
    Note 
    This action is currently not available on -Windows based builds. -
    -

    This action is extremely useful when responding to both Brute Force -and Denial of Service attacks in that, in both cases, you want to -minimize both the network bandwidth and the data returned to the client. - This action causes error message to appear in the log "(9)Bad file -descriptor: core_output_filter: writing data to the network" -

    -

    exec

    -

    Description: Executes an external script/binary supplied as -parameter. As of v2.5.0, if the parameter supplied to exec is a Lua -script (detected by the .lua extension) the script will be processed -internally. This means you will get direct access to the internal -request context from the script. Please read the SecRuleScript -documentation for more details on how to write Lua scripts. -

    Action Group: Non-disruptive -

    Example: -

    -
    # Run external program on rule match 
    -SecRule REQUEST_URI "^/cgi-bin/script\.pl" "phase:2,id:112,t:none,t:lowercase,t:normalizePath,block,\ exec:/usr/local/apache/bin/test.sh"
    -
    -# Run Lua script on rule match 
    -SecRule ARGS:p attack "phase:2,id:113,block,exec:/usr/local/apache/conf/exec.lua"
    -
    -

    The exec action is executed independently from any disruptive actions - specified. External scripts will always be called with no parameters. -Some transaction information will be placed in environment variables. -All the usual CGI environment variables will be there. You should be -aware that forking a threaded process results in all threads being -replicated in the new process. Forking can therefore incur larger -overhead in a multithreaded deployment. The script you execute must -write something (anything) to stdout; if it doesn’t, ModSecurity will -assume that the script failed, and will record the failure. -

    -

    -expirevar

    -

    Description: Configures a collection variable to expire after -the given time period (in seconds). -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule REQUEST_COOKIES:JSESSIONID "!^$" "nolog,phase:1,id:114,pass,setsid:%{REQUEST_COOKIES:JSESSIONID}"
    -SecRule REQUEST_URI "^/cgi-bin/script\.pl" "phase:2,id:115,t:none,t:lowercase,t:normalisePath,log,allow,setvar:session.suspicious=1,expirevar:session.suspicious=3600,phase:1"
    -
    -

    You should use the expirevar actions at the same time that you use -setvar actions in order to keep the indented expiration time. If they -are used on their own (perhaps in a SecAction directive), the expire -time will be reset. -

    -

    id

    -

    Description: Assigns a unique ID to the rule or chain in which - it appears. Starting with ModSecurity 2.7 this action is mandatory and -must be numeric. -

    Action Group: Meta-data -

    Example: -

    -
    SecRule &REQUEST_HEADERS:Host "@eq 0" "log,id:60008,severity:2,msg:'Request Missing a Host Header'"
    -
    -

    These are the reserved ranges: -

    -
    • 1–99,999: reserved for local (internal) use. Use as you see fit, - but do not use this range for rules that are distributed to others -
    • 100,000–199,999: reserved for internal use of the engine, to -assign to rules that do not have explicit IDs -
    • 200,000–299,999: reserved for rules published at -modsecurity.org -
    • 300,000–399,999: reserved for rules published at gotroot.com -
    • 400,000–419,999: unused (available for reservation) -
    • 420,000–429,999: reserved for ScallyWhack [9] -
    • 430,000–439,999: reserved for rules published by Flameeyes [10] -
    • 440.000-599,999: unused (available for reservation) -
    • 600,000-699,999: reserved for use by Akamai [11] -
    • 700,000–799,999: reserved for Ivan Ristic -
    • 900,000–999,999: reserved for the OWASP ModSecurity Core Rule -Set [12] project -
    • 1,000,000-1,999,999: unused (available for reservation) -
    • 2,000,000-2,999,999: reserved for rules from Trustwave's -SpiderLabs Research team -
    • 3,000,000 and above: unused (available for reservation) -
    -

    -initcol

    -

    Description: Initializes a named persistent collection, either - by loading data from storage or by creating a new collection in memory. -

    Action Group: Non-disruptive -

    Example: The following example initiates IP address -tracking, which is best done in phase 1: -

    -
    SecAction phase:1,id:116,nolog,pass,initcol:ip=%{REMOTE_ADDR}
    -
    -

    Collections are loaded into memory on-demand, when the initcol action - is executed. A collection will be persisted only if a change was made -to it in the course of transaction processing. -

    See the "Persistant Storage" section for further details. -

    -

    log

    -

    Description: Indicates that a successful match of the rule -needs to be logged. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecAction phase:1,id:117,pass,initcol:ip=%{REMOTE_ADDR},log
    -
    -

    This action will log matches to the Apache error log file and the -ModSecurity audit log. -

    -

    -logdata

    -

    Description: Logs a data fragment as part of the alert -message. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule ARGS:p "@rx <script>" "phase:2,id:118,log,pass,logdata:%{MATCHED_VAR}"
    -
    -

    The logdata information appears in the error and/or audit log files. -Macro expansion is performed, so you may use variable names such -as %{TX.0} or %{MATCHED_VAR}. The information is properly -escaped for use with logging of binary data. -

    -

    -maturity

    -

    Description: Specifies the relative maturity level of the rule - related to the length of time a rule has been public and the amount of -testing it has received. The value is a string based on a numeric scale - (1-9 where 9 is extensively tested and 1 is a brand new experimental -rule). -

    Action Group: Meta-data -

    Version: 2.7 -

    Example: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \
    -	"phase:2,ver:'CRS/2.2.4,accuracy:'9',maturity:'9',capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \
    -{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}"
    -
    -

    msg

    -

    Description: Assigns a custom message to the rule or chain in -which it appears. The message will be logged along with every alert. -

    Action Group: Meta-data -

    Example: -

    -
    SecRule &REQUEST_HEADERS:Host "@eq 0" "log,id:60008,severity:2,msg:'Request Missing a Host Header'"
    -
    -
    Note 
    The msg information appears in the error -and/or audit log files and is not sent back to the client in response -headers. -
    -

    - multiMatch

    -

    Description: If enabled, ModSecurity will perform multiple -operator invocations for every target, before and after every -anti-evasion transformation is performed. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule ARGS "attack" "phase1,log,deny,id:119,t:removeNulls,t:lowercase,multiMatch"
    -
    -

    Normally, variables are inspected only once per rule, and only after -all transformation functions have been completed. With multiMatch, -variables are checked against the operator before and after every -transformation function that changes the input. -

    -

    - noauditlog

    -

    Description: Indicates that a successful match of the rule -should not be used as criteria to determine whether the transaction -should be logged to the audit log. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" allow,noauditlog,id:120
    -
    -

    If the SecAuditEngine is set to On, all of the transactions will be -logged. If it is set to RelevantOnly, then you can control the logging -with the noauditlog action. -

    The noauditlog action affects only the current rule. If you -prevent audit logging in one rule only, a match in another rule will -still cause audit logging to take place. If you want to prevent audit -logging from taking place, regardless of whether any rule matches, use -ctl:auditEngine=Off. -

    -

    nolog

    -

    Description: Prevents rule matches from appearing in both the -error and audit logs. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" allow,nolog,id:121
    -
    -

    Although nolog implies noauditlog, you can override the former by -using nolog,auditlog. -

    -

    pass

    -

    Description: Continues processing with the next rule in spite -of a successful match. -

    Action Group: Disruptive -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" "log,pass,id:122"
    -
    -

    When using pass with a SecRule with multiple targets, all variables -will be inspected and all non-disruptive actions trigger for every -match. In the following example, the TX.test variable will be -incremented once for every request parameter: -

    -
    # Set TX.test to zero 
    -SecAction "phase:2,nolog,pass,setvar:TX.test=0,id:123"
    -
    -# Increment TX.test for every request parameter 
    -SecRule ARGS "test" "phase:2,log,pass,setvar:TX.test=+1,id:124"
    -
    -

    pause

    -

    Description: Pauses transaction processing for the specified -number of milliseconds. Starting with ModSecurity 2.7 this feature also -supports macro expansion. -

    Action Group: Disruptive -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" "log,pause:5000,id:125"
    -
    -
    Warning 
    This feature can be of limited benefit -for slowing down brute force authentication attacks, but use with care. -If you are under a denial of service attack, the pause feature may make -matters worse, as it will cause an entire Apache worker (process or -thread, depending on the deployment mode) to sit idle until the pause is - completed. -
    -

    phase

    -

    Description: Places the rule or chain into one of five -available processing phases. It can also be used in SecDefaultAction to -establish the rule defaults. -

    Action Group: Meta-data -

    Example: -

    -
    # Initialize IP address tracking in phase 1
    -SecAction phase:1,nolog,pass,id:126,initcol:IP=%{REMOTE_ADDR}
    -
    -

    Starting in ModSecurity version v2.7 there are aliases for some phase - numbers: -

    -
    • 2 - request -
    • 4 - response -
    • 5 - logging -
    -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" "phase:request,log,deny,id:127"
    -
    -
    Warning 
    Keep in mind that if you specify the -incorrect phase, the variable used in the rule may not yet be available. - This could lead to a false negative situation where your variable and -operator may be correct, but it misses malicious data because you -specified the wrong phase. -
    -

    -prepend

    -

    Description: Prepends the text given as parameter to response -body. Content injection must be enabled (using the SecContentInjection -directive). No content type checks are made, which means that before -using any of the content injection actions, you must check whether the -content type of the response is adequate for injection. -

    Action Group: Non-disruptive -

    Processing Phases: 3 and 4. -

    Example: -

    -
    SecRule RESPONSE_CONTENT_TYPE ^text/html \ "phase:3,nolog,pass,id:128,prepend:'Header<br>'"
    -
    -
    Warning 
    Although macro expansion is allowed in -the injected content, you are strongly cautioned against inserting user -defined data fields int output. Doing so would create a cross-site -scripting vulnerability. -
    -

    proxy

    -

    Description: Intercepts the current transaction by forwarding -the request to another web server using the proxy backend. The -forwarding is carried out transparently to the HTTP client (i.e., -there’s no external redirection taking place). -

    Action Group: Disruptive -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" log,id:129,proxy:http://honeypothost/
    -
    -

    For this action to work, mod_proxy must also be installed. This -action is useful if you would like to proxy matching requests onto a -honeypot web server, and especially in combination with IP address or -session tracking. -

    -

    -redirect

    -

    Description: Intercepts transaction by issuing an external -(client-visible) redirection to the given location.. -

    Action Group: Disruptive -

    Example: -

    -
    SecRule REQUEST_HEADERS:User-Agent "Test" "phase:1,id:130,log,redirect:http://www.example.com/failed.html"
    -
    -

    If the status action is present on the same rule, and its value can -be used for a redirection (i.e., is one of the following: 301, 302, 303, - or 307), the value will be used for the redirection status code. -Otherwise, status code 302 will be used. -

    -

    rev

    -

    Description: Specifies rule revision. It is useful in -combination with the id action to provide an indication that a rule has -been changed. -

    Action Group: Meta-data -

    Example: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "(?:(?:[\;\|\`]\W*?\bcc|\b(wget|curl))\b|\/cc(?:[\'\"\|\;\`\-\s]|$))" \
    -	                "phase:2,rev:'2.1.3',capture,t:none,t:normalisePath,t:lowercase,ctl:auditLogParts=+E,block,msg:'System Command Injection',id:'950907',tag:'WEB_ATTACK/COMMAND_INJECTION',tag:'WASCTC/WASC-31',tag:'OWASP_TOP_10/A1',tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.command_injection_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/COMMAND_INJECTION-%{matched_var_name}=%{tx.0},skipAfter:END_COMMAND_INJECTION1"
    -
    -
    Note 
    This action is used in combination with the - id action to allow the same rule ID to be used after changes take place - but to still provide some indication the rule changed. -
    -

    sanitiseArg

    -

    Description: Prevents sensitive request parameter data from -being logged to audit log. Each byte of the named parameter(s) is -replaced with an asterisk. -

    Action Group: Non-disruptive -

    Example: -

    -
    # Never log passwords 
    -SecAction "nolog,phase:2,id:131,sanitiseArg:password,sanitiseArg:newPassword,sanitiseArg:oldPassword"
    -
    -
    Note 
    The sanitize actions affect only the data -as it is logged to audit log. High-level debug logs may contain -sensitive data. Apache access log may contain sensitive data placed in -the request URI. -
    -

    sanitiseMatched

    -

    Description: Prevents the matched variable (request argument, -request header, or response header) from being logged to audit log. Each - byte of the named parameter(s) is replaced with an asterisk. -

    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,id:132,sanitiseMatched
    -
    -
    Note 
    The sanitize actions affect only the data -as it is logged to audit log. High-level debug logs may contain -sensitive data. Apache access log may contain sensitive data placed in -the request URI. -
    -

    sanitiseMatchedBytes

    -

    Description: Prevents the matched string in a variable from -being logged to audit log. Each or a range of bytes of the named -parameter(s) is replaced with an asterisk. -

    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 the credit card number. -

    -
    • sanitiseMatchedBytes -- This would x out only the bytes that -matched. -
    • sanitiseMatchedBytes:1/4 -- This would x out the bytes that -matched, but keep the first byte and last 4 bytes -
    -
    # Detect credit card numbers in parameters and 
    -# prevent them from being logged to audit log 
    -SecRule ARGS "@verifyCC \d{13,16}" "phase:2,id:133,nolog,capture,pass,msg:'Potential credit card number in request',sanitiseMatchedBytes"
    -SecRule RESPONSE_BODY "@verifyCC \d{13,16}" "phase:4,id:134,t:none,log,capture,block,msg:'Potential credit card number is response body',sanitiseMatchedBytes:0/4"
    -
    -
    Note 
    The sanitize actions affect only the data -as it is logged to audit log. High-level debug logs may contain -sensitive data. Apache access log may contain sensitive data placed in -the request URI. You must use capture action with sanitiseMatchedBytes, -so the operator must support capture action. ie: @rx, @verifyCC. -
    -

    sanitiseRequestHeader

    -

    Description: Prevents a named request header from being logged - to audit log. Each byte of the named request header is replaced with an - asterisk.. -

    Action Group: Non-disruptive -

    Example: This will sanitise the data in the Authorization -header. -

    -
    SecAction "phase:1,nolog,pass,id:135,sanitiseRequestHeader:Authorization"
    -
    -
    Note 
    The sanitize actions affect only the data -as it is logged to audit log. High-level debug logs may contain -sensitive data. Apache access log may contain sensitive data placed in -the request URI. -
    -

    sanitiseResponseHeader

    -

    Description: Prevents a named response header from being -logged to audit log. Each byte of the named response header is replaced -with an asterisk. -

    Action Group: Non-disruptive -

    Example: This will sanitise the Set-Cookie data sent to -the client. -

    -
    SecAction "phase:3,nolog,pass,id:136,sanitiseResponseHeader:Set-Cookie"
    -
    -
    Note 
    The sanitize actions affect only the data -as it is logged to audit log. High-level debug logs may contain -sensitive data. Apache access log may contain sensitive data placed in -the request URI. -
    -

    -severity

    -

    Description: Assigns severity to the rule in which it is used. -

    Action Group: Meta-data -

    Example: -

    -
    SecRule REQUEST_METHOD "^PUT$" "id:340002,rev:1,severity:CRITICAL,msg:'Restricted HTTP function'"
    -
    -

    Severity values in ModSecurity follows the numeric scale of syslog -(where 0 is the most severe). The data below is used by the OWASP -ModSecurity Core Rule Set (CRS): -

    -
    • 0 - EMERGENCY: is generated from correlation of anomaly -scoring data where there is an inbound attack and an outbound leakage. -
    • 1 - ALERT: is generated from correlation where there is -an inbound attack and an outbound application level error. -
    • 2 - CRITICAL: Anomaly Score of 5. Is the highest -severity level possible without correlation. It is normally generated -by the web attack rules (40 level files). -
    • 3 - ERROR: Error - Anomaly Score of 4. Is generated -mostly from outbound leakage rules (50 level files). -
    • 4 - WARNING: Anomaly Score of 3. Is generated by -malicious client rules (35 level files). -
    • 5 - NOTICE: Anomaly Score of 2. Is generated by the -Protocol policy and anomaly files. -
    • 6 - INFO -
    • 7 - DEBUG -
    -

    It is possible to specify severity levels using either the numerical -values or the text values, but you should always specify severity levels - using the text values, because it is difficult to remember what a -number stands for. The use of the numerical values is deprecated as of -version 2.5.0 and may be removed in one of the subsequent major updates. -

    -

    setuid

    -

    Description: Special-purpose action that initializes the USER -collection using the username provided as parameter. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule ARGS:username ".*" "phase:2,id:137,t:none,pass,nolog,noauditlog,capture,setvar:session.username=%{TX.0},setuid:%{TX.0}"
    -
    -

    After initialization takes place, the variable USERID will be -available for use in the subsequent rules. This action understands -application namespaces (configured using SecWebAppId), and will use one -if it is configured. -

    -

    setrsc

    -

    Description: Special-purpose action that initializes the -RESOURCE collection using a key provided as parameter. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecAction "phase:1,pass,id:3,log,setrsc:'abcd1234'"
    -
    -

    This action understands application namespaces (configured using -SecWebAppId), and will use one if it is configured. -

    -

    setsid

    -

    Description: Special-purpose action that initializes the -SESSION collection using the session token provided as parameter. -

    Action Group: Non-disruptive -

    Example: -

    -
    # Initialise session variables using the session cookie value 
    -SecRule REQUEST_COOKIES:PHPSESSID !^$ "nolog,pass,id:138,setsid:%{REQUEST_COOKIES.PHPSESSID}"
    -
    -

    Note -

    After the initialization takes place, the variable SESSIONID will - be available for use in the subsequent rules. This action understands -application namespaces (configured using SecWebAppId), and will use one -if it is configured. -

    -

    setenv

    -

    Description: Creates, removes, and updates environment -variables that can be accessed by Apache. -

    Action Group: Non-disruptive -

    Examples: -

    -
    SecRule RESPONSE_HEADERS:/Set-Cookie2?/ "(?i:(j?sessionid|(php)?sessid|(asp|jserv|jw)?session[-_]?(id)?|cf(id|token)|sid))" "phase:3,t:none,pass,id:139,nolog,setvar:tx.sessionid=%{matched_var}"
    -SecRule TX:SESSIONID "!(?i:\;? ?httponly;?)" "phase:3,id:140,t:none,setenv:httponly_cookie=%{matched_var},pass,log,auditlog,msg:'AppDefect: Missing HttpOnly Cookie Flag.'"
    -
    -Header set Set-Cookie "%{httponly_cookie}e; HTTPOnly" env=httponly_cookie
    -
    -
    Note 
    When used in a chain this action will be -execute when an individual rule matches and not the entire chain. -
    -

    setvar

    -

    Description: Creates, removes, or updates a variable. Variable - names are case-insensitive. -

    Action Group: Non-disruptive -

    Examples: -To create a variable and set its value to 1 (usually used for setting -flags), use: setvar:TX.score -

    To create a variable and initialize it at the same time, use: setvar:TX.score=10 -

    To remove a variable prefix the name with exclamation mark, use: setvar:!TX.score -

    To increase or decrease variable value, use + and - characters in - front of a numerical value: setvar:TX.score=+5 -

    Example from OWASP CRS: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bsys\.user_catalog\b" \
    -		"phase:2,rev:'2.1.3',capture,t:none,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase,t:replaceComments,t:compressWhiteSpace,ctl:auditLogParts=+E, \
    -block,msg:'Blind SQL Injection Attack',id:'959517',tag:'WEB_ATTACK/SQL_INJECTION',tag:'WASCTC/WASC-19',tag:'OWASP_TOP_10/A1',tag:'OWASP_AppSensor/CIE1', \
    -tag:'PCI/6.5.2',logdata:'%{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.sql_injection_score=+%{tx.critical_anomaly_score}, \
    -setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/SQL_INJECTION-%{matched_var_name}=%{tx.0}"
    -
    -
    Note 
    When used in a chain this action will be -execute when an individual rule matches and not the entire chain. -
    -

    skip

    -

    Description: Skips one or more rules (or chains) on successful - match. -

    Action Group: Flow -

    Example: -

    -
    # Require Accept header, but not from access from the localhost 
    -SecRule REMOTE_ADDR "^127\.0\.0\.1$" "phase:1,skip:1,id:141" 
    -
    -# This rule will be skipped over when REMOTE_ADDR is 127.0.0.1 
    -SecRule &REQUEST_HEADERS:Accept "@eq 0" "phase:1,id:142,deny,msg:'Request Missing an Accept Header'"
    -
    -

    The skip action works only within the current processing phase and -not necessarily in the order in which the rules appear in the -configuration file. If you place a phase 2 rule after a phase 1 rule -that uses skip, it will not skip over the phase 2 rule. It will skip -over the next phase 1 rule that follows it in the phase. -

    -

    -skipAfter

    -

    Description: Skips one or more rules (or chains) on a successful -match, resuming rule execution with the first rule that follows the rule - (or marker created by SecMarker) with the provided ID. -

    Action Group: Flow -

    Example: The following rules implement the same logic as -the skip example, but using skipAfter: -

    -
    # Require Accept header, but not from access from the localhost 
    -SecRule REMOTE_ADDR "^127\.0\.0\.1$" "phase:1,id:143,skipAfter:IGNORE_LOCALHOST" 
    -
    -# This rule will be skipped over when REMOTE_ADDR is 127.0.0.1 
    -SecRule &REQUEST_HEADERS:Accept "@eq 0" "phase:1,deny,id:144,msg:'Request Missing an Accept Header'" 
    -SecMarker IGNORE_LOCALHOST
    -
    -

    Example from the OWASP ModSecurity CRS: -

    -
    SecMarker BEGIN_HOST_CHECK
    -
    -	SecRule &REQUEST_HEADERS:Host "@eq 0" \
    -    		"skipAfter:END_HOST_CHECK,phase:2,rev:'2.1.3',t:none,block,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER_HOST',tag:'WASCTC/WASC-21', \
    -tag:'OWASP_TOP_10/A7',tag:'PCI/6.5.10',severity:'5',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.notice_anomaly_score}, \
    -setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score},setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/MISSING_HEADER-%{matched_var_name}=%{matched_var}"
    -
    -	SecRule REQUEST_HEADERS:Host "^$" \
    -    		"phase:2,rev:'2.1.3',t:none,block,msg:'Request Missing a Host Header',id:'960008',tag:'PROTOCOL_VIOLATION/MISSING_HEADER_HOST',tag:'WASCTC/WASC-21',tag:'OWASP_TOP_10/A7', \
    -tag:'PCI/6.5.10',severity:'5',setvar:'tx.msg=%{rule.msg}',setvar:tx.anomaly_score=+%{tx.notice_anomaly_score},setvar:tx.protocol_violation_score=+%{tx.notice_anomaly_score}, \
    -setvar:tx.%{rule.id}-PROTOCOL_VIOLATION/MISSING_HEADER-%{matched_var_name}=%{matched_var}"
    -
    -SecMarker END_HOST_CHECK
    -
    -

    The skipAfter action works only within the current processing phase -and not necessarily the order in which the rules appear in the -configuration file. If you place a phase 2 rule after a phase 1 rule -that uses skip, it will not skip over the phase 2 rule. It will skip -over the next phase 1 rule that follows it in the phase. -

    -

    status

    -

    Description: Specifies the response status code to use with -actions deny and redirect. -

    Action Group: Data -

    Example: -

    -
    # Deny with status 403
    -SecDefaultAction "phase:1,log,deny,id:145,status:403"
    -
    -

    Status actions defined in Apache scope locations (such as Directory, -Location, etc...) may be superseded by phase:1 action settings. The -Apache ErrorDocument directive will be triggered if present in the -configuration. Therefore if you have previously defined a custom error -page for a given status then it will be executed and its output -presented to the user. -

    -

    t

    -

    Description: This action is used to specify the transformation - pipeline to use to transform the value of each variable used in the -rule before matching. -

    Action Group: Non-disruptive -

    Example: -

    -
    SecRule ARGS "(asfunction|javascript|vbscript|data|mocha|livescript):" "id:146,t:none,t:htmlEntityDecode,t:lowercase,t:removeNulls,t:removeWhitespace"
    -
    -

    Any transformation functions that you specify in a SecRule will be -added to the previous ones specified in SecDefaultAction. It is -recommended that you always use t:none in your rules, which prevents -them depending on the default configuration. -

    -

    tag

    -

    Description: Assigns a tag (category) to a rule or a chain. -

    Action Group: Meta-data -

    Example: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \
    -	"phase:2,rev:'2.1.3',capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \
    -{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}"
    -
    -

    The tag information appears along with other rule metadata. The -purpose of the tagging mechanism to allow easy automated categorization -of events. Multiple tags can be specified on the same rule. Use forward -slashes to create a hierarchy of categories (as in the example). Since -ModSecurity 2.6.0 tag supports macro expansion. -

    -

    ver

    -

    Description: Specifies the rule set version. -

    Action Group: Meta-data -

    Version: 2.7 -

    Example: -

    -
    SecRule REQUEST_FILENAME|ARGS_NAMES|ARGS|XML:/* "\bgetparentfolder\b" \
    -	"phase:2,ver:'CRS/2.2.4,capture,t:none,t:htmlEntityDecode,t:compressWhiteSpace,t:lowercase,ctl:auditLogParts=+E,block,msg:'Cross-site Scripting (XSS) Attack',id:'958016',tag:'WEB_ATTACK/XSS',tag:'WASCTC/WASC-8',tag:'WASCTC/WASC-22',tag:'OWASP_TOP_10/A2',tag:'OWASP_AppSensor/IE1',tag:'PCI/6.5.1',logdata:'% \
    -{TX.0}',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.xss_score=+%{tx.critical_anomaly_score},setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-WEB_ATTACK/XSS-%{matched_var_name}=%{tx.0}"
    -
    -

    xmlns

    -

    Description: Configures an XML namespace, which will be used -in the execution of XPath expressions. -

    Action Group: Data -

    Example: -

    -
    SecRule REQUEST_HEADERS:Content-Type "text/xml" "phase:1,id:147,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,id:148
    -
    -

    -Operators

    -

    This section documents the operators currently available in -ModSecurity. -

    -

    - beginsWith

    -

    Description: Returns true if the parameter string is found at -the beginning of the input. Macro expansion is performed on the -parameter string before comparison. -

    Example: -

    -
    # Detect request line that does not begin with "GET" 
    -SecRule REQUEST_LINE "!@beginsWith GET" "id:149"
    -
    -
    -

    -contains

    -

    Description: Returns true if the parameter string is found -anywhere in the input. Macro expansion is performed on the parameter -string before comparison. -

    Example: -

    -
    # Detect ".php" anywhere in the request line 
    -SecRule REQUEST_LINE "@contains .php" "id:150"
    -
    -

    containsWord

    -

    Description: Returns true if the parameter string (with word -boundaries) is found anywhere in the input. Macro expansion is performed - on the parameter string before comparison. -

    Example: -

    -
    # Detect "select" anywhere in ARGS 
    -SecRule ARGS "@containsWord select" "id:151"
    -
    -

    Would match on -
    --1 union select -BENCHMARK(2142500,MD5(CHAR(115,113,108,109,97,112))) FROM wp_users WHERE - ID=1 and (ascii(substr(user_login,1,1))&0x01=0) from wp_users where - ID=1-- -

    But not on -
    -Your site has a wide selection of computers. -

    -

    -endsWith

    -

    Description: Returns true if the parameter string is found at -the end of the input. Macro expansion is performed on the parameter -string before comparison. -

    Example: -

    -
    # Detect request line that does not end with "HTTP/1.1" 
    -SecRule REQUEST_LINE "!@endsWith HTTP/1.1" "id:152"
    -
    -

    eq

    -

    Description: Performs numerical comparison and returns true if - the input value is equal to the provided parameter. Macro expansion is -performed on the parameter string before comparison. -

    Example: -

    -
    # Detect exactly 15 request headers 
    -SecRule &REQUEST_HEADERS_NAMES "@eq 15" "id:153"
    -
    -

    ge

    -

    Description: Performs numerical comparison and returns true if - the input value is greater than or equal to the provided parameter. -Macro expansion is performed on the parameter string before comparison. -

    Example: -

    -
    # Detect 15 or more request headers 
    -SecRule &REQUEST_HEADERS_NAMES "@ge 15" "id:154"
    -
    -

    -geoLookup

    -

    Description: Performs a geolocation lookup using the IP -address in input against the geolocation database previously configured -using SecGeoLookupDb. If the lookup is successful, the obtained -information is captured in the GEO collection. -

    Example: -The geoLookup operator matches on success and is thus best used in -combination with nolog,pass. If you wish to block on a failed lookup -(which may be over the top, depending on how accurate the geolocation -database is), the following example demonstrates how best to do it: -

    -
    # Configure geolocation database 
    -SecGeoLookupDb /path/to/GeoLiteCity.dat 
    -... 
    -# Lookup IP address 
    -SecRule REMOTE_ADDR "@geoLookup" "phase:1,id:155,nolog,pass"
    -
    -# Block IP address for which geolocation failed
    - SecRule &GEO "@eq 0" "phase:1,id:156,deny,msg:'Failed to lookup IP'"
    -
    -

    See the GEO variable for an example and more information on various -fields available. -

    -

    -gsbLookup

    -

    Description: Performs a local lookup of Google's Safe Browsing - using URLs in input against the GSB database previously configured -using SecGsbLookupDb. When combined with capture operator it will save -the matched url into tx.0 variable. -

    Syntax: SecRule TARGET "@gsbLookup REGEX" ACTIONS -

    Version: 2.6 -

    Example: -The gsbLookup operator matches on success and is thus best used in -combination with a block or redirect action. If you wish to block on -successful lookups, the following example demonstrates how best to do -it: -

    -
    # Configure Google Safe Browsing database 
    -SecGsbLookupDb /path/to/GsbMalware.dat 
    -... 
    -# Check response bodies for malicious links
    -SecRule RESPONSE_BODY "@gsbLookup =\"https?\:\/\/(.*?)\"" "phase:4,id:157,capture,log,block,msg:'Bad url detected in RESPONSE_BODY (Google Safe Browsing Check)',logdata:'http://www.google.com/safebrowsing/diagnostic?site=%{tx.0}'"
    -
    -

    gt

    -

    Description: Performs numerical comparison and returns true if - the input value is greater than the operator parameter. Macro expansion - is performed on the parameter string before comparison. -

    Example: -

    -
    # Detect more than 15 headers in a request 
    -SecRule &REQUEST_HEADERS_NAMES "@gt 15" "id:158"
    -
    -

    inspectFile

    -

    Description: Executes an external program for every variable -in the target list. The contents of the variable is provided to the -script as the first parameter on the command line. The program must be -specified as the first parameter to the operator. As of version 2.5.0, -if the supplied program filename is not absolute, it is treated as -relative to the directory in which the configuration file resides. Also -as of version 2.5.0, if the filename is determined to be a Lua script -(based on its .lua extension), the script will be processed by the -internal Lua engine. Internally processed scripts will often run faster -(there is no process creation overhead) and have full access to the -transaction context of ModSecurity. -

    The @inspectFile operator was initially designed for file -inspection (hence the name), but it can also be used in any situation -that requires decision making using external logic. -

    The OWASP ModSecurity Core Rule Set (CRS) includes a utility -script in the /util directory called runav.pl [13] that allows the file approval mechanism to -integrate with the ClamAV virus scanner. This is especially handy to -prevent viruses and exploits from entering the web server through file -upload. -

    -
    #!/usr/bin/perl
    -#
    -# runav.pl
    -# Copyright (c) 2004-2011 Trustwave
    -#
    -# This script is an interface between ModSecurity and its
    -# ability to intercept files being uploaded through the
    -# web server, and ClamAV
    -
    -
    -$CLAMSCAN = "clamscan";
    -
    -if ($#ARGV != 0) {
    -    print "Usage: modsec-clamscan.pl <filename>\n";
    -    exit;
    -}
    -
    -my ($FILE) = shift @ARGV;
    -
    -$cmd = "$CLAMSCAN --stdout --disable-summary $FILE";
    -$input = `$cmd`;
    -$input =~ m/^(.+)/;
    -$error_message = $1;
    -
    -$output = "0 Unable to parse clamscan output [$1]";
    -
    -if ($error_message =~ m/: Empty file\.?$/) {
    -    $output = "1 empty file";
    -}
    -elsif ($error_message =~ m/: (.+) ERROR$/) {
    -    $output = "0 clamscan: $1";
    -}
    -elsif ($error_message =~ m/: (.+) FOUND$/) {
    -    $output = "0 clamscan: $1";
    -}
    -elsif ($error_message =~ m/: OK$/) {
    -    $output = "1 clamscan: OK";
    -}
    -
    -print "$output\n";
    -
    -
    -

    Example: Using the runav.pl script: -

    -
    # Execute external program to validate uploaded files 
    -SecRule FILES_TMPNAMES "@inspectFile /path/to/util/runav.pl" "id:159"
    -
    -

    Example of using Lua script (placed in the same directory as the -configuration file): -

    -
    SecRule FILES_TMPNANMES "@inspectFile inspect.lua" "id:160"
    -
    -

    The contents of inspect.lua: -

    -
    function main(filename)
    -    -- Do something to the file to verify it. In this example, we
    -    -- read up to 10 characters from the beginning of the file.
    -    local f = io.open(filename, "rb");
    -    local d = f:read(10);
    -    f:close();
    -   
    -    -- Return null if there is no reason to believe there is ansything
    -    -- wrong with the file (no match). Returning any text will be taken
    -    -- to mean a match should be trigerred.
    -    return null;
    -end
    -
    -

    Reference: http://blog.spiderlabs.com/2010/10/advanced-topic-of-the-week-preventing-malicious-pdf-file-uploads.html -

    -

    -ipMatch

    -

    Description: Performs a fast ipv4 or ipv6 match of REMOTE_ADDR - variable data. Can handle the following formats: -

    -
    • Full IPv4 Address - 192.168.1.100 -
    • Network Block/CIDR Address - 192.168.1.0/24 -
    • Full IPv6 Address - 2001:db8:85a3:8d3:1319:8a2e:370:7348 -
    • Network Block/CIDR Address - -2001:db8:85a3:8d3:1319:8a2e:370:0/24 -
    -

    Examples: -

    Individual Address: -

    -
    SecRule REMOTE_ADDR "@ipMatch 192.168.1.100" "id:161"
    -
    -

    Multiple Addresses w/network block: -

    -
    SecRule REMOTE_ADDR "@ipMatch 192.168.1.100,192.168.1.50,10.10.50.0/24" "id:162"
    -
    -

    -ipMatchF

    -

    short alias for ipMatchFromFile -

    -

    ipMatchFromFile

    -

    Description: Performs a fast ipv4 or ipv6 match of REMOTE_ADDR - variable, loading data from a file. Can handle the following formats: -

    -
    • Full IPv4 Address - 192.168.1.100 -
    • Network Block/CIDR Address - 192.168.1.0/24 -
    • Full IPv6 Address - 2001:db8:85a3:8d3:1319:8a2e:370:7348 -
    • Network Block/CIDR Address - -2001:db8:85a3:8d3:1319:8a2e:370:0/24 -
    -

    Examples: -

    -
    SecRule REMOTE_ADDR "@ipMatch ips.txt" "id:163"
    -
    -

    The file ips.txt may contain: -

    -
    192.168.0.1
    -172.16.0.0/16
    -10.0.0.0/8
    -
    -

    le

    -

    Description: Performs numerical comparison and returns true if - the input value is less than or equal to the operator parameter. Macro -expansion is performed on the parameter string before comparison. -

    Example: -

    -
    # Detect 15 or fewer headers in a request 
    -SecRule &REQUEST_HEADERS_NAMES "@le 15" "id:164"
    -
    -

    lt

    -

    Description: Performs numerical comparison and returns true if - the input value is less than to the operator parameter. Macro expansion - is performed on the parameter string before comparison. -

    Example: -

    -
    # Detect fewer than 15 headers in a request 
    -SecRule &REQUEST_HEADERS_NAMES "@lt 15" "id:165"
    -
    -

    pm

    -

    Description: Performs a case-insensitive match of the provided - phrases against the desired input value. The operator uses a set-based -matching algorithm (Aho-Corasick), which means that it will match any -number of keywords in parallel. When matching of a large number of -keywords is needed, this operator performs much better than a regular -expression. -

    Example: -

    -
    # Detect suspicious client by looking at the user agent identification 
    -SecRule REQUEST_HEADERS:User-Agent "@pm WebZIP WebCopier Webster WebStripper ... SiteSnagger ProWebWalker CheeseBot" "id:166"
    -
    -
    Note 
    Starting on ModSecurity v2.6.0 this -operator supports a snort/suricata content style. ie: "@pm A|42|C|44|F". -
    -

    pmf

    -

    Short alias for pmFromFile. -

    -

    - pmFromFile

    -

    Description: Performs a case-insensitive match of the provided - phrases against the desired input value. The operator uses a set-based -matching algorithm (Aho-Corasick), which means that it will match any -number of keywords in parallel. When matching of a large number of -keywords is needed, this operator performs much better than a regular -expression. -

    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. -

    Example: -

    -
    # Detect suspicious user agents using the keywords in 
    -# the files /path/to/blacklist1 and blacklist2 (the latter 
    -# must be placed in the same folder as the configuration file) 
    -SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "id:167"
    -
    -

    Notes: -

    -
    1. Files must contain exactly one phrase per line. End of line -markers (both LF and CRLF) will be stripped from each phrase and any -whitespace trimmed from both the beginning and the end. Empty lines and -comment lines (those beginning with the # character) will be ignored. -
    2. To allow easier inclusion of phrase files with rule sets, -relative paths may be used to the phrase files. In this case, the path -of the file containing the rule is prepended to the phrase file path. -
    3. The @pm operator phrases do not support metacharacters. -
    4. Because this operator does not check for boundaries when -matching, false positives are possible in some cases. For example, if -you want to use @pm for IP address matching, the phrase 1.2.3.4 will -potentially match more than one IP address (e.g., it will also match -1.2.3.40 or 1.2.3.41). To avoid the false positives, you can use your -own boundaries in phrases. For example, use /1.2.3.4/ instead of just -1.2.3.4. Then, in your rules, also add the boundaries where appropriate. - You will find a complete example in the example. -
    -
    # Prepare custom REMOTE_ADDR variable 
    -SecAction "phase:1,id:168,nolog,pass,setvar:tx.REMOTE_ADDR=/%{REMOTE_ADDR}/"
    -
    -# Check if REMOTE_ADDR is blacklisted 
    -SecRule TX:REMOTE_ADDR "@pmFromFile blacklist.txt" "phase:1,id:169,deny,msg:'Blacklisted IP address'" 
    -
    -

    The file blacklist.txt may contain: -

    -
    # ip-blacklist.txt contents:
    -# NOTE: All IPs must be prefixed/suffixed with "/" as the rules
    -#   will add in this character as a boundary to ensure
    -#   the entire IP is matched.
    -# SecAction "phase:1,id:170,pass,nolog,setvar:tx.remote_addr='/%{REMOTE_ADDR}/'"
    -/1.2.3.4/ 
    -/5.6.7.8/
    -
    -
    Warning 
    Before ModSecurity 2.5.12, the -@pmFromFile operator understood only the LF line endings and did not -trim the whitespace from phrases. If you are using an older version of -ModSecurity, you should take care when editing the phrase files to avoid - using the undesired characters in patterns.e files should be one phrase - per line. End of line markers will be stripped from the phrases (LF and - CRLF), and whitespace is trimmed from both sides of the phrases. Empty -lines and comment lines (beginning with a '#') are ignored. To allow -easier inclusion of phrase files with rulesets, relative paths may be -used to the phrase files. In this case, the path of the file containing -the rule is prepended to the phrase file path. -
    -


    -

    -
    Note 
    Starting on ModSecurity v2.6.0 this -operator supports a snort/suricata content style. ie: "A|42|C|44|F". -
    -

    rbl

    -

    Description: Looks up the input value in the RBL (real-time -block list) given as parameter. The parameter can be an IPv4 address or a - hostname. -

    Example: -

    -
    SecRule REMOTE_ADDR "@rbl sbl-xbl.spamhaus.org" "phase:1,id:171,t:none,pass,nolog,auditlog,msg:'RBL Match for SPAM Source',tag:'AUTOMATION/MALICIOUS',severity:'2',setvar:'tx.msg=%{rule.msg}',setvar:tx.automation_score=+%{tx.warning_anomaly_score},setvar:tx.anomaly_score=+%{tx.warning_anomaly_score}, \
    -setvar:tx.%{rule.id}-AUTOMATION/MALICIOUS-%{matched_var_name}=%{matched_var},setvar:ip.spammer=1,expirevar:ip.spammer=86400,setvar:ip.previous_rbl_check=1,expirevar:ip.previous_rbl_check=86400,skipAfter:END_RBL_CHECK"
    -
    -
    Note 
    If the RBL used is dnsbl.httpbl.org -(Honeypot Project RBL) then the SecHttpBlKey directive must specify the -user's registered API key. -
    Note 
    If the RBL used is either multi.uribl.com -or zen.spamhaus.org combined RBLs, it is possible to also parse the -return codes in the last octet of the DNS response to identify which -specific RBL the IP was found in. -
    -

    rsub

    -

    Description: Performs regular expression data substitution -when applied to either the STREAM_INPUT_BODY or STREAM_OUTPUT_BODY -variables. This operator also supports macro expansion. Starting with -ModSecurity 2.7.0 this operator supports the syntax |hex| allowing users - to use special chars like \n \r -

    Syntax: @rsub s/regex/str/[id] -

    Examples: -Removing HTML Comments from response bodies: -

    -
    SecStreamOutBodyInspection On
    -SecRule STREAM_OUTPUT_BODY "@rsub s/<!--.*?-->/ /" "phase:4,id:172,t:none,nolog,pass"
    -
    -
    Note 
    If you plan to manipulate live data by -using @rsub with the STREAM_ variables, you must also enable -SecContentInjection directive. -
    -

    Regular expressions are handled by the PCRE library [14]. ModSecurity -compiles its regular expressions with the following settings: -

    -
    1. The entire input is treated as a single line, even when there -are newline characters present. -
    2. All matches are case-sensitive. If you wish to perform -case-insensitive matching, you can either use the lowercase -transformation function or force case-insensitive matching by prefixing -the regular expression pattern with the (?i) modifier (a PCRE feature; -you will find many similar features in the PCRE documentation). Also a -flag [d] should be used if you want to escape the regex string chars -when use macro expansion. -
    3. The PCRE_DOTALL and PCRE_DOLLAR_ENDONLY flags are set during -compilation, meaning that a single dot will match any character, -including the newlines, and a $ end anchor will not match a trailing -newline character. -
    -

    Regular expressions are a very powerful tool. You are strongly -advised to read the PCRE documentation to get acquainted with its -features. -

    -

    rx

    -

    Description: Performs a regular expression match of the -pattern provided as parameter. This is the default operator; the -rules that do not explicitly specify an operator default to @rx. -

    Examples: -

    -
    # Detect Nikto 
    -SecRule REQUEST_HEADERS:User-Agent "@rx nikto" phase:1,id:173,t:lowercase
    -
    -# Detect Nikto with a case-insensitive pattern 
    -SecRule REQUEST_HEADERS:User-Agent "@rx (?i)nikto" phase:1,id:174,t:none
    -
    -# Detect Nikto with a case-insensitive pattern 
    -SecRule REQUEST_HEADERS:User-Agent "(?i)nikto" "id:175"
    -
    -

    Regular expressions are handled by the PCRE library [15]. ModSecurity -compiles its regular expressions with the following settings: -

    -
    1. The entire input is treated as a single line, even when there -are newline characters present. -
    2. All matches are case-sensitive. If you wish to perform -case-insensitive matching, you can either use the lowercase -transformation function or force case-insensitive matching by prefixing -the regular expression pattern with the (?i) modifier (a PCRE feature; -you will find many similar features in the PCRE documentation). -
    3. The PCRE_DOTALL and PCRE_DOLLAR_ENDONLY flags are set during -compilation, meaning that a single dot will match any character, -including the newlines, and a $ end anchor will not match a trailing -newline character. -
    -

    Regular expressions are a very powerful tool. You are strongly -advised to read the PCRE documentation to get acquainted with its -features. -

    -

    streq

    -

    Description: Performs a string comparison and returns true if -the parameter string is identical to the input string. Macro expansion -is performed on the parameter string before comparison. -

    Example: -

    -
    # Detect request parameters "foo" that do not # contain "bar", exactly. 
    -SecRule ARGS:foo "!@streq bar" "id:176"
    -
    -

    -strmatch

    -

    Description: Performs a string match of the provided word -against the desired input value. The operator uses the pattern matching - Boyer-Moore-Horspool algorithm, which means that it is a single pattern - matching operator. This operator performs much better than a regular -expression. -

    Example: -

    -
    # Detect suspicious client by looking at the user agent identification 
    -SecRule REQUEST_HEADERS:User-Agent "@strmatch WebZIP" "id:177"
    -
    -


    -

    -
    Note 
    Starting on ModSecurity v2.6.0 this -operator supports a snort/suricata content style. ie: "@strmatch -A|42|C|44|F". -
    -

    validateByteRange

    -

    Description: Validates that the byte values used in input fall - into the range specified by the operator parameter. This operator -matches on an input value that contains bytes that are not in the -specified range. -

    Example: -

    -
    # Enforce very strict byte range for request parameters (only 
    -# works for the applications that do not use the languages other 
    -# than English). 
    -SecRule ARGS "@validateByteRange 10, 13, 32-126" "id:178"
    -
    -

    The validateByteRange is most useful when used to detect the presence - of NUL bytes, which don’t have a legitimate use, but which are often -used as an evasion technique. -

    -
    # Do not allow NUL bytes 
    -SecRule ARGS "@validateByteRange 1-255" "id:179"
    -
    -
    Note 
    You can force requests to consist only of -bytes from a certain byte range. This can be useful to avoid stack -overflow attacks (since they usually contain "random" binary content). -Default range values are 0 and 255, i.e. all byte values are allowed. -This directive does not check byte range in a POST payload when -multipart/form-data encoding (file upload) is used. Doing so would -prevent binary files from being uploaded. However, after the parameters -are extracted from such request they are checked for a valid range. -
    -

    validateByteRange is similar to the ModSecurity 1.X -SecFilterForceByteRange Directive however since it works in a rule -context, it has the following differences: -

    -
    • You can specify a different range for different variables. -
    • It has an "event" context (id, msg....) -
    • It is executed in the flow of rules rather than being a built -in pre-check. -
    -

    validateDTD

    -

    Description: Validates the XML DOM tree against the supplied -DTD. The DOM tree must have been built previously using the XML request -body processor. This operator matches when the validation fails. -

    Example: -

    -
    # Parse the request bodies that contain XML 
    -SecRule REQUEST_HEADERS:Content-Type ^text/xml$ "phase:1,id:180,nolog,pass,t:lowercase,ctl:requestBodyProcessor=XML"
    -
    -# Validate XML payload against DTD 
    -SecRule XML "@validateDTD /path/to/xml.dtd" "phase:2,id:181,deny,msg:'Failed DTD validation'"
    -
    -

    validateEncryption

    -

    Description: Validates REQUEST_URI that contains data -protected by the encryption engine. -

    Example: -

    -
    # Validates requested URI that matches a regular expression.
    -SecRule REQUEST_URI "@validateEncryption "product_info|product_list" "phase:1,deny,id:123456"
    -
    -

    validateSchema

    -

    Description: Validates the XML DOM tree against the supplied -XML Schema. The DOM tree must have been built previously using the XML -request body processor. This operator matches when the validation fails. -

    Example: -

    -
    # Parse the request bodies that contain XML 
    -SecRule REQUEST_HEADERS:Content-Type ^text/xml$ "phase:1,id:190,nolog,pass,t:lowercase,ctl:requestBodyProcessor=XML"
    -
    -# Validate XML payload against DTD 
    -SecRule XML "@validateSchema /path/to/xml.xsd" "phase:2,id:191,deny,msg:'Failed DTD validation'"
    -
    -

    validateUrlEncoding

    -

    Description: Validates the URL-encoded characters in the -provided input string. -

    Example: -

    -
    # Validate URL-encoded characters in the request URI 
    -SecRule REQUEST_URI_RAW "@validateUrlEncoding" "id:192"
    -
    -

    ModSecurity will automatically decode the URL-encoded characters in -request parameters, which means that there is little sense in applying -the @validateUrlEncoding operator to them —that is, unless you know that - some of the request parameters were URL-encoded more than once. Use -this operator against raw input, or against the input that you know is -URL-encoded. For example, some applications will URL-encode cookies, -although that’s not in the standard. Because it is not in the standard, -ModSecurity will neither validate nor decode such encodings. -

    -

    validateUtf8Encoding

    -

    Description: Check whether the input is a valid UTF-8 string. -

    Example: -

    -
    # Make sure all request parameters contain only valid UTF-8 
    -SecRule ARGS "@validateUtf8Encoding" "id:193"
    -
    -

    The @validateUtf8Encoding operator detects the following problems: -

    -
    Not enough bytes 
    UTF-8 supports two-, three-, -four-, five-, and six-byte encodings. ModSecurity will locate cases when - one or more bytes is/are missing from a character. -
    Invalid characters 
    The two most significant -bits in most characters should be fixed to 0x80. Some attack techniques -use different values as an evasion technique. -
    Overlong characters 
    ASCII characters are mapped - directly into UTF-8, which means that an ASCII character is one UTF-8 -character at the same time. However, in UTF-8 many ASCII characters can -also be encoded with two, three, four, five, and six bytes. This is no -longer legal in the newer versions of Unicode, but many older -implementations still support it. The use of overlong UTF-8 characters -is common for evasion. -
    -
    Notes 
    -
    -
    • Most, but not all applications use UTF-8. If you are dealing -with an application that does, validating that all request parameters -are valid UTF-8 strings is a great way to prevent a number of evasion -techniques that use the assorted UTF-8 weaknesses. False positives are -likely if you use this operator in an application that does not use -UTF-8. -
    • Many web servers will also allow UTF-8 in request URIs. If -yours does, you can verify the request URI using @validateUtf8Encoding. -
    -

    -verifyCC

    -

    Description: Detects credit card numbers in input. This -operator will first use the supplied regular expression to perform an -initial match, following up with the Luhn algorithm calculation to -minimize false positives. -

    Example: -

    -
    # Detect credit card numbers in parameters and 
    -# prevent them from being logged to audit log 
    -SecRule ARGS "@verifyCC \d{13,16}" "phase:2,id:194,nolog,pass,msg:'Potential credit card number',sanitiseMatched"
    -
    -

    -verifyCPF

    -

    Description: Detects CPF numbers (Brazilian social number) in -input. This operator will first use the supplied regular expression to -perform an initial match, following up with an algorithm calculation to -minimize false positives. -

    Example: -

    -
    # Detect CPF numbers in parameters and 
    -# prevent them from being logged to audit log 
    -SecRule ARGS "@verifyCPF /^([0-9]{3}\.){2}[0-9]{3}-[0-9]{2}$/" "phase:2,id:195,nolog,pass,msg:'Potential CPF number',sanitiseMatched"
    -
    -

    -verifySSN

    -

    Description: Detects US social security numbers (SSN) in -input. This operator will first use the supplied regular expression to -perform an initial match, following up with an SSN algorithm calculation - to minimize false positives. -

    Example: -

    -
    # Detect social security numbers in parameters and 
    -# prevent them from being logged to audit log 
    -SecRule ARGS "@verifySSN \d{3}-?\d{2}-?\d{4}" "phase:2,id:196,nolog,pass,msg:'Potential social security number',sanitiseMatched"
    -
    -

    Version: 2.6 -

    SSN Format: -

    A Social Security number is broken up into 3 sections: -

    -
    • Area (3 digits) -
    • Group (2 digits) -
    • Serial (4 digits) -
    -

    verifySSN checks: -

    -
    • Must have 9 digits -
    • Cannot be a sequence number (ie,, 123456789, 012345678) -
    • Cannot be a repetition sequence number ( ie 11111111 , -222222222) -
    • Cannot have area and/or group and/or serial zeroed sequences -
    • Area code must be less than 740 -
    • Area code must be different then 666 -
    -

    within

    -

    Description: Returns true if the input value is found anywhere - within the parameter value (the opposite of @contains). Macro expansion - is performed on the parameter string before comparison. -

    Example: -

    -
    # Detect request methods other than GET, POST and HEAD SecRule REQUEST_METHOD "!@within GET,POST,HEAD"
    -
    -

    Macro Expansion

    -

    Macros allow for using place holders in rules that will be expanded -out to their values at runtime. Currently only variable expansion is -supported, however more options may be added in future versions of -ModSecurity. -

    Format: -

    -
    %{VARIABLE}
    -%{COLLECTION.VARIABLE}
    -
    -

    Macro expansion can be used in actions such as initcol, setsid, -setuid, setvar, setenv, logdata. Operators that are evaluated at runtime - support expansion and are noted above. Such operators include -@beginsWith, @endsWith, @contains, @within and @streq. You cannot use -macro expansion for operators that are "compiled" such as @pm, @rx, etc. - as these operators have their values fixed at configure time for -efficiency. -

    Some values you may want to expand include: TX, REMOTE_ADDR, -USERID, HIGHEST_SEVERITY, MATCHED_VAR, MATCHED_VAR_NAME, -MULTIPART_STRICT_ERROR, RULE, SESSION, USERID, among others. -

    -

    Persistant Storage

    -

    At this time it is only possible to havefive collections in which -data is stored persistantly (i.e. data available to multiple requests). -These are: GLOBAL, RESOURCE, IP, SESSION and USER. -

    Every collection contains several built-in variables that are -available and are read-only unless otherwise specified: -

    -
    1. CREATE_TIME - date/time of the creation of the -collection. -
    2. IS_NEW - set to 1 if the collection is new (not yet -persisted) otherwise set to 0. -
    3. KEY - the value of the initcol variable (the client's IP - address in the example). -
    4. LAST_UPDATE_TIME - date/time of the last update to the -collection. -
    5. TIMEOUT - date/time in seconds when the collection will -be updated on disk from memory (if no other updates occur). This -variable may be set if you wish to specifiy an explicit expiration time -(default is 3600 seconds). -
    6. UPDATE_COUNTER - how many times the collection has been -updated since creation. -
    7. UPDATE_RATE - is the average rate updates per minute -since creation. -
    -

    To create a collection to hold session variables (SESSION) use action - setsid. To create a collection to hold user variables (USER) use action - setuid. To create a collection to hold client address variables (IP), -global data or resource-specific data, use action initcol. -

    -
    Note 
    Persistent collections can only be -initialized once per transaction. -
    -
    Note 
    ModSecurity implements atomic updates of -persistent variables only for integer variables (counters) at this time. - Variables are read from storage whenever initcol is encountered in the -rules and persisted at the end of request processing. Counters are -adjusted by applying a delta generated by re-reading the persisted data -just before being persisted. This keeps counter data consistent even if -the counter was modified and persisted by another thread/process during -the transaction. -
    -
    Note 
    ModSecurity uses a Berkley Database (SDBM) -for persistant storage. This type of database is generally limited to -storing a maximum of 1008 bytes per key. This may be a limitation if you - are attempting to store a considerable amount of data in variables for a - single key. Some of this limitation is planned to be reduced in a -future version of ModSecurity. -
    -

    Miscellaneous Topics

    -

    Impedance Mismatch

    -

    Web application firewalls have a difficult job trying to make sense -of data that passes by, without any knowledge of the application and its - business logic. The protection they provide comes from having an -independent layer of security on the outside. Because data validation is - done twice, security can be increased without having to touch the -application. In some cases, however, the fact that everything is done -twice brings problems. Problems can arise in the areas where the -communication protocols are not well specified, or where either the -device or the application do things that are not in the specification. -In such cases it may be possible to design payload that will be -interpreted in one way by one device and in another by the other device. - This problem is better known as Impedance Mismatch. It can be exploited - to evade the security devices. -

    While we will continue to enhance ModSecurity to deal with -various evasion techniques the problem can only be minimized, but never -solved. With so many different application backend chances are some will - always do something completely unexpected. The only solution is to be -aware of the technologies in the backend when writing rules, adapting -the rules to remove the mismatch. See the next section for some -examples. -

    -

    - Impedance Mismatch with PHP Apps

    -
    1. When writing rules to protect PHP applications you need to pay -attention to the following facts: -
    2. When "register_globals" is set to "On" request parameters are -automatically converted to script variables. In some PHP versions it is -even possible to override the $GLOBALS array. -
    3. Whitespace at the beginning of parameter names is ignored. -(This is very dangerous if you are writing rules to target specific -named variables.) -
    4. The remaining whitespace (in parameter names) is converted to -underscores. The same applies to dots and to a "[" if the variable name -does not contain a matching closing bracket. (Meaning that if you want -to exploit a script through a variable that contains an underscore in -the name you can send a parameter with a whitespace or a dot instead.) -
    5. Cookies can be treated as request parameters. -
    6. The discussion about variable names applies equally to the -cookie names. -
    7. The order in which parameters are taken from the request and -the environment is EGPCS (environment, GET, POST, Cookies, built-in -variables). This means that a POST parameter will overwrite the -parameters transported on the request line (in QUERY_STRING). -
    8. When "magic_quotes_gpc" is set to "On" PHP will use backslash -to escape the following characters: single quote, double quote, -backslash, and the nul byte. -
    9. If "magic_quotes_sybase" is set to "On" only the single quote -will be escaped using another single quote. In this case the -"magic_quotes_gpc" setting becomes irrelevant. The "magic_quotes_sybase" - setting completely overrides the "magic_quotes_gpc" behaviour but -"magic_quotes_gpc" still must be set to "On" for the Sybase-specific -quoting to be work. -
    10. PHP will also automatically create nested arrays for you. For -example "p[x][y]=1" results in a total of three variables. -
    -

    - A Recommended Base Configuration

    -

    The following is a recommended configuration file which handles the -main ModSecurity directives/setting. These are the items that the Admin - should handle and configure for their own site. These settings should -not be including within 3rd party rules files. -

    -
    # -- Rule engine initialization ----------------------------------------------
    -
    -# Enable ModSecurity, attaching it to every transaction. Use detection
    -# only to start with, because that minimises the chances of post-installation
    -# disruption.
    -#
    -SecRuleEngine DetectionOnly
    -
    -
    -# -- Request body handling ---------------------------------------------------
    -
    -# Allow ModSecurity to access request bodies. If you don't, ModSecurity
    -# won't be able to see any POST parameters, which opens a large security
    -# hole for attackers to exploit.
    -#
    -SecRequestBodyAccess On
    -
    -
    -# Enable XML request body parser.
    -# Initiate XML Processor in case of xml content-type
    -#
    -SecRule REQUEST_HEADERS:Content-Type "text/xml" \
    -     "phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
    -
    -
    -# Maximum request body size we will accept for buffering. If you support
    -# file uploads then the value given on the first line has to be as large
    -# as the largest file you are willing to accept. The second value refers
    -# to the size of data, with files excluded. You want to keep that value as
    -# low as practical.
    -#
    -SecRequestBodyLimit 13107200
    -SecRequestBodyNoFilesLimit 131072
    -
    -# Store up to 128 KB of request body data in memory. When the multipart
    -# parser reachers this limit, it will start using your hard disk for
    -# storage. That is slow, but unavoidable.
    -#
    -SecRequestBodyInMemoryLimit 131072
    -
    -# What do do if the request body size is above our configured limit.
    -# Keep in mind that this setting will automatically be set to ProcessPartial
    -# when SecRuleEngine is set to DetectionOnly mode in order to minimize
    -# disruptions when initially deploying ModSecurity.
    -#
    -SecRequestBodyLimitAction Reject
    -
    -# Verify that we've correctly processed the request body.
    -# As a rule of thumb, when failing to process a request body
    -# you should reject the request (when deployed in blocking mode)
    -# or log a high-severity alert (when deployed in detection-only mode).
    -#
    -SecRule REQBODY_ERROR "!@eq 0" \
    -"phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
    -
    -# By default be strict with what we accept in the multipart/form-data
    -# request body. If the rule below proves to be too strict for your
    -# environment consider changing it to detection-only. You are encouraged
    -# _not_ to remove it altogether.
    -#
    -SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
    -"phase:2,t:none,log,deny,status:44,msg:'Multipart request body \
    -failed strict validation: \
    -PE %{REQBODY_PROCESSOR_ERROR}, \
    -BQ %{MULTIPART_BOUNDARY_QUOTED}, \
    -BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
    -DB %{MULTIPART_DATA_BEFORE}, \
    -DA %{MULTIPART_DATA_AFTER}, \
    -HF %{MULTIPART_HEADER_FOLDING}, \
    -LF %{MULTIPART_LF_LINE}, \
    -SM %{MULTIPART_MISSING_SEMICOLON}, \
    -IQ %{MULTIPART_INVALID_QUOTING}, \
    -IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
    -IH %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
    -
    -# Did we see anything that might be a boundary?
    -#
    -SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \
    -"phase:2,t:none,log,deny,status:44,msg:'Multipart parser detected a possible unmatched boundary.'"
    -
    -# PCRE Tuning
    -# We want to avoid a potential RegEx DoS condition
    -#
    -SecPcreMatchLimit 1000
    -SecPcreMatchLimitRecursion 1000
    -
    -# Some internal errors will set flags in TX and we will need to look for these.
    -# All of these are prefixed with "MSC_".  The following flags currently exist:
    -#
    -# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded.
    -#
    -SecRule TX:/^MSC_/ "!@streq 0" \
    -        "phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
    -
    -
    -# -- Response body handling --------------------------------------------------
    -
    -# Allow ModSecurity to access response bodies. 
    -# You should have this directive enabled in order to identify errors
    -# and data leakage issues.
    -# 
    -# Do keep in mind that enabling this directive does increases both
    -# memory consumption and response latency.
    -#
    -SecResponseBodyAccess On
    -
    -# Which response MIME types do you want to inspect? You should adjust the
    -# configuration below to catch documents but avoid static files
    -# (e.g., images and archives).
    -#
    -SecResponseBodyMimeType text/plain text/html text/xml
    -
    -# Buffer response bodies of up to 512 KB in length.
    -SecResponseBodyLimit 524288
    -
    -# What happens when we encounter a response body larger than the configured
    -# limit? By default, we process what we have and let the rest through.
    -# That's somewhat less secure, but does not break any legitimate pages.
    -#
    -SecResponseBodyLimitAction ProcessPartial
    -
    -
    -# -- Filesystem configuration ------------------------------------------------
    -
    -# The location where ModSecurity stores temporary files (for example, when
    -# it needs to handle a file upload that is larger than the configured limit).
    -# 
    -# This default setting is chosen due to all systems have /tmp available however, 
    -# this is less than ideal. It is recommended that you specify a location that's private.
    -#
    -SecTmpDir /tmp/
    -
    -# The location where ModSecurity will keep its persistent data.  This default setting 
    -# is chosen due to all systems have /tmp available however, it
    -# too should be updated to a place that other users can't access.
    -#
    -SecDataDir /tmp/
    -
    -
    -# -- File uploads handling configuration -------------------------------------
    -
    -# The location where ModSecurity stores intercepted uploaded files. This
    -# location must be private to ModSecurity. You don't want other users on
    -# the server to access the files, do you?
    -#
    -#SecUploadDir /opt/modsecurity/var/upload/
    -
    -# By default, only keep the files that were determined to be unusual
    -# in some way (by an external inspection script). For this to work you
    -# will also need at least one file inspection rule.
    -#
    -#SecUploadKeepFiles RelevantOnly
    -
    -# Uploaded files are by default created with permissions that do not allow
    -# any other user to access them. You may need to relax that if you want to
    -# interface ModSecurity to an external program (e.g., an anti-virus).
    -#
    -#SecUploadFileMode 0600
    -
    -
    -# -- Debug log configuration -------------------------------------------------
    -
    -# The default debug log configuration is to duplicate the error, warning
    -# and notice messages from the error log.
    -#
    -#SecDebugLog /opt/modsecurity/var/log/debug.log
    -#SecDebugLogLevel 3
    -
    -
    -# -- Audit log configuration -------------------------------------------------
    -
    -# Log the transactions that are marked by a rule, as well as those that
    -# trigger a server error (determined by a 5xx or 4xx, excluding 404,  
    -# level response status codes).
    -#
    -SecAuditEngine RelevantOnly
    -SecAuditLogRelevantStatus "^(?:5|4(?!04))"
    -
    -# Log everything we know about a transaction.
    -SecAuditLogParts ABIJDEFHKZ
    -
    -# Use a single file for logging. This is much easier to look at, but
    -# assumes that you will use the audit log only ocassionally.
    -#
    -SecAuditLogType Serial
    -SecAuditLog /var/log/modsec_audit.log
    -
    -# Specify the path for concurrent audit logging.
    -#SecAuditLogStorageDir /opt/modsecurity/var/audit/
    -
    -
    -# -- Miscellaneous -----------------------------------------------------------
    -
    -# Use the most commonly used application/x-www-form-urlencoded parameter
    -# separator. There's probably only one application somewhere that uses
    -# something else so don't expect to change this value.
    -#
    -SecArgumentSeparator &
    -
    -# Settle on version 0 (zero) cookies, as that is what most applications
    -# use. Using an incorrect cookie version may open your installation to
    -# evasion attacks (against the rules that examine named cookies).
    -#
    -SecCookieFormat 0
    -
    - - - - - - -
    -
    -
    -
    -
    -
    -
    Views
    - -
    -
    -
    Personal tools
    -
    -
    -
      -
    -
    -
    - - - - - -
    -
    - -
    - - - - - - - \ No newline at end of file diff --git a/doc/Reference_Manual_files/600px-Apache_request_cycle-modsecurity.jpg b/doc/Reference_Manual_files/600px-Apache_request_cycle-modsecurity.jpg deleted file mode 100644 index 6241c7252ff0b6140c9bbe11cdf8fc396fa9f92b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32954 zcmeFZWmua{*Df4Nfwo9XDV_i=THKws6o&%ECD4|l!D(=cwm1YUZpAe?!L7IjcXtQ` zx1t}tpZ9s?*w4P7@5kQ%_6o;!T*q9KHJO<;Yn|t;Yq*`fT?9OlmzI+TU|?VXobNt> z+Zlia0Q)``Hr9P?Y%FXX9P9^=o;-T=@ZqCp1ds8akUk?LBYj3fLII+url6z+l914_ z($F$6zGQw$PR+*2#>7d_^pfee8G!o$qxYUR1_mMEwhaITU;r?%?wa^7!M=xi9}5TL z!QE5iCjbmg%)38=NB19LVdLT4)&BF85F6*|b0RMBXC$OARMa&L>>{F~%Z7=$dHA*L z9exarJfMYo3%pg;{QM=Yw7i0jPpxH%Uc%7WF@}s$QrgJJH!?kAbf1BV_uKbhZMV~a z2Y=ds4M4j-R?sdu){o3D87y8DFQODJ$+3^DRHQK$vrbD0@QzNa}LCHFaH=v;fYdlW0nv2 z8-Wjqs_!hMNK5yvxa{InPnX_2OWW(QkzZ(k^?(0H-={1gt-VAUuLzTlYpLz=Y;k%3 znsn9vZ03i@boW$oAO7nTriN&G$^Riz-kyb#bDwK8o5zK9%^?hiACrgIE5Zqc1=%!_ z9F@9>Z?%gx4qAyd9B3IM!hOgdQn%la3VX>c``?El<073br+TY;BQUaLmO7>(t@Rc# ztZq+SdQLI5h~jv&(DDi!;Vc|HD6p^W4NoCRKgPczTD181Ew=&F^ZviAX;yjg({1Txa%N4W!=-{-_N(oL%8 ztu@rZ+)U_-V%jK};JHw1PHN>XU`+pn@V|-K$%Foxch~8+fWv6q2ry#Ap`fU8roSve zkD2d5Ml_t`dCZ2wGpkg}DFXK7P@DjE!*g4jcZroVTrk>wtEFHRK`0Y8EKXNnzi}ah?I0`o`@p*7Z35UCEo2y!R(@2 zcG&L&0L)VQ2z&vVk1ko^nnL3(oOU6SN1_q(UHU|1)Qwa|5pYWMC#Rw;N;U9 zg0(LMjosRmEi+5ghU}nmUj#8yAfUvLLz;6ht%!LwMyqO=v3P%cc@Ug&3y>zpRt3}g z)B8}?KNM-hv(H6K*Ef}$6O#z*GP~B;T)Z~qWAkq}gD1~Ny5@+d>{92zDbtobc6 zW0*Oca8ht}zA_#m{H&jU;WMG5Q`6%h$`K0{oS%aT4(#gXC~l+E%|-BumnVK&4Wt)_ zC=jK(S;*Pg`yg?RCm2RO!r&+|JTWU&=5 zY3D?~T&xKSc`y?d;}=G%ERT|izgvGKVefAN@Cu~R%@fbx^1hfO5tbb*o|)+tBNbZy z)v7`V8j{`mis~qTesU^II<|PE3m&7OP%rqG3U>c-;j=OIece8lQL^sYy!dP?B{fzn z-om z6%JBZmri`huOd&c>11_K-^=~+YgeToIy+f)Ms~Pf*}jACxId3{&OI5B*+E&beLbl- z=$A4?dFwO5b0hlc`}5+|x9K?>$+3Kn_;Pv3HoSRWL{oat+8xSlvuAKCiqg zr}L0&dFoMNC@K=VSftHMPoJSZ;6YTY+H0?wK&BwaJK87GUKD|ZX4yG`RzmO|lh1-@ zAU?lce`Z*te`Q2gYnE4`Yoz0}uTmxgve{ZaBO)<`gtz!TvlsAzo;ZzMGWe{|_JZ=w zRs(-5ixX^5?=R}ohnosLZ+og0Zh5IFx$Q!-e4hK<^4am>s3IM{#b>U22GCw0x>Klb z7vlI>xVLokQYp>5)OdCF~RCLZB zU2zT8qIZx6uJkBA$J!HOOvUtCEZJKSBz$iY7ENeV9!=}a<7^6=bs}Y%@V^C6yRm~rle_NRG0BEHDnD4WvP74L z_dw-UWcG%Aisl|!Ub-2Ot3OuIZlw9G>BX9=BT%vzj$~=tv!r|hm0bVZ}*>A_CEnV0tF3xz$^R!ch zYH(ffn~#8g>TJpS(#5-B&nbUTSZ(9^uBX|RCjQ}()aMEfefDw(F?<%M^&g>&9UJ7$ z-MZ%eQl_K2%xE3M8dbfQp}?ugg!k7AV=4CuXwHMeb~Ljeu?1|o=67fKBv5+3q$?A_ zHxw75JI1OO5g&eyspv@I=RLk@`fMP%xa%ol_@EF{h6)69iH6HnfW!=|^iYbH4rPNX zC-y&%k@L_DI*b|UJ-Nk)U>S4pI|^%`-*$z53*tIJWkZx61xC0re89nw2Mlo#Uj*!ny=0WE#YrtQLG}QYfC>z)B$?Y0NN0XF~sOf_(VP|hqXKE+!{XKCa=N9G% z1lrN112E>fI(-QE2ce>bM|z5o>9c-TM`dQ+4-_(j^l-bCLDC#Oh@Fb1*86o9{unTcGIT(LL^r3sgFVSpCkS zYhH#AiOQ@%X2cTQ#vJFVGqfm>J+HiDaeVw?Q`UW(=tf6g-^J02$o3`~EccHGQiMLK_-7!6L=tWwv`- z1$~_twofPE%Ku<}DI}RSk)*Ea-H0l`2&=}>Xg7}x^VG{F_Us_G1H!D;!zrVE0I5Aa+^}f#3eLD!sS`INTC!_ShZ)LPUx!7I& zD}nV|-6|%_t4r5`^SJ+e)Qnx8yF$+edi``YgOso*(1~{f=tN*#AQ%cDe ztmYct*+Y=@wyz=QV)DIMrlsydF^8snZ&5ZtjkL_EUFn6r9q>t)*M`slsb|ZF8VCxX z1A!l<3s;P&C~1cRdY?z^GTc*0T4{@lrXADF|5nMyXUEGkHp7Chbkv=Y{Ix{%v5r>Z z77#g7EA5uaZb8s$6E!loT&;Xp#`Inpqb>fPN!NUqqv$&`oSNvWZ*RPbOVJQTWC#l&8`(tbZlm!up2IX z0(#w1O|Zk+F)Tf^Zm3m9X^wXzV2vDO7QwT&oC6A}a5{2bxCK;u5*Lk%ycpJU16egv z>hs~E3}=ZsWo9~8td+Pc$KlZscdQ)ivaL%G<~f^Om~QtfM|5+CGl6#`Pcs`R%>E{@ z)c;B?XAQ^C^--8k1wnpcZu)pQ7~YX9g-KIR-V8Hy4QS~lUBS3++=l>Lf!mgj#<{ty z1LZnPIYz45ip1^`IX3jRabMriifW&(Efk6_F3DDscvp`NZq>YG$jdFruGU4dFy$05 z#E@-u(5)JZ707LM3J1+2WSpzD%$*fQpLUArTOnsYvRPDNZme{D0odLsBq?$A6^>b+ z=?vJ_n3GJR-&JeWW)oTwE6y1~)dGIMxiWMXs-L6D!(T9o6++nARH==Qj?PVC-RF5f zs!6I+9>Kc6YJe6;D@T7q=||78Uj%FXG6+&|pY2ssuywacn3e+pLIlDw5sv%kQyqI@ zJ|Vh=6pyHh#hdDe~?zBU^F zY_e$aW1HWK`4SC2YyVuB|3zHMBnn!K{s=-sL>i@-uKac_*G6L+Qvzk!C zl$?Q;91&wYKygJp6S?)*gP)ea7Rb?KXO3F6>#NtgdLvixXxYjt17AB-9I%J&Oft(S2eG@A+~nerG_5T?j?U51J4N)XStlu$4LNFOKlGw%5lPj({GOS!Z3n}%D)HxprY0Vp&g*z$O zYNBg{%@xfo9HHj;m;l^aXX-1K`RdcGV+V_q8L`prD05F4&881Lb4t~xIjM`NiEV^i z-UQWXBh1S57GTXYr#s(-2$;#MQZBzVA~wP#Vrb|<>e(L}!Dc{$>tOa8{+tv_;mZA6 zKr#7lJ~)^E=R`yQR70lF`GReOG;V2sR9(BqoxU3w+@s#KHrWoXu=!Ub1=v%T6T;uj zYd_xta*z9o6Z}T7)RpbENJdHHb-cX*TbP$xM!>E+c3)Bylh2az$?EGpgZqBkFj-bR z!#G)*ZwDWiR_(O)Ev7}r=2MhigeNMT3K~RH4jqck_Ox7G#8$UI49yoEAG&pqH)J&E zZ>^q1!QEtB>qffw704cKJ~{4Oc^ zYvP0Du~rc2u-?n`+#EaPN+%Z(dEd^$UmW;0D4{Mp1ZRm+(P3mq5Y<12%IEhS_C5@u z?gfmK7y5C@4bKTiOp>*VyOk`%{_+l_;G9c2@s9!&P*Q$#w61 z_jU`tYZqCBM4FlgMyCGe0A8jN&$~j(Q5RAFB?z{Fl3sl>Gbf9W*D|=ffz+lF-3RDui^ze6uVk25?d*iG0a724ykFt)1|`939;? z!D>OjW}y*1z&rpCODp5OT%n*4^f;Q+_-h=QjTr5B4c#)tMneR-(pE_g12M^&Gw*)H zdn=tv{MXrhn1z*nH3PPm___A>_R6UaFuHqA;+@=Mj^-mCk4GDGQs+K^Dz?f8tpB># zd;MNhb;wFJ^&`Z!Ur|llTmogr!g|ExN;};EchkidfciyT_UvL>>DmOGzgBZiv1xIe zDo{Lst$LAS;~>HZX~IE$RO{c(F@Xy#-UVuxhS8m|>?`U#cV&Uvt|}^ znu+@!Stp|NsC{&?B0%ov=$#8vOtHr7%hA=m1&H5yEMn9U?P^+~!B-_WCpkd)7hypk zMDCxtx|4rH@_Gmf{AiOc=QLC!#>Y7mo+uYW=7>k9JN0rt`YfWAGCAQ(& z$vG`0jJ~(hdpvX@K$s}WK$U1)>O9d?PdQ1)h{B8Bib2hj=S8T35dOoC{+endMg${H zBCtn8M}#TQG%xQ*ry?eZHIgWN+&j{(9>B~eQ^dqhY5}4zO4$A==al2{j+EK;GemnR z<*DFeMG};7SS0`b@EKl3q^Kl|hA%ojPXu}Y?ID3nH52LP($&MQkCSpYEEDqoH1oeV z@eJLbZ50K4KCHqp-(3z)RU;$L6^F4hlTzv~~XFw=Kq)JH>1CezuRK zfzJ%KCXV}Ie7w}xN0uAssnEVqiI&;mkKxcN)Wx}3{!4WEv>6u9!b3w&G7<5ih&~x< zQo)G}^L#NmL@f%}p$mCoPLLkM(0%c1AE^bUrVN7$wx~4ZLzol!jIHP!$MP~Q(9isk z7lyormL1aMzf~W1(7L7O0B1>1y(aZC2$Vu#W+OMPQwlA^PEz7?l*$aA4^O16t1JeD zH7234!aNu5n2*|oobVA%x$ut2u-RB{2+9#1>aIVs!I&k2 z_J%>_SSe-2N_vpvIjkGD=VH6l)7lEgmVHqXFY;#HJ_(dZ^+(UVmYFu677LY7{^D0e zzT*CgaE!0t*@kJy)``lv>>BvmTa4xM;p=wSE!n%7`Xm2lk*e^_0Ii`(6o78VIOkCb zS)r=!=nok*2`=7QXrqi=g6wp6(VJy5!}8Pa;}C@~ zsxJk152dVS!XJd$cZc{zg$Z+u8tmb7uV6K^^L%X-k+teMvM(-MuwY_gJ>Hk%YxF+B zU$B^!rGv6{&EV$}P}u0YMrv%%;1~R^fs4s8@1y-jr3n(p<1U~6Db4jx(k5v4x?%!ShcC7_bR$TN%82gP`MGWb&pWy!tk`7(L)jwFH&E)&} zZB5lp1D_AEpe!XpWRR5;TGC4ClF$(XAkqCRiV!sy*zLswm)GNv0(X)B&+Nh9Z2|6) z_zNZhQ}7R>)o2Z=&MGrpZUJP+(SN=ZvMu~5Rsw5$@?p(Pl8J9kb9Cxagh<<)fge~k z<+IOg!Fk&+133rCB5F1O$5cx=-XJWK^uOBr>+-j$Cq_(q@y~|El**A#0F(uYk)9}F z*E}$6n?gp3j694v8N=(|UrJcGt$WVioC;P{v5U5L6=Qg}-1%_gD%m9-?a5hja&{!( z77!#s^xS$I8$*aLDe`On@{1Sy;X*w&1e-_M`@6m?`|B&TVmQ@WPx7Y~u>+g3Snt%0;@PI$0Q#icna`+3mN#(&udhv(~>n*?fkd}FIgIHGiT){?eIAMsjNQBKT0Pj;?&tGb?WelpZ(mKCE=VTMs zI|k2e>N3yd^X@vP!?Bi3r?_BdbMK(_4C@R3gUN9Lk)NN8*3<(6e81pp@aHz5W2}Ts z#-jNtzmmvq_Znmbeg2JyG*~`}Xr(ev;pImN-U2$3N5ZZ(6wf(?JI9;kV_$2ic|5R} zt^h<;g2OQb8OGjmT?ebUPl5K{4_M~bTTv`&@YNb<$S@(^bV|{*5D=J=17{y&Rc%x2 zUNh_cQN*ZyF|nn(fy%dO`7f0Z$6r-msMlQNC+;hio)iyf4UDNT_zZoRDy(pX553Eq zaEJ{Ofsa){NBYI@zbUtmYR?eEt-AjohW5)EQng=M;$i$jd*~U) zAWw^U9wNsfFFPnb)s&)E*7#GTK)_OKg0%Sgbvv}oYI}1+>q5}|B-mk^|7SizqeY?I zUArg#x!ViqN&v5Dg_p8Te-f)cPmztI1r`?OsZ{}p^+{!7Xu^!04nU9Bfa>>5hq*Ywu3w0^Mx)T9>@KNu}_{m@oT zRwN0&Re>+gNur1P^NO}b*e>&KSRq%)yw{j4I?Kc7+> z-Kn(M-TDBqg1G;Lr%&Cw&hp2oV#33UvB|lu+I0B>w-3M0X?#Q7vgeTxp7%H7h+enH z>Qv@4<*|=(YVWG4fqj|2uJsuipAhoPPRohqVxS4^n#m6e$sI%Y`KC{&*v<-kf0<9_ z#apkX+uQFwORD>BP1fq{wJ%=IRE{ z5`zuN#@skT{cptl*^BOm_%D%k3_cWgL-)M4GTNC~$4fjCZ1Z!^EF-~LM6_;w@7(frF;fV(%!HPqt)Q`iTY&7&DO;-0z^`X| zw}7h8s0OA#epxOaND1_=E5cfYp8M4P0Ywdk5NMahn{IkA<#3}A`Ex=;k?Qk>eJy|P zJCBkr6ELZ_YcNZ$iM;X=;9CpXps5PHBIU}VzA?5I1S-Y;NxI6qPNLvRswn1U710Tb z0FXzTpPw{>Q}Ts?k)V^DHqlOu-$(zKiS~K0p)c(5Ki+wJjf__Nm^?LfnPj`qnS@oG zYA9MytlFfZ_li6}!Aa5XSs#HtRC01P44N2VsN~97zKKwjd-HoGt!}hMTurjaHNj*= z+C@(1fp3|AMEy|hPz+|sfsXEm{20&Y%G%F8-36%hpFxp$24S)GFi~gBRu(ypQe<&P zLUx9#-ixO3GP7-#8CZ(VW~|b&N9I|Qy3B>s(xq{py8ak36zc=+?WnNHlqWVcz1M*! ze1UCbby~f@$JI<_&@w9$WQQQ7*ZvRJ|98IsJ$|Dn zW1MxD7}aarw4*vF1<7&H>(5G0(dBQl9cybIKKDNG?)#}*CHtYsHGRW5dnEc- z8!DfC;Z4_6kK&UIE)v8GCpp6b!XYFH(J|KP!bb(!ejz(}#K@4kI5g6v>%!R(_Ga_L z)do$qEs)@=Lf0|D&vYMHda4pqU)d%-AzQHPae{UMy;~>Ij9m44#PVY0Spk-_k59i& zc0P*@DW(3hye??nX82Wlfh+mPC@)>XNI~C`Q#9W33`urQq325te5qQY)TBL81v0yV zXh*6$rYmI)!6lEWXqalotj#yFGC5tubY4C0xrT(KdxK_4Ly2+fqlwaR_ya#UVyx88-y^CP>JDx?2(M2BlIZFR5 za*(9tXi`Vv5_hUtOmDYoCY0*n^TwQ~V=WRMBtZMVS`>W$_dzR&6k%ayykJ}#vtXGM znT<#fFJm$!ht5SbGleFaIo0Cw)rMa3j_tbh@!D1+nD!NPF*>c5bsD0IUrQ(S4$15> z(@!UlKRC;pLX?EYZ*Xcu5F}+Kh^whR$xjf#K2-4!ofksBOH7*Q8?vXD8ZVI5%^X#aRGIsYU6xN;FBnMK zoCL!W9M}V2G2Ce)&U>wWBR3R9ZmqY{zpC}7xmi+Dy=!^=>G+-?0X~vD*T@ZN6j@oH zOV)M6;q(ag;>A+{-4WKOhLQF-P)$_m@;q!)OX7{=DfYpll3=vzixlbLi;jzd)q6HJ>obeyD#kEnf-Y&Z12)s2^uAxzNXKb&|ngnWa)vZ}A!3*L`T z_p5_Oq2g#0p{Y(TX_Gf&3IBdN2;VxkUs2C`S}EL^@NxUcFN`xU{}polmg<^?W+_u5 z>wSGADvRt~fV*PM6_T185q)N?p~HenfY%Gl(!a)gwm21LIpu_!cd_gU33Vgi&RTQU zN5O!tJq?6674le?GNyLjuNnkOgFL!CC>ugOdRUY$(*~Jd zX{c{j@aGotq%Kw5^%AyK*L7_K^V9Di^X_Sy$KT z5A@2^sG89rNEMjw+5A-T0IV{mY9tV4iBK)7& zg+J_+4>1?1G`>6r!)xd0h_p1d+eTz5DC?I6lL#=+*wZpznd!XBz&W=9|HE-f5l(Y- zZS7StT~|i^FK;mh5g|AhZ&ohNm5;dceuyG(O11v1p4_A73z1?I_avZgE44-GFDic9 zvC!h%L915@@Ik{3e$Odo=b?c@^qoR?)m48}^x0J7?URe5LS)anCcJ1nupbkhGp~e} zmT-l(%Dnx9I?VojOsv{GRgu8c*WWZyR7&A6K`OVAC8)Q@kn)2&dB12ZW zgCd*K&+KPoPlcs-TQzt;+3>==R#JD?--{WPH<;!XMW1j~fb*vE7Jmj+cI1-k;)_hL z)U;L+;NR_M8Eww}FsFPqXhoV(YvH6iqNz`pqA82J036zFO#yyQCTDKIDT9lfq?-6` z$XRktf^6NL9}|$rw{@(CI3j_9#|GwBb+lu)^BY+)xNc@lFKUG&m#1z4;14K-P@zSq zAg!p}rW#1awBRnWm44!$zBT@3jBPc9m^ZC!?zcA?_} zUXRMEvwXgN+}u7bIcJd_oZBp#h%e$&f3-6Wt-kt15|gqKXnnCVZ93FnH?ea@R3w(| z4xDN57_bxmABFZi>vP+uZo$~yMf|?5Rs4u=rs@nhuVh=?e|xQku{z-+x8;%BVUScA z{=JXovv+x|_+zN`$)#A^zbb6coOp`nP&d152-swCXDZII$2RzCc*ZkQSIx89v#t9j zQWyWVWkzl%o3g?)<(95_3&87XlJ>ja-^t%_mKQI+ql3?1SKmxWUGNs$xf#b1&l|G)f0=9@F{x==MqP7LKlUAW1T0Yp%8@2aqo%f+>#7>Lo!?_ih?>MJOp!e)Mg5|%o{T&XVS})BSu)X6t zbc=vW2{rA2t_IzG&*SP_z_aa`_N4;T`IRfRA~2~RJk>4iK&i&^0|ml;_jSAQTB)=7 z)Y<>BdaiSFhR| z&6|UQ-0xlw=>Q`6-5k+FwWQ?{qH-JThb(Xa9ssb5h2r?rP2_VpP>wNPrQvyW8XTCy zt*3lOr^3&M9{jQrDc4P}Cn>3!%;RLVdJE7#wPl5L57?=$_ww2$uV5hc<_QFMjr9mQ z<&E)dn51!QHO%gk7<_f+Rof%xIU|>1wR&cs@?v81DfREW4TMfQnXe?VHa1n}3|Y;L zt1kpOT*F@lThEteU#S(NA;)0HlW2iu_&^a~$iCdO8Rpu?mlWEkiO~WiDfgkd9z7T9 zQ|O@lIh!f>X{Ee^$h_Ti={Q5@B{g_zm(MSrM*4*88NYOS1T6p>>)G-T5d4!v1 z6qh^eg0#f}6u)?JuxCNGA?jC|L6_odJR;N|I=b_;a$^PO6ULmz7wfe?KP{3KjBj4l zK}CWylup`%L$ern8-4-7KRm*Owi(v3q5Er&VlP!U9kSyR^Ih0vATHUD?OQ5CMB_|5 z*;ktJLg0>t(@KdW{NL>Is@t`QO#N`mVl*52*PJWcOcR|DhiuZ{r&&032Q;*It622D zsLo39<&(%$(o4Gewt1&4Bnmy!?FZ+i?!(X*(^56d!mVS9?IMBAkA;>x{i7kpb&A8M z&Q?U-_C7^1YajRRp~Lc1;@L&O_zSk{h(_LOZ>HT>Y~TMrFrquQ}*=#JMcgEx85x)?}VdlA#nau)@1{L#aN{7-h&PS7zdfnzUtF0^N)h7Bh z_|Rk@T_}uvpxO#_nlGQ>Sf;29RUC4u`Ugnt_LB$FB6z6>#Y6y^a-bij<#K>yRm^NDD)Fp2(_Q94h zXPKronQ@N&Kyp2_yQHM#Tc>k{U6=NWYp;48KSjce7-zAlXlI=eVWDkMlo40MW6QJnoR5m9rc_Y21kZ4FLJ=NZ zUP^W)8PomGPBeMM%8Fo9&@^L1miz}6+?$zEktP_AomYDmzJMY*A9U+nFn;XM&#(;F zwt!?1_~;nNFO`5U`|SXt`xd~C4F}`~jnSt4P5LNl19<_>7(Cl&i#S(3LDy9r zXbCejW`oib$DS<{pK})9VfI;_jGkeqP%M`_DB!GzBZQd|lo4af(Y3*CNQco! z%=#6{jrqjn6>V^N!(~Ki!w`1=b7*;_&h#zd%bLi&@4e-{eT4NL8*{samA{vN8PO1T zu~O2Hp?!)L*yFN)M#t!GyA`ydowWMS_PBOC2_u?`u7mmgO@*_Ue(r_kk0@os;l-8w z6faXcWhb{XEa;=e@Wqhpn!Eb(3reHr?4zfSYHu8y= z4W8yOxriC7=%uT|=K^G=RnIc!)$^N(j=C#^BO2cs7!qY9x$)$SoEau<94j+hoO#f;#s~XEwo<@Nx%TeK_}P(d7%GCRKhR52w|BBnvz zmPUQemZ907pl=dm0YD=Ht&`Mobj>(R#jBXFPSPUEy20$aFs3H@ryhrE9juB4gYn}+7Mddohl=msLDZaMzZ zZ$XJqN7;h3z|?&>50wbCvjesQ!pnEGaK$XFqfc9ixO75*MAg33G(qr6J( zfZ#+I-B69mCHeYu(F+z88_`(#jtUu2CRyO4(PgN2$NUeWTnx-cqKZ9uhBK{MzXDh}lOeBG;S8 z4#NLJzgB~*72Yc7hoTSH&0WJ_cfUz%T?><#0^{8zHLZ@VRJ!L&mx+fBi$=Dhz-Rw) ze(qmUqbZb4Vytq0?+&qR|4m-{^*6Eb^1DSy%3VCwK8|S3E#zn#>NKs>(9k51pFdEc zT#xIc*oY7Q(7a5szKg%UYGlg%yole_*=IyFCfFEj7_AR#lkXAvN~k*0nEl{IpIxuW zJ8Mxd(Of7}|2eD3Bnb*(=uwRdN)Uof;Ul2xXS1t5@)O!uIBNns?&kp$>%xQMrvY_U@X!X-N2Qa_PXCf*0v9pqH~85OwLS3F2$=q zT8*AAfhB;P63+=K&Y33yeqo^MD z^P&J?#u50U_LJ~Zj6c2GN@LHqROE& z;3>S!v1^Nx>L>|`-CwAi{B?~!zL}jZ zrt?vf9m}YswItG`$5QWu8J(DfM}B+spf7#p6NmO4{v zTqM}Ei119-kHSRke|$79_{vJ+sB z%%u3Z%KDs8lu2C&AqE*vn01|L4j=kXIWWze)b8E`FmWUGuKbvneMR)2Iv!4?v@cp| zp&(6I(WcYusWl5$rzx3@Dq~t*pL6&STQo*6OgbEqw%WhTqR}SsD-tcoheitcOmMY6 zZ{DYYg;z?v)7BjvpR)3X0{}0z0hnIE?(zQM=JAEv@_i+Q!L9l5i-A>+b;Sye5y47gSI!n0UiD_jmBWPSJNOpRH%A2W!Y-R4e=yf+8 z?{4k%f2hE1&1Pl4Psvu|!zrF+O1%UA{yw?X6X%S2M%6Pu^j+Gz(@78}vS>vt~$osb@uO)dOV#-(P{#nvtWl^81q>q zK}6YWk$xby^SuH(fSf3CRp0$WL+y=Nyj22#lbm#!1f8EuMTaxrPeUqhtr7_!p`Ya7 zIDuEui<}?U2WGbHnDjpnj#w0vdNID@**U#&W*bNbcBcY83oVs5JT~WX+8*9TkU1V> zs9Zz_V0B*$^9>R}zHHm@*GiM>IeoL?Fi_D9w41;^H@yiA&h~!3K9Fp2g0&o`tdqYx zkpRo88ZP&M1hCJMW5yY!&_S7QQpZ#Awzp4{{eJML%qS>o5Pymlt&Xp2t?b0&z-n(# zn=2uo8z9c8MKOX;#8UeA^`|puCAtf9{5?_R-E#&Dxx9TdmKS z7ZnofS&QbViu>sEuQ(-Ptgr7DT7hzrS!LYI+s4C#`aEgRCS+^K%Y>bveXgRxu1?L} z1Jo4jl$v%TZ>BzVx758;roMPuTkPf|M4Y(v_l`CC@N7GrFvmruV~0xa;FR96w>=hw0^36L`phCHR%i zScT|`QyJJh^+jVn9Y$vl{E=TbQ7X>1le0ks<5@Gso>S8T{@R)4t;`Sba>;t z3PXF5%2)3Tj4Tc8EZ(p$%K}lEe@3b&>yRrHnzTqU|3#S_fr|Ui_Cv%4w$4U5b;R+J zeLHTjv$`ph0JF#Y@XK0K#OlFeVk^!WH1lf>_m`wN>Nd9Sqhhjd$xpA|e@xoLz6E?< zy983!WL`3?m3I=VeVdd%D2;rW4rWg~w+1Kok5$4fE+WkqaN9&+=k*(@T_voV zl*uD+P}z|$?N;)YeGIojH}1FQVSJsyS74-v6cf{@&6v z6;eBoJt5m=Zl1nJ3zpk*W6F8_#wz;8b)Cd~J*#IFxvLgwhH{YvYNDNPz$+9bTh#kx zK3P6c(Jeq!*{3u{aM`GCUoTYl`2b+mxW4UZaium}%Sy~$1RAu(*0}TApne7uMQivQ z=pZ7ATxt$oYJ*uDPdFA|J>RK9-*Js!ECu)f%+pTI|zn?hDFeTjORI32qJ@ z*AB*b!~N7RNi91;he)fNQT!zz8O$U+Fuz#gsyDol5qNhzR{TmU^`4dCZ}g`VxhqYq zGZ`!4-fnRpr=P$JW&M!5v(1qn%(EgUl}f{qV7sE> z*Qc^i#8@@Azn72p+aektOtzAniFEo4@BVqy@{jy0B>o>SR9(UAE6HdV0 zte?eBh*GXyWdpsLyW)ouM*SFgrR%1*WVtev9TU^R{(fjav%d`tmYudlZtU*uVc+e7 zo*f&=Z90fGY440LeJv02mQs;YC1W(%J{+$GhA}aVl-8nZt%A2028ZQn`>Ea~*5Ffa zAmve9wL0IqdHtkyf$K4+Q@hMFxL}^O-rgimaXZ>GEHK%)!2j3YcLz1q^!H#vpakKxN~Oiz31F}?(d%R`#sNdeB)?1TJE83nI&aqcms~84@{&3vV`)X zt-%2)CybyHXnydwiF%p(S%GeXtvrO9~8>lC!*4>_vU~>66xvm zqz)G|Fl4La(fkJ0N^o1lixvW>uqdk@`xAsl;I?-+owxr9x`E|{jnJ`Mw;1bn{m)oD zF+CxoCZ=6~>Q`^zMuVI!msCT_Um%OJojHqRo7&$u5m;L9JIQ~VUZ}O)DA{<^ckiQE z;lQlOK@whTDLLom7vk-@|0w8h5w6zNFgsak=x+ez_HO{q7PQ8Og0s451C2t}wrfF9 z{{0kpw6d!1$&gI$c*k?eFP#@V{P3gUfY%fDz%p7pL}sF2@z7&WEgfojbaYet&b}5|`Lnl&IvQ{ZL0rT^vDY26@ZHDfbyXa!R?Zy;i7nr9Lk zNusiz?m==BkHfW`K;HKBaxbPQ7qvJBB28c!bvK)$m6?6(dB{ZW9eScP7zQo@`5Q|K zALwRzG6|n9XE#5zu@~HA`Q`{50I_m4nf}GU`tyI%@a?FocFnM6rcqIRJ2I#M$zNVX zAQ4Ei=WIg;656l)5W^4V2mOA4HR`jnB+Y&XSEA8MU$-~%K2H-u!G^m6E_#fYvs;_! zcy9%H*UIA)Pq0bw>&X$dusU22I)BDi!2=}(yn}PuCSC!b+EJ}$ZysJ5n+n&#^!RB2 z5s1Vofn`WrSgiV<`*vb*L__jW6hV^MOXOhB|GfPeF>JT9JBuzkVW=*O`3>;=mq)21 zK1%zi5i4uFCGm`wa~;_9`uY1MDuKL|8yCY#;j1tDuCG)6!`c4#~Mp(c2Q9OPN_Xv-5 zYd*27iE+UX-_!cTIXgbspWBQ5QYxW%XX?bzV#A{rhJFmT+O+yi{b6MIxUGZgkaCs# zfa=M3(a5vJ4dvbM&(0^T5tmJ<>>%y((xF}bHmWIM`ZZb>#liW2QvLK1e;9urjEOze z(bxU`^G6#mtuvMv^;NaF;U^|v?%R||kq?V@g%rlJG&W;GI^`ZC^~3N7&1yOg4x z2k(N4jqlUEAA)!kib$DmK+nsPJ9d|*2>mnO1IO^3nc5UwT0QoohJg)9Vde-9%_Fw= zmSooXnMA`QWhV(v*hMnjdp^&9T=$j2ky^U@>@L(;}aO?Zbx@aBk&0ZTD zmx2#jNm?xK<#HmPG<)r zrSL<|+ez@y=eypXuLIV35j$yhKjK&@_*u!kd(K#9l!%0nA~u9W_-L*I!64*MHk|C5MFwg*g!klgeAL&#kwv`2|Ku)q%8ZoPw#A zU6atknng4M9Hrf4q8`H&#!zVc)wd}}hUoIK@RS+i+fVeQ z*>!BLqDDPx&8(eMw9R|D(M2hD+HAUU$pl0}>djv+93?GZ)+(zG4h~+Gqxw$!2Rs zgyrxChf!G>YwOCJ7V8bq@nO_lGUr+uq$><2zPPafcc;SwNTc>a6jXv2S~kArjbB&o z?qF&Nn;UEs11(gyUyU?0!rPErfeojiNZ^l;J9pZp7OlRTo#UE>%+KUZTczlX)(~O+ z*>jm8u$3X--+=w4YOag?%NsV1|DLt0(T?A{KW3v>`p3dt;#(hZxxE4&&oJ%9#oDJ- ztr1&l!*^iP3jC-!`7~Jd+c7X&GB%vftE}sF(=fikeBkehvK1Qt?K4P0#x?AkwUROT%+-&O+qDF*ci2W%_FBH_81r*NG)$sB%mhzHAM0+$$o6AP zD3txlryFx%SlQEL)41a8sSz)n+2aZ8NkiXs45NtjR2_w)4C02|eIV1&PMs=eaB|xi z*e<1Mn~?j9-|JtiM*nNokV%~CsrJGqv0}rZI7k&X&W1BT8~goyeVIX8z|b^}EH@)u z-vCb1+9-ar*v#Tb#gt#DbY@}|TAtq8&5ob?W3AFD&N{UQ{x&c*;NKc%>FKRdV!TwE z6iQ@{T%xZZ!jiIhgJF5^l~S}P=~}C0kKbwrpj@~X!vm@nm1AT}d#!=u%q7GDTwR0F z&73zmBi@xj!3(^XsJ?hCmnfIpxWr0M0qV^>3_;4m_gsFa#->llBRt&T}IU$UW3--jeIpL&J5XAdzUy%kPyZ|P_q@4Rn3G;GXpx}Ah_L>^AmiIVK=&$QXg(lf$mD5zKzY^7!bdlY z28feF?awzE*r^uf)w!X`=fIf(poVsaRBIbg&|fo}5){j7Y?Z0hw&Hwjq|Q7v^9%5J z7k(8-dh(L+&Y-Dzgr4gbk|@e_kf?P&V03$EE=FbGWLJH(v+K286FWS9oCeQ0uL3}F z=iQEhDT8rpu4R$u@+#)%HvQkIPDp#R*$0OQ@@dA{4dJR$13Lvb=%|jA#`dg#8lB76 zp8v?+^c}>A=9hU^TI{0jO8tr!C8}|@dtP&_yA>AjYJz0MZqT&ZHa=>}Y>cJ^U5Rdz z_Sb0QrkQyqgJ4>D?+_1YE{Ob9p!-a$amWk(r`NzzNr&ISga`>huN1<-(7fb>t9wi_ zfjQ5uDLJay0ijN)c@1dKOUD3NMm|GSl2U&JZ=#oi0mX)jI7}Ah6 z8~)RbiP8B7+~MMMDyGZfax&tSGpD$M*ud1n_?5P=71X5UX)MZnw)c9dPTD9hMfT|A z3rr**IHgy-lF12pMwNbGAqc``uzt%i0B;Nqy2+$~18@X;*scbZ4weOHYr( zYT@P~&#r551SR*|{-w_PYMABh1O-?l^s|MkL;BE2-{i*)E1+(0Up_!uf0{r4ePnHf z*+%zlhgx7Bd`HAJUu*N=k~uD{?(Sxh?1L^x19_h_T1m*de`kiPlTsE2kSw2ztufe?$l zSASYHcXP0M6K&a3@xBZR4IP3HgP%{}I@VODQ&a-Bxq)f30{uRP`8n)!yET6ORx+hYP4CdejfEPk8NTMi z$~Yv)`*ZPXzu0Wb*wkslJ)Yj!*lEQ~A+2U42JamCTitpp&9tP1L#gc2?t-Vv+0iaz zNi)#$@3)R8x2%FnN?@ix7lK_Im>=*Gc5qL!el1-W$VEiGA7#5U77{+JeW&P?s)jQ{ z>est3UMI3(23%CiGR;WSTZ04yN3(`0;W>fbH%-Ue@m7;DAK$0ScRHL00EZWH3;m16 zr;`0t^Kk2}!Tn42%Y30$%`^j~v1ytfU1P`Dde= zP9}Q!g@;Y9vWc@eIynfyTjsdshTF*GIOx}qULqsgK0yHxGP6_jpN@#f)2g7K2~ox- z@=wNu-Sbo7Z|dKglk`6C2G7)LSy(yc*I~~*ZZnu3@P6o@ElLSWHRv^xtDsg|4dM#Djjm5?U>?7rXlIdO$9X>nwLdcdDm7=QIba;i`K@ zkX0y+Qq>uU#C%@uu=axb>fvHSb@(FDav@>DbZ-0VtJmPx58ERgiUfw#r>CD3AtyO% zG2d$jV}i!3lE3eWD3-o?{`Re|(@w|wGo~eV_rqcx%augHz3JO!Ot{)y^rFCJevmR=3$1-6uY_W?jUN={A!?3X2>gEEZ=GL zSDC6p!=7$7{76c@Q-L>`zlyd0_fU*g8Ny~jyDXIPQPGK|%*u;OKA-4Z*!#Xjv6Suz!_h7&%AbWFpq zI6leoV{G6G#Z~E!2Rv;Bz%W-68SCwtjh}k!BSX^RE9RhshVkPF>I>m_4mF)x7r%N| zw#?V56#d&QJc?F8jE05=)PAcChlAYwv9BCz`L2X{o2SCwyC050KTViU$*>1H%;`|U zc<{&3zO8+v+~FTTYwCoSXYiJm<<<_jIF{rGbM`oYnrF>e?2@%XaV2@a4f81rY#M5$ zqgY%%yD~N-#epL5AhBC22KQK(3rg$*4`t+p9{6rEHn91P_&2`w*M0YWCRW$qdmz<6 zI_`a$dc1`l?GD(6HoIumD_6Y$IR@wQuO^P4?>*Fs$ABND7$|CRrqio>uIn#%|F0? zA(fqrfg9U87K^n#7UV3#p+6kfOJYqCy;#aB7LbX(TeSFjV^b)j z<_E_(Dm>51%5AlTri#5BQ7D~c?Cf}F1 zI^Q%G+V%7J*4VCPm}a}TA-2%Q$rE|&g7w?!cX9{*u)DmXQn`a2GOxlIkdYmJN_+!T zoJmfXg&i%>U4t5_d>SXeODgMKc*;CdahFpnxjMb(WTSg}uniu}tlzwZ_=z&|{sY=X$bhCN?YJ-y% z*Q36_nvNP7?i_5(bw@PasJA?ho;ZGgdh@`o*|48xNU~nlg2Bmwi^u?{`3(rdJsY18 zYO?K-0_`5qrdJ}s{h@vMbcbM4)G+rOAfA?k6d6E15vDn5CEc;<6t`%qrNlmU(4xp{ zu5#d#1*xrjf1FXWnZf@CY|O4|+gVFs*Ar@CQM8t54k@6!xemkoyOre2(3k~5>#S0S zeZ1_SE2;(UUUg&QYqMYh*rzE4^XJv_?Gahl&i+YS)WEzr#3g#3*Yx(gS_-vGDS zt1{8lOBsS6{){Z?{>i5$YFoT93@b-WOws)Y+)}j^)iZO;dR2p4vkwdY6rF<(g!R6BAB_4?2Ens6?s}jE(a3fbLs~v`ik<6+y6m$Ru<1yxubJ+t+Yh~{JfgA~=iNf+)ZQP8TOB(xE;KPL z)1})q$eYx@=|v{FdQ21GDj55%F8$=ue1Ig8TVS69DpEH`-I-3M;YZ#uP%-=#QW zDA{|)l>hU^udka5bCD&N^I>B0$dd&(<$A6@{r_bbnMe=YNcBXa7rdgJ@p5|(w+okh zGgq0s`PZJ49f21A-l6_yA8sn-HlJ4%^24b1kMx1HfC0ckq89+DHP2M+gZ&<6(>uNM zxy#{=pupuP;8PEIk~uk6;Z_PBlut+3w+@T)atAiH@fcIUklgcyzNRg|-uYnvzo1{W zcBIqIMCez*j zJrK=b`e?xySIGnopam?9#*zi9%L-x%Ri~T;B7E%H_)coe@g5P2W~;1kKHDjF427o6 zTdtx^Z>X$Mgm-Le|CHk*PcMHt7cU3JPM!W^Bc${D2u30lPrIcT z{2O}@6V?p6S+G63x-9Lg`f?J(?JdeD$=~gXW9-i?G^(*mW3SEZ#syecgpi`lv%%U5 z@&d^&Qkv^Ej9JDDghDy=Y1zFZ3pC=-O})jkd{&hb7pq_-W!eF)H!_v zL;v>tq29e>ffC%}@Ve8kZl71?8fMF_KNvKo>w#moX)Ij(l)Z&+(1anpbJ%H@(Z->7+4)??3meQ~9% zF%FM61fW?-&1?V~UJil#YJ2}|nQ?4(Z)k3&7OV_o)Umdw2M9NNbTEP?!qUcYId1|u z-+D3iCXI&vPc+`Y^A&#Vps_Wxg5?wRx@WxM@=@IYxH@pH4iU}J-;lXh%(9S#{j*Q_ zkLhppqH1L5vi-u)W4{3*R4#~yp(%)-9=;Y8lO`*XT$=o;Iv|rI^@g|s;N66vAnLq_ z&8-Jg=6WJulr3ku!9B<7{8OdZKbk(%5iW!QKQ?;AherM3AnN$!P*IXuskPk0Knso< zlGg$VEBKxd!z;@2Y6-4_TYt=07?UgM@Fh}QQK56?t68Rxz%z9-2{W_ZT}@GlE9pf1Xv*#9u9=2Xa<3_WD75;n+Sl-+6n4LUjOWn9lP$!rVbCq? z{oaZ2-bO6ra&jVN!LD^?uGOx^y20DdHi z`r;N->JXL9Zjq?v=mj+&7SVZ4eD0fCR)Wx-BD9+^#HfX5S;h0pY$y3+6`5)r&y{sOm z!EChF_}OXRDA^-WkFcD`J9;w|s}RJ(lGmLmZYHV83$J6lm{~*MFcfLoz%2iWtG|S*pxHsgl7{EUcf1_XG z>cOSB{Tl!-yF6vefTD~-U5cJmKk6co{qXkH3U1Tk*@>8)iAa?RkfmLx%9#4jRGa5S zq=`@H`Ctb%%>2XWd?fMUsBVkTWJclm{&5{0oN zp#8E*MuyTYYP__0-Sg7m{N*V=xYixT%p2F*@`k3yqmV!Jo)kL0(@>Q(DVI9X{cMZaqmB0BqQ`>k>yF@!qrwVk;q`J>KoQ|XNx z^Q@fN9EO}m^+{3suET9ebV6h~ol?<0)X~p-T66nFpE&y1fve2~JXnFX7TnzZP=+;a z{$SMK-KlCw_9QA(0~f(d0i6bBCNd!jMVPG$KT@wyD>bRw>p`fJN^wu^B@IQNz3{_U z?zAuS3k*kCj%H1eWgy}io^N=xCaPk6gwbRG;Z3j=;-4*~Mw<1wQL-n+9gcUgJ(Cg0 z9@dgh&BN%I;eQ;Xv|q`PMIK$Bv_2+qPMN>-q4`2aZ;6isr8f^d%1`POa~+N_032HCnuj?%TA>B}EEpTV=q zc9UmiHs((9Ia-Gw^=@oV-gE4r+dNnBzdM^w^yRg#_}7>Y)2m1kutod6j8!is$Hq5$ zP_C*-lHgv{O!kXJ*$O+IDwvgLgN{^HP>|Wkkm+=JQm4r(N_N?y&{^zjwrL>mEDOlp z-HW0a;*FP|#SWZ)W&WdlOneMe>+g1rOX*&yu=m$9L7nb|a8vCi7Wi`p(r5<+7%~); z5^cP(KkaxWk;Eef9VnQW{T6%OPyOVF!Z#ZZ%^4cUw<&f0h)nxvLkJ#qi2TBCw~uK{ zLKko1!rCgMv^#3Au?oK(;1{t+R61r9_R-S+G7T|qEc^rwz%V#?qnWBfQaX+@seLTt z(*-Z~feX8e&C8LeZB;<8`I;YC3X@+PPzm|wHGWX@nB>RapyYwXX2IHSBg?hVfD{+s z$(GAMu4`1eK?~K6FU-K+^&RE=jcd4R}00Y&3*5j|3^W3BE)U0%DcEVq%qWnIr>Z!@K!-c9mP;8o$!50te`p zQ=Psj{P3;%!nNm09j1G<UAJ*f~0Oty>n%`D3nhvYIy8_tZXj!Xv)P%~Vye8{zQcSarzHkR7NHhvu-aFro;B zmDkwY`^rXGx^whEM8Myl)ysS|!yy}8+plNfT9iH}9QVTt!*DO|yiMDRGLb2@0mg(uZiO1EGEf&mEEkXUM}+fz<943Pb&+N*c!*j# zN#n96qO zRxphstaj{hcqDb`^;7z;Zz>5@{ZpO_?8tx)b?*`T#3GoXePZ)f<8k>BlZ{-*j2+~| zbG_AQdryFm#EVS$U2$O+k@2(LG3k0JWVwDstV&OJB7zU*-!1~2XUuPgP&o}OpLdr_ zZ>+uroH^6Z`pH#?f0S>jIi($54O^^&ol8d%tQc(=^G0ZNAEhlr?NV}>v=gT8+Nr59 zDk5p&5{dFYKGi<2-dkQS7(+Y?>W8#>KID)1O5NzP!}3ydvxzFnkau#ppeBKmEXl`rYViNe`*p z%5wDW)inJFwB2w^*~|}V$Da*FjRqN0UXLGl>hU{%S=6FIVJ(%`0dHW zM}y)hwvzWS2`V-VzWfwOZzc)AuQPxV^I@93bXO+G^zqcPlUyu*U@KxVuFu!sFtyKy z&UL(=S+1YXb#+jC!d&`OfRl|BhYgUKxa$m~+M%j~W!F68P61Nh|0;=?$ElxPL*tD1 zte1tTLZ>OnMZYgsv)n6SAkbGLZo(@7fRO7XP+7mVi->40C2Xw7*)b*}iI(nI;73U|t zHg|SqPP2I?DGJ^Di)QucB_&@D?H4M(2^&6j_z|LDO9~Wg4VJ1153sp4YEt!R^}dYQ zeogp9W>W!u-y#Wb0a7r+xt8If9yS)mDVbB^a;~hgdFt1ARrU$k0#avY(r##>qGf1E z*WD`&3Qq4-D&^&k_qK*L$JJ3x)TUgWxG%bu?=G!ebo-k6C0l zjYFC$&az=mI5HxhDdovmPDN7?4#qZ4Lv&QW&;Qh}LcVHweMFmc!}6QM+&f9xC4l#= zBE0STj`WmoO$~!JdvV5SoeH4levrk}*GXFU9PCB8eZE$U&U#SDf34pt4@8$B2)Cq6 ze}a4R!3GbObT`;0SpkrafFw?x2wc5qT&O&~MqymymRYBe<>?#@1h*CkQa7q_@W9-5 z5R3`(@>n*DC4@SKxVh!NgDn3``U8CmS`QM7(3tTJo}~Fr72z8wS0{H6bn#WV49mRu za6})a`FOdIW3724DsfU1)R@E*BKo@WCZ?PQSft_M1?a5`0L8bCK8dq0C?-J7JCIuS zG*5Z~3w?}TNaGZ zqn>>ZW}p70;6Cr7cxv0+>mFQ6wKJ+O8NYJ3`$cqu|6zI?Mp5nyQGpCg4Aqp}<=cc% zfAoAf(yf@8D#MGB+n|zSipp4P_n#d74Or}}>-x{@gTGTnXNvlv=f&LX8`_-0mZEa& zKyzZ$=aJ{3Q-NQx@{P5OOgQ?y_UF7?*k?L#`TzYHi-Pbqa47`3RWe@~o}Cz=V2Lyw zK*Ya9vgbdi2cBu{zAeA=5r3TZ#4%;KDS8(onwXjcZT_jZ)^6 z9S4V!>ZZ9vX+jl+;*PsS#j$)aAGhRRHa{-UBp(Zv&p&EwT^TkpB&f1%Lj z759!7D5I|~(WwNS@u~EMowN`?^E1xH|FgNgVLh+V%c=!CJsJrvom7OV6rqTVYH}Suc|5pd&H1C6rU*XC_PZj5euj~A$jg%b!+j}hizjx}9->YlU z2J;MW2&{^7IA8P0;|w6Hnjw1-D+gj58lsYWO8S+#DgOIf(AfEx;vGk4S@60f!5@S"; - } - } - else { - alert("bad target for sajax_do_call: not a function or object: " + target); - } - - return; - } - - sajax_debug(func_name + " uri = " + uri + " / post = " + post_data); - x.send(post_data); - sajax_debug(func_name + " waiting.."); - delete x; - - return true; -} - -/** - * @return boolean whether the browser supports XMLHttpRequest - */ -function wfSupportsAjax() { - var request = sajax_init_object(); - var supportsAjax = request ? true : false; - delete request; - return supportsAjax; -} - diff --git a/doc/Reference_Manual_files/commonPrint.css b/doc/Reference_Manual_files/commonPrint.css deleted file mode 100644 index ecf146deef..0000000000 --- a/doc/Reference_Manual_files/commonPrint.css +++ /dev/null @@ -1,267 +0,0 @@ -/* -** MediaWiki Print style sheet for CSS2-capable browsers. -** Copyright Gabriel Wicke, http://www.aulinx.de/ -** -** Derived from the plone (http://plone.org/) styles -** Copyright Alexander Limi -*/ - -/* Thanks to A List Apart (http://alistapart.com/) for useful extras */ -a.stub, -a.new{ color:#ba0000; text-decoration:none; } - -#toc { - /*border:1px solid #2f6fab;*/ - border:1px solid #aaaaaa; - background-color:#f9f9f9; - padding:5px; -} -.tocindent { - margin-left: 2em; -} -.tocline { - margin-bottom: 0px; -} - -/* images */ -div.floatright { - float: right; - clear: right; - margin: 0; - position:relative; - border: 0.5em solid White; - border-width: 0.5em 0 0.8em 1.4em; -} -div.floatright p { font-style: italic;} -div.floatleft { - float: left; - margin: 0.3em 0.5em 0.5em 0; - position:relative; - border: 0.5em solid White; - border-width: 0.5em 1.4em 0.8em 0; -} -div.floatleft p { font-style: italic; } -/* thumbnails */ -div.thumb { - margin-bottom: 0.5em; - border-style: solid; border-color: White; - width: auto; - overflow: hidden; -} -div.thumb div { - border:1px solid #cccccc; - padding: 3px !important; - background-color:#f9f9f9; - font-size: 94%; - text-align: center; -} -div.thumb div a img { - border:1px solid #cccccc; -} -div.thumb div div.thumbcaption { - border: none; - padding: 0.3em 0 0.1em 0; -} -div.magnify { display: none; } -div.tright { - float: right; - clear: right; - border-width: 0.5em 0 0.8em 1.4em; -} -div.tleft { - float: left; - margin-right:0.5em; - border-width: 0.5em 1.4em 0.8em 0; -} -img.thumbborder { - border: 1px solid #dddddd; -} - -/* table standards */ -table.rimage { - float:right; - width:1pt; - position:relative; - margin-left:1em; - margin-bottom:1em; - text-align:center; -} - -body { - background: White; - /*font-size: 11pt !important;*/ - color: Black; - margin: 0; - padding: 0; -} - -.noprint, -div#jump-to-nav, -div.top, -div#column-one, -#colophon, -.editsection, -.toctoggle, -.tochidden, -div#f-poweredbyico, -div#f-copyrightico, -li#viewcount, -li#about, -li#disclaimer, -li#privacy { - /* Hides all the elements irrelevant for printing */ - display: none; -} - -ul { - list-style-type: square; -} - -#content { - background: none; - border: none ! important; - padding: 0 ! important; - margin: 0 ! important; -} -#footer { - background : white; - color : black; - border-top: 1px solid black; -} - -h1, h2, h3, h4, h5, h6 { - font-weight: bold; -} - -p, .documentDescription { - margin: 1em 0 ! important; - line-height: 1.2em; -} - -.tocindent p { - margin: 0 0 0 0 ! important; -} - -pre { - border: 1pt dashed black; - white-space: pre; - font-size: 8pt; - overflow: auto; - padding: 1em 0; - background : white; - color : black; -} - -table.listing, -table.listing td { - border: 1pt solid black; - border-collapse: collapse; -} - -a { - color: Black !important; - background: none !important; - padding: 0 !important; -} - -a:link, a:visited { - color: #520; - background: transparent; - text-decoration: underline; -} - -#content a.external.text:after, #content a.external.autonumber:after { - /* Expand URLs for printing */ - content: " (" attr(href) ") "; -} - -#globalWrapper { - width: 100% !important; - min-width: 0 !important; -} - -#content { - background : white; - color : black; -} - -#column-content { - margin: 0 !important; -} - -#column-content #content { - padding: 1em; - margin: 0 !important; -} -/* MSIE/Win doesn't understand 'inherit' */ -a, a.external, a.new, a.stub { - color: black ! important; - text-decoration: none ! important; -} - -/* Continue ... */ -a, a.external, a.new, a.stub { - color: inherit ! important; - text-decoration: inherit ! important; -} - -img { border: none; } -img.tex { vertical-align: middle; } -span.texhtml { font-family: serif; } - -#siteNotice { display: none; } - -table.gallery { - border: 1px solid #cccccc; - margin: 2px; - padding: 2px; - background-color:#ffffff; -} - -table.gallery tr { - vertical-align:top; -} - -div.gallerybox { - border: 1px solid #cccccc; - margin: 2px; - background-color:#f9f9f9; - width: 150px; -} - -div.gallerybox div.thumb { - text-align: center; - border: 1px solid #cccccc; - margin: 2px; -} - -div.gallerytext { - font-size: 94%; - padding: 2px 4px; -} - -/* -** Diff rendering -*/ -table.diff { background:white; } -td.diff-otitle { background:#ffffff; } -td.diff-ntitle { background:#ffffff; } -td.diff-addedline { - background:#ccffcc; - font-size: smaller; - border: solid 2px black; -} -td.diff-deletedline { - background:#ffffaa; - font-size: smaller; - border: dotted 2px black; -} -td.diff-context { - background:#eeeeee; - font-size: smaller; -} -.diffchange { - color: silver; - font-weight: bold; - text-decoration: underline; -} diff --git a/doc/Reference_Manual_files/index.css b/doc/Reference_Manual_files/index.css deleted file mode 100644 index aba5a86eaa..0000000000 --- a/doc/Reference_Manual_files/index.css +++ /dev/null @@ -1 +0,0 @@ -/* CSS placed here will affect the print output */ \ No newline at end of file diff --git a/doc/Reference_Manual_files/index.php b/doc/Reference_Manual_files/index.php deleted file mode 100644 index d4ddda0ed9..0000000000 --- a/doc/Reference_Manual_files/index.php +++ /dev/null @@ -1,8 +0,0 @@ -/* generated javascript */ -var skin = 'sourceforge'; -var stylepath = '/apps/mediawiki/mod-security/skins'; - -/* MediaWiki:Common.js */ -/* Any JavaScript here will be loaded for all users on every page load. */ - -/* MediaWiki:Sourceforge.js */ diff --git a/doc/Reference_Manual_files/index_002.css b/doc/Reference_Manual_files/index_002.css deleted file mode 100644 index f010367507..0000000000 --- a/doc/Reference_Manual_files/index_002.css +++ /dev/null @@ -1,2 +0,0 @@ -/* generated user stylesheet */ -a.new, #quickbar a.new { color: #CC2200; } diff --git a/doc/Reference_Manual_files/index_003.css b/doc/Reference_Manual_files/index_003.css deleted file mode 100644 index e034ba2a4b..0000000000 --- a/doc/Reference_Manual_files/index_003.css +++ /dev/null @@ -1 +0,0 @@ -/* CSS placed here will be applied to all skins */ \ No newline at end of file diff --git a/doc/Reference_Manual_files/index_004.css b/doc/Reference_Manual_files/index_004.css deleted file mode 100644 index c94e37366e..0000000000 --- a/doc/Reference_Manual_files/index_004.css +++ /dev/null @@ -1 +0,0 @@ -/* Empty */ \ No newline at end of file diff --git a/doc/Reference_Manual_files/poweredby_mediawiki_88x31.png b/doc/Reference_Manual_files/poweredby_mediawiki_88x31.png deleted file mode 100644 index ce1765d163e2b4b9c25a500b0c142a2d1f925cfd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1933 zcmV;82Xgp{P)es=;zzc)Ys6*W8v9&j*&Ntjg9in3D(xhhLx`D+aq3>wcnNvI)tq- zJ9u`ayXM$G^|A-&;i(5#i3trmnJ;tggNeZJp}b8mes+*2ym+XqLIe zn}vnAD@%eYd!?(o#n;lc*4EZIa+kr**WcNi<*E+w$`r`BOogMjQjxK2kf_pz4cN*U z{niJSt-i#*bMoCa-O* zw=^1Ch9)MOGHsO3&Bfl{)}FJ#^veL@(vlRWRXHs zTHCq{<<23NdJ}4$w{)DfrM}Dc-Wv#1hLMt`#n9LAyaPmvtuRi47+;Lwoe}2H4%CSf z_vlC5;Nnbwqv*#Pprn=9&Q<@(1{-vtjgq_g#u4h_63*7#=+QEiZVp?Ds0JN*_ty~a z-WlW9YR9=moRuwNj;z+sjON^h?7tG(+2+A&4Vsy@%zYI&berX;5w&s>owdgF z&5NY9^WQ1N#m-`puhpCx%*@Q*-rlCW$b6l%B4drLzssMizz|xD#&i|n&rH_64vwq7 z;IRo!dYQw(!sxvMQiG#LVU8DEi`>_)I74Ud*hfuOUG=;N_s<4)25#J|GnU z000SaNLh0L01EH`01EH{Laa2H000D#Nkl4MJv1tr^8y3o z5>G=zN+3+~?1YVlTn2`?j>PRd#Ek?t=A2`8Mg#$^4O<%CreoBBGr7!5V9EMUa$QC6 zpXnTs#P#$%^&mg=JiUK7pU?SxzTejmUu4ZH1zGdWs^ouoIXwGVYBto=)Xc6tVQ}Tq z`=^gqRz4XRUzVt!qhc!GsNDNya&q#csZXml`|!Vsc44n|hmFJG!orT`!qW80z2l{i zMSxvDl5f#XhlHCRY0dI9qUmTe7g1v9ekn4tu@c*;((6xm$RYPRHZ%bd8Cr z&dwV+uJ(GlL&K6*vsq>q2zK`{@X5bN|WXxESNP9;}X2UTwu;??b2#nTXC+KL-+!W4`+o6y8iMM z$Vn;w%k2&LI}shc`souV>K#2j{r%fM#_^K~cgVnA#zT28v(F(RvVJhI9Sd`X9ZI;p zHcyLZ6@63@nL8Bd)IDWYj1DQpAP7B*8N}H#jr^PVprYH?Uu`yQFQ4}6mC8qw2=UH`}onC1D zjG9trQqpRr+~bMkX3<89VqpLf4FGr7m9!-#r48hK#y&-UU~5Uj?JWRk5Wr?;#sxs^ z=%^O}g5U-wlgFY?@o)_d_!d7cQ~{r*Zsef8nx>_|vk3qeh7g&A4W}cl)_o2~+P#5+ z_uFqCvkoS#dmTaz0@=JOVhHFGO-V63!;AnhG8JTkrbrJMu>@^4uP_=@O-U7uN%_`@ z5Jt;s&vghF;U;)x{kDYfJAQ8qY)^Z){Zq%pM9JJM=OAoagfkf#CeI8?U8BA-QO5A7 zemIyrD<8@*>SKcd!(>FeFqJ|fR2cnGC|ZK|sNuPt_e)|@Q};Rg`lipeWm`Y` z_4971-m?fRM{H$w_P#|mWVhIDHV6$tLayf;-;~d!k}LgjQ^Qc@_lrbj5PrJ^S5#cZ zzIv(nNMCkyv*W?#q+{8Q={-Lk$FQpv6&yUH>}56=xr6H5pdnKG_ilW@@`1tw$1M}&+`SFQ~=F}Z$#v4!+0F=v&9eqvI>$db} zSoA~Hre0OGeKfaMK4c0CT^1uB)sGU`lntd+euYiS2vL^^MOm197!=(xj~=jIDUFCa z^WXvNEiK6F94VSS4A<5E+|D zav4mL@hIBai8gfZc>79X1G%z129qS*T}zU;4sEC<(I*Q(xv=nytCIgDyh`~4vFUd0 TV@{7Y00000NkvXXu0mjfc&yx* diff --git a/doc/Reference_Manual_files/wikibits.js b/doc/Reference_Manual_files/wikibits.js deleted file mode 100644 index 397dac9124..0000000000 --- a/doc/Reference_Manual_files/wikibits.js +++ /dev/null @@ -1,1000 +0,0 @@ -// MediaWiki JavaScript support functions - -var clientPC = navigator.userAgent.toLowerCase(); // Get client info -var is_gecko = /gecko/.test( clientPC ) && - !/khtml|spoofer|netscape\/7\.0/.test(clientPC); -var webkit_match = clientPC.match(/applewebkit\/(\d+)/); -if (webkit_match) { - var is_safari = clientPC.indexOf('applewebkit') != -1 && - clientPC.indexOf('spoofer') == -1; - var is_safari_win = is_safari && clientPC.indexOf('windows') != -1; - var webkit_version = parseInt(webkit_match[1]); -} -var is_khtml = navigator.vendor == 'KDE' || - ( document.childNodes && !document.all && !navigator.taintEnabled ); -// For accesskeys; note that FF3+ is included here! -var is_ff2 = /firefox\/[2-9]|minefield\/3/.test( clientPC ); -var is_ff2_ = /firefox\/2/.test( clientPC ); -// These aren't used here, but some custom scripts rely on them -var is_ff2_win = is_ff2 && clientPC.indexOf('windows') != -1; -var is_ff2_x11 = is_ff2 && clientPC.indexOf('x11') != -1; -if (clientPC.indexOf('opera') != -1) { - var is_opera = true; - var is_opera_preseven = window.opera && !document.childNodes; - var is_opera_seven = window.opera && document.childNodes; - var is_opera_95 = /opera\/(9.[5-9]|[1-9][0-9])/.test( clientPC ); -} - -// Global external objects used by this script. -/*extern ta, stylepath, skin */ - -// add any onload functions in this hook (please don't hard-code any events in the xhtml source) -var doneOnloadHook; - -if (!window.onloadFuncts) { - var onloadFuncts = []; -} - -function addOnloadHook(hookFunct) { - // Allows add-on scripts to add onload functions - if(!doneOnloadHook) { - onloadFuncts[onloadFuncts.length] = hookFunct; - } else { - hookFunct(); // bug in MSIE script loading - } -} - -function hookEvent(hookName, hookFunct) { - addHandler(window, hookName, hookFunct); -} - -function importScript(page) { - var uri = wgScript + '?title=' + - encodeURIComponent(page.replace(/ /g,'_')).replace('%2F','/').replace('%3A',':') + - '&action=raw&ctype=text/javascript'; - return importScriptURI(uri); -} - -var loadedScripts = {}; // included-scripts tracker -function importScriptURI(url) { - if (loadedScripts[url]) { - return null; - } - loadedScripts[url] = true; - var s = document.createElement('script'); - s.setAttribute('src',url); - s.setAttribute('type','text/javascript'); - document.getElementsByTagName('head')[0].appendChild(s); - return s; -} - -function importStylesheet(page) { - return importStylesheetURI(wgScript + '?action=raw&ctype=text/css&title=' + encodeURIComponent(page.replace(/ /g,'_'))); -} - -function importStylesheetURI(url) { - return document.createStyleSheet ? document.createStyleSheet(url) : appendCSS('@import "' + url + '";'); -} - -function appendCSS(text) { - var s = document.createElement('style'); - s.type = 'text/css'; - s.rel = 'stylesheet'; - if (s.styleSheet) s.styleSheet.cssText = text //IE - else s.appendChild(document.createTextNode(text + '')) //Safari sometimes borks on null - document.getElementsByTagName('head')[0].appendChild(s); - return s; -} - -// special stylesheet links -if (typeof stylepath != 'undefined' && typeof skin != 'undefined') { - if (is_opera_preseven) { - importStylesheetURI(stylepath+'/'+skin+'/Opera6Fixes.css'); - } else if (is_opera_seven && !is_opera_95) { - importStylesheetURI(stylepath+'/'+skin+'/Opera7Fixes.css'); - } else if (is_opera_95) { - importStylesheetURI(stylepath+'/'+skin+'/Opera9Fixes.css'); - } else if (is_khtml) { - importStylesheetURI(stylepath+'/'+skin+'/KHTMLFixes.css'); - } else if (is_ff2_) { - importStylesheetURI(stylepath+'/'+skin+'/FF2Fixes.css'); - } -} - -if (wgBreakFrames) { - // Un-trap us from framesets - if (window.top != window) { - window.top.location = window.location; - } -} - -function showTocToggle() { - if (document.createTextNode) { - // Uses DOM calls to avoid document.write + XHTML issues - - var linkHolder = document.getElementById('toctitle'); - if (!linkHolder) { - return; - } - - var outerSpan = document.createElement('span'); - outerSpan.className = 'toctoggle'; - - var toggleLink = document.createElement('a'); - toggleLink.id = 'togglelink'; - toggleLink.className = 'internal'; - toggleLink.href = 'javascript:toggleToc()'; - toggleLink.appendChild(document.createTextNode(tocHideText)); - - outerSpan.appendChild(document.createTextNode('[')); - outerSpan.appendChild(toggleLink); - outerSpan.appendChild(document.createTextNode(']')); - - linkHolder.appendChild(document.createTextNode(' ')); - linkHolder.appendChild(outerSpan); - - var cookiePos = document.cookie.indexOf("hidetoc="); - if (cookiePos > -1 && document.cookie.charAt(cookiePos + 8) == 1) { - toggleToc(); - } - } -} - -function changeText(el, newText) { - // Safari work around - if (el.innerText) { - el.innerText = newText; - } else if (el.firstChild && el.firstChild.nodeValue) { - el.firstChild.nodeValue = newText; - } -} - -function toggleToc() { - var toc = document.getElementById('toc').getElementsByTagName('ul')[0]; - var toggleLink = document.getElementById('togglelink'); - - if (toc && toggleLink && toc.style.display == 'none') { - changeText(toggleLink, tocHideText); - toc.style.display = 'block'; - document.cookie = "hidetoc=0"; - } else { - changeText(toggleLink, tocShowText); - toc.style.display = 'none'; - document.cookie = "hidetoc=1"; - } -} - -var mwEditButtons = []; -var mwCustomEditButtons = []; // eg to add in MediaWiki:Common.js - -function escapeQuotes(text) { - var re = new RegExp("'","g"); - text = text.replace(re,"\\'"); - re = new RegExp("\\n","g"); - text = text.replace(re,"\\n"); - return escapeQuotesHTML(text); -} - -function escapeQuotesHTML(text) { - var re = new RegExp('&',"g"); - text = text.replace(re,"&"); - re = new RegExp('"',"g"); - text = text.replace(re,"""); - re = new RegExp('<',"g"); - text = text.replace(re,"<"); - re = new RegExp('>',"g"); - text = text.replace(re,">"); - return text; -} - - -/** - * Set the accesskey prefix based on browser detection. - */ -var tooltipAccessKeyPrefix = 'alt-'; -if (is_opera) { - tooltipAccessKeyPrefix = 'shift-esc-'; -} else if (!is_safari_win && is_safari && webkit_version > 526) { - tooltipAccessKeyPrefix = 'ctrl-alt-'; -} else if (!is_safari_win && (is_safari - || clientPC.indexOf('mac') != -1 - || clientPC.indexOf('konqueror') != -1 )) { - tooltipAccessKeyPrefix = 'ctrl-'; -} else if (is_ff2) { - tooltipAccessKeyPrefix = 'alt-shift-'; -} -var tooltipAccessKeyRegexp = /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/; - -/** - * Add the appropriate prefix to the accesskey shown in the tooltip. - * If the nodeList parameter is given, only those nodes are updated; - * otherwise, all the nodes that will probably have accesskeys by - * default are updated. - * - * @param Array nodeList -- list of elements to update - */ -function updateTooltipAccessKeys( nodeList ) { - if ( !nodeList ) { - // skins without a "column-one" element don't seem to have links with accesskeys either - var columnOne = document.getElementById("column-one"); - if ( columnOne ) - updateTooltipAccessKeys( columnOne.getElementsByTagName("a") ); - // these are rare enough that no such optimization is needed - updateTooltipAccessKeys( document.getElementsByTagName("input") ); - updateTooltipAccessKeys( document.getElementsByTagName("label") ); - return; - } - - for ( var i = 0; i < nodeList.length; i++ ) { - var element = nodeList[i]; - var tip = element.getAttribute("title"); - if ( tip && tooltipAccessKeyRegexp.exec(tip) ) { - tip = tip.replace(tooltipAccessKeyRegexp, - "["+tooltipAccessKeyPrefix+"$5]"); - element.setAttribute("title", tip ); - } - } -} - -/** - * Add a link to one of the portlet menus on the page, including: - * - * p-cactions: Content actions (shown as tabs above the main content in Monobook) - * p-personal: Personal tools (shown at the top right of the page in Monobook) - * p-navigation: Navigation - * p-tb: Toolbox - * - * This function exists for the convenience of custom JS authors. All - * but the first three parameters are optional, though providing at - * least an id and a tooltip is recommended. - * - * By default the new link will be added to the end of the list. To - * add the link before a given existing item, pass the DOM node of - * that item (easily obtained with document.getElementById()) as the - * nextnode parameter; to add the link _after_ an existing item, pass - * the node's nextSibling instead. - * - * @param String portlet -- id of the target portlet ("p-cactions", "p-personal", "p-navigation" or "p-tb") - * @param String href -- link URL - * @param String text -- link text (will be automatically lowercased by CSS for p-cactions in Monobook) - * @param String id -- id of the new item, should be unique and preferably have the appropriate prefix ("ca-", "pt-", "n-" or "t-") - * @param String tooltip -- text to show when hovering over the link, without accesskey suffix - * @param String accesskey -- accesskey to activate this link (one character, try to avoid conflicts) - * @param Node nextnode -- the DOM node before which the new item should be added, should be another item in the same list - * - * @return Node -- the DOM node of the new item (an LI element) or null - */ -function addPortletLink(portlet, href, text, id, tooltip, accesskey, nextnode) { - var node = document.getElementById(portlet); - if ( !node ) return null; - node = node.getElementsByTagName( "ul" )[0]; - if ( !node ) return null; - - var link = document.createElement( "a" ); - link.appendChild( document.createTextNode( text ) ); - link.href = href; - - var item = document.createElement( "li" ); - item.appendChild( link ); - if ( id ) item.id = id; - - if ( accesskey ) { - link.setAttribute( "accesskey", accesskey ); - tooltip += " ["+accesskey+"]"; - } - if ( tooltip ) { - link.setAttribute( "title", tooltip ); - } - if ( accesskey && tooltip ) { - updateTooltipAccessKeys( new Array( link ) ); - } - - if ( nextnode && nextnode.parentNode == node ) - node.insertBefore( item, nextnode ); - else - node.appendChild( item ); // IE compatibility (?) - - return item; -} - -function getInnerText(el) { - if (typeof el == "string") return el; - if (typeof el == "undefined") { return el }; - if (el.textContent) return el.textContent; // not needed but it is faster - if (el.innerText) return el.innerText; // IE doesn't have textContent - var str = ""; - - var cs = el.childNodes; - var l = cs.length; - for (var i = 0; i < l; i++) { - switch (cs[i].nodeType) { - case 1: //ELEMENT_NODE - str += ts_getInnerText(cs[i]); - break; - case 3: //TEXT_NODE - str += cs[i].nodeValue; - break; - } - } - return str; -} - - -/** - * Set up accesskeys/tooltips from the deprecated ta array. If doId - * is specified, only set up for that id. Note that this function is - * deprecated and will not be supported indefinitely -- use - * updateTooltipAccessKey() instead. - * - * @param mixed doId string or null - */ -function akeytt( doId ) { - // A lot of user scripts (and some of the code below) break if - // ta isn't defined, so we make sure it is. Explictly using - // window.ta avoids a "ta is not defined" error. - if (!window.ta) window.ta = new Array; - - // Make a local, possibly restricted, copy to avoid clobbering - // the original. - var ta; - if ( doId ) { - ta = [doId]; - } else { - ta = window.ta; - } - - // Now deal with evil deprecated ta - var watchCheckboxExists = document.getElementById( 'wpWatchthis' ) ? true : false; - for (var id in ta) { - var n = document.getElementById(id); - if (n) { - var a = null; - var ak = ''; - // Are we putting accesskey in it - if (ta[id][0].length > 0) { - // Is this object a object? If not assume it's the next child. - - if (n.nodeName.toLowerCase() == "a") { - a = n; - } else { - a = n.childNodes[0]; - } - // Don't add an accesskey for the watch tab if the watch - // checkbox is also available. - if (a && ((id != 'ca-watch' && id != 'ca-unwatch') || !watchCheckboxExists)) { - a.accessKey = ta[id][0]; - ak = ' ['+tooltipAccessKeyPrefix+ta[id][0]+']'; - } - } else { - // We don't care what type the object is when assigning tooltip - a = n; - ak = ''; - } - - if (a) { - a.title = ta[id][1]+ak; - } - } - } -} - -var checkboxes; -var lastCheckbox; - -function setupCheckboxShiftClick() { - checkboxes = []; - lastCheckbox = null; - var inputs = document.getElementsByTagName('input'); - addCheckboxClickHandlers(inputs); -} - -function addCheckboxClickHandlers(inputs, start) { - if ( !start) start = 0; - - var finish = start + 250; - if ( finish > inputs.length ) - finish = inputs.length; - - for ( var i = start; i < finish; i++ ) { - var cb = inputs[i]; - if ( !cb.type || cb.type.toLowerCase() != 'checkbox' ) - continue; - var end = checkboxes.length; - checkboxes[end] = cb; - cb.index = end; - cb.onclick = checkboxClickHandler; - } - - if ( finish < inputs.length ) { - setTimeout( function () { - addCheckboxClickHandlers(inputs, finish); - }, 200 ); - } -} - -function checkboxClickHandler(e) { - if (typeof e == 'undefined') { - e = window.event; - } - if ( !e.shiftKey || lastCheckbox === null ) { - lastCheckbox = this.index; - return true; - } - var endState = this.checked; - var start, finish; - if ( this.index < lastCheckbox ) { - start = this.index + 1; - finish = lastCheckbox; - } else { - start = lastCheckbox; - finish = this.index - 1; - } - for (var i = start; i <= finish; ++i ) { - checkboxes[i].checked = endState; - } - lastCheckbox = this.index; - return true; -} - -function toggle_element_activation(ida,idb) { - if (!document.getElementById) { - return; - } - document.getElementById(ida).disabled=true; - document.getElementById(idb).disabled=false; -} - -function toggle_element_check(ida,idb) { - if (!document.getElementById) { - return; - } - document.getElementById(ida).checked=true; - document.getElementById(idb).checked=false; -} - -/* - Written by Jonathan Snook, http://www.snook.ca/jonathan - Add-ons by Robert Nyman, http://www.robertnyman.com - Author says "The credit comment is all it takes, no license. Go crazy with it!:-)" - From http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/ -*/ -function getElementsByClassName(oElm, strTagName, oClassNames){ - var arrReturnElements = new Array(); - if ( typeof( oElm.getElementsByClassName ) == "function" ) { - /* Use a native implementation where possible FF3, Saf3.2, Opera 9.5 */ - var arrNativeReturn = oElm.getElementsByClassName( oClassNames ); - if ( strTagName == "*" ) - return arrNativeReturn; - for ( var h=0; h < arrNativeReturn.length; h++ ) { - if( arrNativeReturn[h].tagName.toLowerCase() == strTagName.toLowerCase() ) - arrReturnElements[arrReturnElements.length] = arrNativeReturn[h]; - } - return arrReturnElements; - } - var arrElements = (strTagName == "*" && oElm.all)? oElm.all : oElm.getElementsByTagName(strTagName); - var arrRegExpClassNames = new Array(); - if(typeof oClassNames == "object"){ - for(var i=0; i 0) { - if (table.tHead && table.tHead.rows.length > 0) { - firstRow = table.tHead.rows[table.tHead.rows.length-1]; - } else { - firstRow = table.rows[0]; - } - } - if (!firstRow) return; - - // We have a first row: assume it's the header, and make its contents clickable links - for (var i = 0; i < firstRow.cells.length; i++) { - var cell = firstRow.cells[i]; - if ((" "+cell.className+" ").indexOf(" unsortable ") == -1) { - cell.innerHTML += '  ' - + '' - + '' - + '↓'; - } - } - if (ts_alternate_row_colors) { - ts_alternate(table); - } -} - -function ts_getInnerText(el) { - return getInnerText( el ); -} - -function ts_resortTable(lnk) { - // get the span - var span = lnk.getElementsByTagName('span')[0]; - - var td = lnk.parentNode; - var tr = td.parentNode; - var column = td.cellIndex; - - var table = tr.parentNode; - while (table && !(table.tagName && table.tagName.toLowerCase() == 'table')) - table = table.parentNode; - if (!table) return; - - if (table.rows.length <= 1) return; - - // Generate the number transform table if it's not done already - if (ts_number_transform_table == null) { - ts_initTransformTable(); - } - - // Work out a type for the column - // Skip the first row if that's where the headings are - var rowStart = (table.tHead && table.tHead.rows.length > 0 ? 0 : 1); - - var itm = ""; - for (var i = rowStart; i < table.rows.length; i++) { - if (table.rows[i].cells.length > column) { - itm = ts_getInnerText(table.rows[i].cells[column]); - itm = itm.replace(/^[\s\xa0]+/, "").replace(/[\s\xa0]+$/, ""); - if (itm != "") break; - } - } - - // TODO: bug 8226, localised date formats - var sortfn = ts_sort_generic; - var preprocessor = ts_toLowerCase; - if (/^\d\d[\/. -][a-zA-Z]{3}[\/. -]\d\d\d\d$/.test(itm)) { - preprocessor = ts_dateToSortKey; - } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d\d\d$/.test(itm)) { - preprocessor = ts_dateToSortKey; - } else if (/^\d\d[\/.-]\d\d[\/.-]\d\d$/.test(itm)) { - preprocessor = ts_dateToSortKey; - // pound dollar euro yen currency cents - } else if (/(^[\u00a3$\u20ac\u00a4\u00a5]|\u00a2$)/.test(itm)) { - preprocessor = ts_currencyToSortKey; - } else if (ts_number_regex.test(itm)) { - preprocessor = ts_parseFloat; - } - - var reverse = (span.getAttribute("sortdir") == 'down'); - - var newRows = new Array(); - var staticRows = new Array(); - for (var j = rowStart; j < table.rows.length; j++) { - var row = table.rows[j]; - if((" "+row.className+" ").indexOf(" unsortable ") < 0) { - var keyText = ts_getInnerText(row.cells[column]); - var oldIndex = (reverse ? -j : j); - var preprocessed = preprocessor( keyText ); - - newRows[newRows.length] = new Array(row, preprocessed, oldIndex); - } else staticRows[staticRows.length] = new Array(row, false, j-rowStart); - } - - newRows.sort(sortfn); - - var arrowHTML; - if (reverse) { - arrowHTML = '↓'; - newRows.reverse(); - span.setAttribute('sortdir','up'); - } else { - arrowHTML = '↑'; - span.setAttribute('sortdir','down'); - } - - for (var i = 0; i < staticRows.length; i++) { - var row = staticRows[i]; - newRows.splice(row[2], 0, row); - } - - // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones - // don't do sortbottom rows - for (var i = 0; i < newRows.length; i++) { - if ((" "+newRows[i][0].className+" ").indexOf(" sortbottom ") == -1) - table.tBodies[0].appendChild(newRows[i][0]); - } - // do sortbottom rows only - for (var i = 0; i < newRows.length; i++) { - if ((" "+newRows[i][0].className+" ").indexOf(" sortbottom ") != -1) - table.tBodies[0].appendChild(newRows[i][0]); - } - - // Delete any other arrows there may be showing - var spans = getElementsByClassName(tr, "span", "sortarrow"); - for (var i = 0; i < spans.length; i++) { - spans[i].innerHTML = '↓'; - } - span.innerHTML = arrowHTML; - - if (ts_alternate_row_colors) { - ts_alternate(table); - } -} - -function ts_initTransformTable() { - if ( typeof wgSeparatorTransformTable == "undefined" - || ( wgSeparatorTransformTable[0] == '' && wgDigitTransformTable[2] == '' ) ) - { - digitClass = "[0-9,.]"; - ts_number_transform_table = false; - } else { - ts_number_transform_table = {}; - // Unpack the transform table - // Separators - ascii = wgSeparatorTransformTable[0].split("\t"); - localised = wgSeparatorTransformTable[1].split("\t"); - for ( var i = 0; i < ascii.length; i++ ) { - ts_number_transform_table[localised[i]] = ascii[i]; - } - // Digits - ascii = wgDigitTransformTable[0].split("\t"); - localised = wgDigitTransformTable[1].split("\t"); - for ( var i = 0; i < ascii.length; i++ ) { - ts_number_transform_table[localised[i]] = ascii[i]; - } - - // Construct regex for number identification - digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '\\.']; - maxDigitLength = 1; - for ( var digit in ts_number_transform_table ) { - // Escape regex metacharacters - digits.push( - digit.replace( /[\\\\$\*\+\?\.\(\)\|\{\}\[\]\-]/, - function( s ) { return '\\' + s; } ) - ); - if (digit.length > maxDigitLength) { - maxDigitLength = digit.length; - } - } - if ( maxDigitLength > 1 ) { - digitClass = '[' + digits.join( '', digits ) + ']'; - } else { - digitClass = '(' + digits.join( '|', digits ) + ')'; - } - } - - // We allow a trailing percent sign, which we just strip. This works fine - // if percents and regular numbers aren't being mixed. - ts_number_regex = new RegExp( - "^(" + - "[+-]?[0-9][0-9,]*(\\.[0-9,]*)?(E[+-]?[0-9][0-9,]*)?" + // Fortran-style scientific - "|" + - "[+-]?" + digitClass + "+%?" + // Generic localised - ")$", "i" - ); -} - -function ts_toLowerCase( s ) { - return s.toLowerCase(); -} - -function ts_dateToSortKey(date) { - // y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX - if (date.length == 11) { - switch (date.substr(3,3).toLowerCase()) { - case "jan": var month = "01"; break; - case "feb": var month = "02"; break; - case "mar": var month = "03"; break; - case "apr": var month = "04"; break; - case "may": var month = "05"; break; - case "jun": var month = "06"; break; - case "jul": var month = "07"; break; - case "aug": var month = "08"; break; - case "sep": var month = "09"; break; - case "oct": var month = "10"; break; - case "nov": var month = "11"; break; - case "dec": var month = "12"; break; - // default: var month = "00"; - } - return date.substr(7,4)+month+date.substr(0,2); - } else if (date.length == 10) { - if (ts_europeandate == false) { - return date.substr(6,4)+date.substr(0,2)+date.substr(3,2); - } else { - return date.substr(6,4)+date.substr(3,2)+date.substr(0,2); - } - } else if (date.length == 8) { - yr = date.substr(6,2); - if (parseInt(yr) < 50) { - yr = '20'+yr; - } else { - yr = '19'+yr; - } - if (ts_europeandate == true) { - return yr+date.substr(3,2)+date.substr(0,2); - } else { - return yr+date.substr(0,2)+date.substr(3,2); - } - } - return "00000000"; -} - -function ts_parseFloat( s ) { - if ( !s ) { - return 0; - } - if (ts_number_transform_table != false) { - var newNum = '', c; - - for ( var p = 0; p < s.length; p++ ) { - c = s.charAt( p ); - if (c in ts_number_transform_table) { - newNum += ts_number_transform_table[c]; - } else { - newNum += c; - } - } - s = newNum; - } - - num = parseFloat(s.replace(/,/g, "")); - return (isNaN(num) ? 0 : num); -} - -function ts_currencyToSortKey( s ) { - return ts_parseFloat(s.replace(/[^0-9.,]/g,'')); -} - -function ts_sort_generic(a, b) { - return a[1] < b[1] ? -1 : a[1] > b[1] ? 1 : a[2] - b[2]; -} - -function ts_alternate(table) { - // Take object table and get all it's tbodies. - var tableBodies = table.getElementsByTagName("tbody"); - // Loop through these tbodies - for (var i = 0; i < tableBodies.length; i++) { - // Take the tbody, and get all it's rows - var tableRows = tableBodies[i].getElementsByTagName("tr"); - // Loop through these rows - // Start at 1 because we want to leave the heading row untouched - for (var j = 0; j < tableRows.length; j++) { - // Check if j is even, and apply classes for both possible results - var oldClasses = tableRows[j].className.split(" "); - var newClassName = ""; - for (var k = 0; k < oldClasses.length; k++) { - if (oldClasses[k] != "" && oldClasses[k] != "even" && oldClasses[k] != "odd") - newClassName += oldClasses[k] + " "; - } - tableRows[j].className = newClassName + (j % 2 == 0 ? "even" : "odd"); - } - } -} - -/* - * End of table sorting code - */ - - -/** - * Add a cute little box at the top of the screen to inform the user of - * something, replacing any preexisting message. - * - * @param String -or- Dom Object message HTML to be put inside the right div - * @param String className Used in adding a class; should be different for each - * call to allow CSS/JS to hide different boxes. null = no class used. - * @return Boolean True on success, false on failure - */ -function jsMsg( message, className ) { - if ( !document.getElementById ) { - return false; - } - // We special-case skin structures provided by the software. Skins that - // choose to abandon or significantly modify our formatting can just define - // an mw-js-message div to start with. - var messageDiv = document.getElementById( 'mw-js-message' ); - if ( !messageDiv ) { - messageDiv = document.createElement( 'div' ); - if ( document.getElementById( 'column-content' ) - && document.getElementById( 'content' ) ) { - // MonoBook, presumably - document.getElementById( 'content' ).insertBefore( - messageDiv, - document.getElementById( 'content' ).firstChild - ); - } else if ( document.getElementById('content') - && document.getElementById( 'article' ) ) { - // Non-Monobook but still recognizable (old-style) - document.getElementById( 'article').insertBefore( - messageDiv, - document.getElementById( 'article' ).firstChild - ); - } else { - return false; - } - } - - messageDiv.setAttribute( 'id', 'mw-js-message' ); - messageDiv.style.display = 'block'; - if( className ) { - messageDiv.setAttribute( 'class', 'mw-js-message-'+className ); - } - - if (typeof message === 'object') { - while (messageDiv.hasChildNodes()) // Remove old content - messageDiv.removeChild(messageDiv.firstChild); - messageDiv.appendChild (message); // Append new content - } - else { - messageDiv.innerHTML = message; - } - return true; -} - -/** - * Inject a cute little progress spinner after the specified element - * - * @param element Element to inject after - * @param id Identifier string (for use with removeSpinner(), below) - */ -function injectSpinner( element, id ) { - var spinner = document.createElement( "img" ); - spinner.id = "mw-spinner-" + id; - spinner.src = stylepath + "/common/images/spinner.gif"; - spinner.alt = spinner.title = "..."; - if( element.nextSibling ) { - element.parentNode.insertBefore( spinner, element.nextSibling ); - } else { - element.parentNode.appendChild( spinner ); - } -} - -/** - * Remove a progress spinner added with injectSpinner() - * - * @param id Identifier string - */ -function removeSpinner( id ) { - var spinner = document.getElementById( "mw-spinner-" + id ); - if( spinner ) { - spinner.parentNode.removeChild( spinner ); - } -} - -function runOnloadHook() { - // don't run anything below this for non-dom browsers - if (doneOnloadHook || !(document.getElementById && document.getElementsByTagName)) { - return; - } - - // set this before running any hooks, since any errors below - // might cause the function to terminate prematurely - doneOnloadHook = true; - - updateTooltipAccessKeys( null ); - akeytt( null ); - setupCheckboxShiftClick(); - sortables_init(); - - // Run any added-on functions - for (var i = 0; i < onloadFuncts.length; i++) { - onloadFuncts[i](); - } -} - -/** - * Add an event handler to an element - * - * @param Element element Element to add handler to - * @param String attach Event to attach to - * @param callable handler Event handler callback - */ -function addHandler( element, attach, handler ) { - if( window.addEventListener ) { - element.addEventListener( attach, handler, false ); - } else if( window.attachEvent ) { - element.attachEvent( 'on' + attach, handler ); - } -} - -/** - * Add a click event handler to an element - * - * @param Element element Element to add handler to - * @param callable handler Event handler callback - */ -function addClickHandler( element, handler ) { - addHandler( element, 'click', handler ); -} - -/** - * Removes an event handler from an element - * - * @param Element element Element to remove handler from - * @param String remove Event to remove - * @param callable handler Event handler callback to remove - */ -function removeHandler( element, remove, handler ) { - if( window.removeEventListener ) { - element.removeEventListener( remove, handler, false ); - } else if( window.detachEvent ) { - element.detachEvent( 'on' + remove, handler ); - } -} -//note: all skins should call runOnloadHook() at the end of html output, -// so the below should be redundant. It's there just in case. -hookEvent("load", runOnloadHook); From f22bca10f8ab9635bee4eb9fdbee9efdf5a42c4c Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Tue, 30 Oct 2012 21:15:10 -0400 Subject: [PATCH 88/99] Fix error_message type --- tests/msc_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/msc_test.c b/tests/msc_test.c index 6200d78db7..f6637f506a 100644 --- a/tests/msc_test.c +++ b/tests/msc_test.c @@ -79,7 +79,7 @@ unsigned long int DSOLOCAL msc_pcre_match_limit = 0; unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0; /* Stubs */ -char *format_error_log_message(apr_pool_t *mp, error_message *em) { +char *format_error_log_message(apr_pool_t *mp, error_message_t *em) { return "FAKE ERROR LOG MESSAGE"; } From 408f37015ec19998edc795b5062ab871cf23ad8b Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Wed, 31 Oct 2012 17:07:42 -0400 Subject: [PATCH 89/99] Fixed byte conversion issue during logging under zlinux --- CHANGES | 2 ++ apache2/msc_logging.c | 4 ++++ apache2/msc_util.c | 9 +++++++++ apache2/msc_util.h | 5 +++++ configure.ac | 8 +++++++- 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 459a57079b..b293d1067e 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,8 @@ * Added a better random bytes generator using apr_generate_random_bytes() to create the HMAC key. + * Fixed byte conversion issue during logging under Linux s390x platform. + * Fixed compilation bug with LibXML2 2.9.0 (Thanks Athmane Madjoudj). * Fixed parsing error with modsecurity-recommended.conf and Apache 2.4. diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index 8b560d4b08..b2f2e445a2 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -229,7 +229,11 @@ static char *construct_auditlog_filename(apr_pool_t *mp, const char *uniqueid) { * as an audit log boundary. */ static char *create_auditlog_boundary(request_rec *r) { +#ifdef LINUX_S390 + int data = swap_int32(rand()); +#else unsigned long data = rand(); +#endif /* Do note that I tried using apr_generate_random_bytes but it turned * out to be terribly slow for some reason. Needs further investigation. */ diff --git a/apache2/msc_util.c b/apache2/msc_util.c index bb2d3457e0..8f9d3d0e4d 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -74,6 +74,15 @@ static unsigned char *c2x(unsigned what, unsigned char *where); static unsigned char x2c(unsigned char *what); static unsigned char xsingle2c(unsigned char *what); +#ifdef LINUX_S390 +int swap_int32(int x) { + int swap = ((x>>24)&0xff) | ((x<<8)&0xff0000) | + ((x>>8)&0xff00) | ((x<<24)&0xff000000); + return swap; +} +#endif + + /** \brief Decode utf-8 to unicode format. * * \param mp Pointer to memory pool diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 76227df881..06fa26c5af 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -46,6 +46,11 @@ int DSOLOCAL inet_pton(int family, const char *src, void *dst); #define UNICODE_ERROR_RESTRICTED_CHARACTER -4 #define UNICODE_ERROR_DECODING_ERROR -5 +#ifdef LINUX_S390 +int DSOLOCAL swap_int32(int x); +#endif + + char DSOLOCAL *utf8_unicode_inplace_ex(apr_pool_t *mp, unsigned char *input, long int input_len, int *changed); char DSOLOCAL *m_strcasestr(const char *haystack, const char *needle); diff --git a/configure.ac b/configure.ac index 703a57e959..05283b9326 100644 --- a/configure.ac +++ b/configure.ac @@ -100,6 +100,11 @@ case $host in *-*-linux*) echo "Checking plataform... Identified as Linux" linuxos=true + case "${host_cpu}" in + s390x) + cpu_type="-DLINUX_S390" + ;; + esac ;; *-*-solaris*) echo "Checking plataform... Identified as Solaris" @@ -135,6 +140,7 @@ AM_CONDITIONAL([AIX], [test x$aixos = xtrue]) AM_CONDITIONAL([HPUX], [test x$hpuxos = xtrue]) AM_CONDITIONAL([MACOSX], [test x$macos = xtrue]) AM_CONDITIONAL([LINUX], [test x$linuxos = xtrue]) +AM_CONDITIONAL([LINUX390], [test x$linuxos390 = xtrue]) AM_CONDITIONAL([SOLARIS], [test x$solarisos = xtrue]) AM_CONDITIONAL([FREEBSD], [test x$freebsdos = xtrue]) AM_CONDITIONAL([OPENBSD], [test x$openbsdos = xtrue]) @@ -627,7 +633,7 @@ else fi fi -MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $pcre_jit $request_early $lua_cache $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api" +MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $pcre_jit $request_early $lua_cache $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api $cpu_type" APXS_WRAPPER=build/apxs-wrapper APXS_EXTRA_CFLAGS="" From 267bfe6ca0828e8347da5760d8f20d0359632bd6 Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Mon, 5 Nov 2012 22:51:20 -0400 Subject: [PATCH 90/99] Minor changes to IIS module --- iis/mymodule.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index 33ce7173a2..2a66d6881f 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -1,4 +1,4 @@ -/* +/* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ * Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) * @@ -796,8 +796,8 @@ CMyHttpModule::OnBeginRequest( HTTP_REQUEST *req = pRequest->GetRawHttpRequest(); - r->hostname = ConvertUTF16ToUTF8(req->CookedUrl.pHost, req->CookedUrl.HostLength, r->pool); - r->path_info = ConvertUTF16ToUTF8(req->CookedUrl.pAbsPath, req->CookedUrl.AbsPathLength, r->pool); + r->hostname = ConvertUTF16ToUTF8(req->CookedUrl.pHost, req->CookedUrl.HostLength / sizeof(WCHAR), r->pool); + r->path_info = ConvertUTF16ToUTF8(req->CookedUrl.pAbsPath, req->CookedUrl.AbsPathLength / sizeof(WCHAR), r->pool); if(r->hostname == NULL) { @@ -825,8 +825,8 @@ CMyHttpModule::OnBeginRequest( } } - if(req->CookedUrl.pQueryString != NULL) - r->args = ConvertUTF16ToUTF8(req->CookedUrl.pQueryString + 1, req->CookedUrl.QueryStringLength - 1, r->pool); + if(req->CookedUrl.pQueryString != NULL && req->CookedUrl.QueryStringLength > 0) + r->args = ConvertUTF16ToUTF8(req->CookedUrl.pQueryString + 1, (req->CookedUrl.QueryStringLength / sizeof(WCHAR)) - 1, r->pool); #define _TRANSHEADER(id,str) if(req->Headers.KnownHeaders[id].pRawValue != NULL) \ {\ From b353bd60a96a5a15f25962f317cdc9c1ea452127 Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Tue, 6 Nov 2012 01:23:00 -0400 Subject: [PATCH 91/99] Fixed build on Apache 2.4. --- apache2/msc_crypt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index d1d810fa73..5699c83248 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -16,6 +16,7 @@ #include "msc_util.h" #include "apr_sha1.h" #include "apr_uri.h" +#include "apr_base64.h" #include "acmp.h" #include "libxml/HTMLtree.h" #include "libxml/uri.h" From d1c002d6ccb8e3531d05118b3d27743b2bc015ec Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Fri, 9 Nov 2012 15:03:43 -0400 Subject: [PATCH 92/99] handle invalid escape sequence passed to strmatch operator --- apache2/re_operators.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apache2/re_operators.c b/apache2/re_operators.c index e6179135cc..7884ee4335 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -2394,6 +2394,7 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * static int msre_op_strmatch_param_init(msre_rule *rule, char **error_msg) { const apr_strmatch_pattern *compiled_pattern; + char *processed = NULL; const char *pattern = rule->op_param; unsigned short int op_len; @@ -2402,8 +2403,14 @@ static int msre_op_strmatch_param_init(msre_rule *rule, char **error_msg) { op_len = strlen(pattern); + /* Process pattern */ + processed = parse_pm_content(pattern, op_len, rule, error_msg); + if (processed == NULL) { + return 0; + } + /* Compile pattern */ - compiled_pattern = apr_strmatch_precompile(rule->ruleset->mp, parse_pm_content(pattern, op_len, rule, error_msg), 1); + compiled_pattern = apr_strmatch_precompile(rule->ruleset->mp, processed, 1); if (compiled_pattern == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern: %s", pattern); return 0; From 179b536611f3d69beab4fafc3e70c8faacc6f710 Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Fri, 9 Nov 2012 15:07:37 -0400 Subject: [PATCH 93/99] =?UTF-8?q?Fixed=20crash=20with=20invalid=20commands?= =?UTF-8?q?=20and=20marked=20IIS=20server=20as=20non-virtua=E2=80=A6=20?= =?UTF-8?q?=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iis/mymodule.cpp | 8 ++++---- standalone/api.c | 1 + standalone/config.c | 21 +++++++++------------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index 2a66d6881f..b02e6f6071 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -752,8 +752,8 @@ CMyHttpModule::OnBeginRequest( pConfig->m_dwLastCheck = ctime; - if(ret == 0 || pConfig->m_LastChange.dwLowDateTime != fdata.ftLastWriteTime.dwLowDateTime || - pConfig->m_LastChange.dwHighDateTime != fdata.ftLastWriteTime.dwHighDateTime) + if(pConfig->m_Config == NULL || (ret != 0 && (pConfig->m_LastChange.dwLowDateTime != fdata.ftLastWriteTime.dwLowDateTime || + pConfig->m_LastChange.dwHighDateTime != fdata.ftLastWriteTime.dwHighDateTime))) { pConfig->m_LastChange.dwLowDateTime = fdata.ftLastWriteTime.dwLowDateTime; pConfig->m_LastChange.dwHighDateTime = fdata.ftLastWriteTime.dwHighDateTime; @@ -769,9 +769,9 @@ CMyHttpModule::OnBeginRequest( WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE); } } - - delete path; } + + delete path; } conn_rec *c; diff --git a/standalone/api.c b/standalone/api.c index 7eb65be2a3..5a08d8e333 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -143,6 +143,7 @@ server_rec *modsecInit() { server->server_scheme = ""; server->timeout = 60 * 1000000;// 60 seconds server->wild_names = NULL; + server->is_virtual = 0; ap_server_config_defines = apr_array_make(pool, 1, sizeof(char *)); diff --git a/standalone/config.c b/standalone/config.c index d31ac50c7e..615f0a9eaf 100644 --- a/standalone/config.c +++ b/standalone/config.c @@ -991,8 +991,8 @@ const char *process_command_config(server_rec *s, apr_status_t status; ap_directive_t *newdir; int optional; + char *err = NULL; - //*(char **)apr_array_push(ari) = (char *)filename; errmsg = populate_include_files(p, ptemp, ari, filename, 0); if(errmsg != NULL) @@ -1108,21 +1108,18 @@ const char *process_command_config(server_rec *s, break; } - while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) - { - ap_cfg_closefile(parms->config_file); - } - if (errmsg) { - char *err = (char *)apr_palloc(p, 1024); + err = (char *)apr_palloc(p, 1024); - apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, - parms->config_file->line_number, errmsg); - - return err; + if(parms != NULL) + apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, + parms->config_file->line_number, errmsg); + else + apr_snprintf(err, 1024, "Syntax error in config file: %s", errmsg); } - return NULL; + errmsg = err; + Exit: while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) { From 19d71f794ceb368f5a98ede20ea85b98cdf6fe6e Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Fri, 9 Nov 2012 16:42:02 -0400 Subject: [PATCH 94/99] Update CHANGES --- CHANGES | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b293d1067e..2964afb3b9 100644 --- a/CHANGES +++ b/CHANGES @@ -23,7 +23,11 @@ * Fixed DROP action was disabled for Apache 2 module by mistake. - * Fixed bug when use ctl:ruleRemoveByTag. + * Fixed bug when use ctl:ruleRemoveTargetByTag. + + * Fixed many IIS port bugs. + + * Fixed bug when @strmatch patterns use invalid escape sequence (Thanks Hideaki Hayashi). * The doc/ directory now contains the instructions to access online documentation. From 00b22f0c59e9a494d36c69e723bd2f1a10129dba Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Fri, 9 Nov 2012 18:36:12 -0400 Subject: [PATCH 95/99] Fixed NGINX compilation issues --- CHANGES | 2 +- nginx/modsecurity/ngx_http_modsecurity.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 2964afb3b9..8b2c1687f5 100644 --- a/CHANGES +++ b/CHANGES @@ -25,7 +25,7 @@ * Fixed bug when use ctl:ruleRemoveTargetByTag. - * Fixed many IIS port bugs. + * Fixed IIS and NGINX modules bugs. * Fixed bug when @strmatch patterns use invalid escape sequence (Thanks Hideaki Hayashi). diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 5b4cbda949..683727b497 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -488,6 +488,9 @@ ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r) if (rb->buf->last == rb->buf->end) { rc = ngx_http_process_request_body(r, rb->to_write); + if(rc != NGX_OK) { + return rc; + } rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; rb->buf->last = rb->buf->start; @@ -555,7 +558,10 @@ ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r) ngx_del_timer(c->read); } - ngx_http_process_request_body(r, rb->to_write); + rc = ngx_http_process_request_body(r, rb->to_write); + if(rc != NGX_OK) { + return rc; + } return ngx_http_upload_body_handler(r); } @@ -779,7 +785,7 @@ modsecurity_read_body_cb(request_rec *r, char *buf, unsigned int length, if (!ctx->body_pos) { ctx->body_pos = b->start; } - if ((b->end - ctx->body_pos) > length) { + if ((unsigned int)(b->end - ctx->body_pos) > length) { ngx_memcpy(buf, (char *) ctx->body_pos, length); ctx->processed += length; ctx->body_pos += length; From 91e5f85cc10575e4185ffb42e39c7459bc86c0d4 Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Tue, 13 Nov 2012 15:26:38 -0400 Subject: [PATCH 96/99] fix for issues in ssn_verify such as infinite loop. --- CHANGES | 2 ++ apache2/re_operators.c | 27 +++++++++++---------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 8b2c1687f5..bab7fd20bc 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,8 @@ * Fixed bug when @strmatch patterns use invalid escape sequence (Thanks Hideaki Hayashi). + * Fixed bugs in @verifySSN (Thanks Hideaki Hayashi). + * The doc/ directory now contains the instructions to access online documentation. 15 Oct 2012 - 2.7.0 diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 7884ee4335..622958266d 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -3170,15 +3170,15 @@ static int ssn_verify(modsec_rec *msr, const char *ssnumber, int len) { int area, serial, grp; int sequencial = 0; int repetitions = 0; - int progression = 0; char *str_area; char *str_grp; char *str_serial; for (i = 0; i < len; i++) { if (apr_isdigit(ssnumber[i])) { - num[i] = convert_to_int(ssnumber[i]); - digits++; + if (digits < 9) + num[digits] = convert_to_int(ssnumber[i]); + digits++; } } @@ -3186,24 +3186,19 @@ static int ssn_verify(modsec_rec *msr, const char *ssnumber, int len) { if (digits != 9) goto invalid; - digits = 0; - - for (i=0; i < len-1; i++) { - progression = (num[i] - (num[i+1]-1)); - repetitions = (num[i] - num[i+1]); + for (i=0; i < 8; i++) { + if (num[i] == (num[i+1]-1)) + sequencial++; - if (repetitions != 0 ) - sequencial = 1; - - if (progression == 0) - digits++; + if (num[i] == num[i+1]) + repetitions++; } - /* We are blocking when all numbers were repeated */ - if (sequencial == 0) + /* We are blocking when all numbers were sequencial or repeated */ + if (sequencial == 8) goto invalid; - if (digits == 8) + if (repetitions == 8) goto invalid; str_area = apr_psprintf(msr->mp,"%d%d%d",num[0],num[1],num[2]); From bfdb28e2ab04b1680f9db0f901751d8a129b3cad Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Tue, 13 Nov 2012 15:54:23 -0400 Subject: [PATCH 97/99] Fix for broken logging of IP client address in some cases. --- iis/ModSecurityIIS.sln | 4 ++-- iis/ModSecurityIIS.vcxproj | 4 ++-- iis/mymodule.cpp | 5 +++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/iis/ModSecurityIIS.sln b/iis/ModSecurityIIS.sln index 0a45f76bd3..1b272dd114 100644 --- a/iis/ModSecurityIIS.sln +++ b/iis/ModSecurityIIS.sln @@ -15,8 +15,8 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 - {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|Mixed Platforms.Build.0 = Debug|Win32 {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|Win32.ActiveCfg = Debug|Win32 {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|Win32.Build.0 = Debug|Win32 {D1F7201F-064B-48AB-868C-FED22464841C}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/iis/ModSecurityIIS.vcxproj b/iis/ModSecurityIIS.vcxproj index beb52e1fc0..8570be807b 100644 --- a/iis/ModSecurityIIS.vcxproj +++ b/iis/ModSecurityIIS.vcxproj @@ -79,7 +79,7 @@ Disabled - C:\work\pcre-8.30\include;C:\work\pcre-8.30;C:\work\libxml2-2.7.7\include;C:\apache22\include;..\apache2;..\standalone + C:\work\pcre-8.30\include;C:\work\pcre-8.30;C:\work\libxml2-2.7.7\include;C:\work\apache24\include;..\apache2;..\standalone _MBCS;%(PreprocessorDefinitions);WIN32;WINNT;inline=APR_INLINE;AP_DECLARE_STATIC;VERSION_IIS NotUsing precomp.h @@ -89,7 +89,7 @@ mymodule.def true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies);C:\apache22\lib\libapr-1.lib;C:\apache22\lib\libaprutil-1.lib;C:\work\pcre-8.30\pcre.lib;C:\work\libxml2-2.7.7\win32\bin.msvc\libxml2.lib + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies);C:\work\apache24\lib\libapr-1.lib;C:\work\apache24\lib\libaprutil-1.lib;C:\work\pcre-8.30\pcre.lib;C:\work\libxml2-2.7.7\win32\bin.msvc\libxml2.lib copy /y $(TargetPath) c:\drop\$(PlatformShortName) diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index b02e6f6071..164a26be99 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -87,6 +87,11 @@ char *GetIpAddr(apr_pool_t *pool, PSOCKADDR pAddr) DWORD len = 50; char *buf = (char *)apr_palloc(pool, len); + if(buf == NULL) + return ""; + + buf[0] = 0; + WSAAddressToString(pAddr, sizeof(SOCKADDR), NULL, buf, &len); return buf; From 0e2b07b00e582a93837dcbdb4944919686a18e8d Mon Sep 17 00:00:00 2001 From: Breno Silva Date: Tue, 13 Nov 2012 22:24:41 -0400 Subject: [PATCH 98/99] Fixed \ handling in Include command on Windows. --- standalone/config.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/standalone/config.c b/standalone/config.c index 615f0a9eaf..95c363d269 100644 --- a/standalone/config.c +++ b/standalone/config.c @@ -849,7 +849,11 @@ static const char *process_resource_config_fnmatch(const char *path, /* find the first part of the filename */ rest = ap_strchr_c(fname, '/'); - if (rest) { + + if(rest == NULL) + rest = ap_strchr_c(fname, '\\'); + + if (rest) { fname = apr_pstrndup(ptemp, fname, rest - fname); rest++; } @@ -953,7 +957,7 @@ AP_DECLARE(const char *) process_fnmatch_configs(apr_array_header_t *ari, const char *rootpath, *filepath = fname; /* locate the start of the directories proper */ - status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, ptemp); + status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); /* we allow APR_SUCCESS and APR_EINCOMPLETE */ if (APR_ERELATIVE == status) { From f6b9080757eec645fe24695be9649f58a0834c16 Mon Sep 17 00:00:00 2001 From: Greg Wroblewski Date: Thu, 17 Jan 2013 23:31:23 -0800 Subject: [PATCH 99/99] IIS version improvements --- apache2/Makefile.win | 7 +- apache2/mod_security2_config.hw | 1 - apache2/modsecurity_config.h | 4 + apache2/msc_release.h | 2 +- apache2/msc_util.c | 19 + iis/Makefile.win | 5 +- iis/ModSecurityIIS/Installer/Installer.cpp | 1994 ++++++++ .../Installer/Installer.vcxproj | 96 + .../Installer/Installer.vcxproj.filters | 42 + .../Installer/Installer.vcxproj.user | 7 + iis/ModSecurityIIS/Installer/ReadMe.txt | 40 + iis/ModSecurityIIS/Installer/XUnzip.cpp | 4384 +++++++++++++++++ iis/ModSecurityIIS/Installer/XUnzip.h | 382 ++ iis/ModSecurityIIS/Installer/stdafx.cpp | 8 + iis/ModSecurityIIS/Installer/stdafx.h | 15 + iis/ModSecurityIIS/Installer/targetver.h | 8 + iis/ModSecurityIIS/ModSecurityIIS.sln | 36 +- iis/ModSecurityIIS/ModSecurityIIS/readme.rtf | 118 +- iis/mymodule.cpp | 36 +- nginx/modsecurity/ngx_http_modsecurity.c | 2 +- standalone/api.c | 1114 +++-- standalone/api.h | 5 +- standalone/config.c | 2322 ++++----- standalone/main.cpp | 2 +- standalone/standalone.vcxproj.user | 2 +- 25 files changed, 8894 insertions(+), 1757 deletions(-) delete mode 100644 apache2/mod_security2_config.hw create mode 100644 iis/ModSecurityIIS/Installer/Installer.cpp create mode 100644 iis/ModSecurityIIS/Installer/Installer.vcxproj create mode 100644 iis/ModSecurityIIS/Installer/Installer.vcxproj.filters create mode 100644 iis/ModSecurityIIS/Installer/Installer.vcxproj.user create mode 100644 iis/ModSecurityIIS/Installer/ReadMe.txt create mode 100644 iis/ModSecurityIIS/Installer/XUnzip.cpp create mode 100644 iis/ModSecurityIIS/Installer/XUnzip.h create mode 100644 iis/ModSecurityIIS/Installer/stdafx.cpp create mode 100644 iis/ModSecurityIIS/Installer/stdafx.h create mode 100644 iis/ModSecurityIIS/Installer/targetver.h diff --git a/apache2/Makefile.win b/apache2/Makefile.win index 87a576d3be..c6c4bb6bc8 100644 --- a/apache2/Makefile.win +++ b/apache2/Makefile.win @@ -52,16 +52,13 @@ all: $(DLL) dll: $(DLL) -mod_security2_config.h: mod_security2_config.hw - @type mod_security2_config.hw > modsecurity_config.h - .c.obj: $(CC) $(CFLAGS) -c $< -Fo$@ .cpp.obj: $(CC) $(CFLAGS) -c $< -Fo$@ -$(DLL): mod_security2_config.h $(OBJS) +$(DLL): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -LD $(OBJS) -Fe$(DLL) $(LIBS) /link IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);2 @@ -69,4 +66,4 @@ install: $(DLL) copy /Y $(DLL) $(APACHE)\modules clean: - del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin mod_security2_config.h *.manifest + del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin *.manifest diff --git a/apache2/mod_security2_config.hw b/apache2/mod_security2_config.hw deleted file mode 100644 index c95004462f..0000000000 --- a/apache2/mod_security2_config.hw +++ /dev/null @@ -1 +0,0 @@ -/* This file is left empty for building on Windows. */ diff --git a/apache2/modsecurity_config.h b/apache2/modsecurity_config.h index 720a3c5fdd..7de60afe71 100644 --- a/apache2/modsecurity_config.h +++ b/apache2/modsecurity_config.h @@ -2,6 +2,8 @@ * so this is here to prevent that by removing them. */ +#ifndef WIN32 + /* Undefine all these so there are no conflicts */ #undef PACKAGE #undef PACKAGE_BUGREPORT @@ -22,3 +24,5 @@ #undef PACKAGE_TARNAME #undef PACKAGE_URL #undef PACKAGE_VERSION + +#endif diff --git a/apache2/msc_release.h b/apache2/msc_release.h index f960d61bd4..ff95235b77 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -50,7 +50,7 @@ /* Apache Module Defines */ #ifdef VERSION_IIS -#define MODSEC_MODULE_NAME "ModSecurity for IIS (Beta)" +#define MODSEC_MODULE_NAME "ModSecurity for IIS (RC)" #else #ifdef VERSION_NGINX #define MODSEC_MODULE_NAME "ModSecurity for nginx (Beta)" diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 1e20473d09..a46312211f 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1206,7 +1206,26 @@ char *log_escape(apr_pool_t *mp, const char *text) { } char *log_escape_nq(apr_pool_t *mp, const char *text) { +#ifdef VERSION_IIS + int l = 0; + + // this is a workaround for unknown bug that causes 'text' sometimes to lack zero-termination + // + __try + { + l = text ? strlen(text) : 0; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + l = -1; + } + if(l < 0) + return _log_escape(mp, "BUG: see log_escape_nq()", 24, 0, 0, 0); + + return _log_escape(mp, (const unsigned char *)text, l, 0, 0, 0); +#else return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 0, 0, 0); +#endif } char *log_escape_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) { diff --git a/iis/Makefile.win b/iis/Makefile.win index ae6acdbbf2..dafbacda9a 100644 --- a/iis/Makefile.win +++ b/iis/Makefile.win @@ -57,9 +57,6 @@ all: $(DLL) dll: $(DLL) -..\apache2\mod_security2_config.h: ..\apache2\mod_security2_config.hw - @type ..\apache2\mod_security2_config.hw > ..\apache2\modsecurity_config.h - $(OBJS1): ..\apache2\$*.c $(CC) $(CFLAGS) -c ..\apache2\$*.c -Fo$@ @@ -69,7 +66,7 @@ $(OBJS2): ..\standalone\$*.c .cpp.obj: $(CC) $(CFLAGS) -c $< -Fo$@ -$(DLL): ..\apache2\mod_security2_config.h $(OBJS1) $(OBJS2) $(OBJS3) +$(DLL): $(OBJS1) $(OBJS2) $(OBJS3) $(LINK) $(LDFLAGS) $(OBJS1) $(OBJS2) $(OBJS3) $(LIBS) IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);#1 diff --git a/iis/ModSecurityIIS/Installer/Installer.cpp b/iis/ModSecurityIIS/Installer/Installer.cpp new file mode 100644 index 0000000000..24f6d90726 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.cpp @@ -0,0 +1,1994 @@ +// Installer.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include +#include +#include "XUnzip.h" + +// Logging level definition. +#define EVENT_ERROR 100 +#define EVENT_CRITICAL 75 +#define EVENT_IMPORTANT 50 +#define EVENT_NORMAL 25 +#define EVENT_LEVEL_MASK 0xFFFF + +#define EVENT_APPLOG (1 << 17) +#define EVENT_SYSLOG (1 << 18) +#define EVENT_STDOUT (1 << 19) +#define EVENT_STDERR (1 << 20) + +// This class uese IIS7.0 native API to operate on +// %windir%\system32\inetsrv\config\applicationHost.config +class Iis7Config +{ + public: + Iis7Config() {} + virtual ~Iis7Config() {} + + // Defines the IIS running mode. + enum AppMode { + MODE_X64, // IIS runs under x86_64 mode. + MODE_X86, // IIS runs under i386 mode. + MODE_UNKNOWN, // IIS run under an unknow mode. + MODE_FAIL // Failed to retrieve app mode. + }; + + static void Log(int event_meta, const char *event_pattern, ...) { + const int kErrorLen = 2048; + char event_message[kErrorLen]; + va_list argptr; + + // If current event is less than log_level_, return directly. + int event_level = event_meta & EVENT_LEVEL_MASK; + //if (event_level < log_level_) return; + + // Combile log type with default log type. + int event_type = event_meta;// | log_type_; + + va_start(argptr, event_pattern); + vsnprintf(event_message, kErrorLen, event_pattern, argptr); + va_end(argptr); + +// #ifdef WIN32 + // Only write error and critical event to windows event log +// if ((event_type & EVENT_SYSLOG) != 0 && event_level >= EVENT_CRITICAL) { + WORD windows_event_type; + if (event_level == EVENT_ERROR) { + windows_event_type = EVENTLOG_ERROR_TYPE; + } else { // if (event_level == EVENT_CRITICAL) + windows_event_type= EVENTLOG_INFORMATION_TYPE; + } + + LPCSTR event_message_pointer = event_message; + + // Get a handle to the event log. + HANDLE h = RegisterEventSource(NULL, L"ModSecurityIIS Installer"); + if (h != NULL) { + // Report the event. + ReportEventA(h, // event log handle + windows_event_type, // event type + 0, // event category + 1, // event identifier + NULL, // no user security identifier + 1, // number of strings + 0, // no data + &event_message_pointer, // pointer to strings + NULL); // no data + DeregisterEventSource(h); + } +// } +// #endif // WIN32 + + // Output log to file +/* if ((event_type & EVENT_APPLOG) != 0) { + if (CreateLogFile()) { + FILE* file = fopen(log_file_name_.c_str(), "a+"); + if (file != NULL) { + fputs(FormatW3CTime(time(NULL)).c_str(), file); + fputs(": ", file); + fputs(event_message, file); + fputs("\n", file); + fclose(file); + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to open log file (%d).\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to create log file (%d)\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } + + // Output to std err. + if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: %s\n", + FormatW3CTime(time(NULL)).c_str(), event_message); + }*/ + } + + static void Log(int event_meta, const wchar_t *event_pattern, ...) { + const int kErrorLen = 2048; + wchar_t event_message[kErrorLen]; + va_list argptr; + + // If current event is less than log_level_, return directly. + int event_level = event_meta & EVENT_LEVEL_MASK; + //if (event_level < log_level_) return; + + // Combile log type with default log type. + int event_type = event_meta;// | log_type_; + + va_start(argptr, event_pattern); + _vsnwprintf(event_message, kErrorLen, event_pattern, argptr); + va_end(argptr); + +// #ifdef WIN32 + // Only write error and critical event to windows event log +// if ((event_type & EVENT_SYSLOG) != 0 && event_level >= EVENT_CRITICAL) { + WORD windows_event_type; + if (event_level == EVENT_ERROR) { + windows_event_type = EVENTLOG_ERROR_TYPE; + } else { // if (event_level == EVENT_CRITICAL) + windows_event_type= EVENTLOG_INFORMATION_TYPE; + } + + LPCWSTR event_message_pointer = event_message; + + // Get a handle to the event log. + HANDLE h = RegisterEventSource(NULL, L"ModSecurityIIS Installer"); + if (h != NULL) { + // Report the event. + ReportEventW(h, // event log handle + windows_event_type, // event type + 0, // event category + 1, // event identifier + NULL, // no user security identifier + 1, // number of strings + 0, // no data + &event_message_pointer, // pointer to strings + NULL); // no data + DeregisterEventSource(h); + } +// } +// #endif // WIN32 + + // Output log to file +/* if ((event_type & EVENT_APPLOG) != 0) { + if (CreateLogFile()) { + FILE* file = fopen(log_file_name_.c_str(), "a+"); + if (file != NULL) { + fputs(FormatW3CTime(time(NULL)).c_str(), file); + fputs(": ", file); + fputs(event_message, file); + fputs("\n", file); + fclose(file); + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to open log file (%d).\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } else if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: Failed to create log file (%d)\n", + FormatW3CTime(time(NULL)).c_str(), errno); + } + } + + // Output to std err. + if ((event_type & EVENT_STDERR) != 0) { + fprintf(stderr, "%s: %s\n", + FormatW3CTime(time(NULL)).c_str(), event_message); + }*/ + } + + // Install/uninstall filters in IIS. + // Actually, add/remove entry to/from + // adn sections. + static bool InstallFilter(const wchar_t* dll_file); + static bool UninstallFilter(); + + // Install/uninstall a site for Admin Console. + // Permissions for CGI exe are set/unset accordingly. +/* static bool InstallAdminConsole(const char* site_root, const char* cgi_path); + static bool UninstallAdminConsole(const char* site_root, + const char* cgi_path); +*/ + // Determine whether version 7 mode supported. + static bool IsSupported(); + + // Load configuration from IIS + // Actually, load from section. + virtual bool Load(); + + // Get IIS 7 running mode. + static AppMode GetAppMode(); + + private: + // Module name + static BSTR kModuleName; + static BSTR kConfigName; + + // Admin Console site name, "Google Sitemap Generator Admin Console". + //static BSTR kSiteName; + + static bool Iis7Config::AddToConfigSections(IAppHostWritableAdminManager* manager, + BSTR config); + + // Add an entry to section, like: + // + static bool AddToGlobalModules(IAppHostWritableAdminManager* manager, + BSTR image); + + // Add an entry to section, like: + // + static bool AddToModules(IAppHostWritableAdminManager* manager); + + // All CGI exe to be executed. + // "cgi_path" is the executable file path. + //static bool SetCGIRestriction(IAppHostWritableAdminManager* manager, + // BSTR cgi_path); + + // Create Admin Console site. + // "site_root" is the site root directory. + //static bool CreateAdminConsoleSite(IAppHostWritableAdminManager *manager, + // BSTR site_root); + + // Remove customization for Admin Console site. + // This customzation is added in CustomizeAdminConsole method. + //static bool RemoveAdminConsoleCustomization(IAppHostWritableAdminManager* manager); + + // Add customization for Admin Console site. + // Actually, it adds a tag for the Admin Console site. + //static bool CustomizeAdminConsole(IAppHostWritableAdminManager* manager, + // BSTR cgi_path); + + // Set property value. + static bool SetProperty(IAppHostElement* element, BSTR name, BSTR value); + + // Get property value. + static bool GetProperty(IAppHostElement* element, BSTR name, VARIANT* value); + + // Get the index from given collection, whose key=value. + // *index == -1 indicates nothing found. + static bool GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + short* index); + + // Get the element from given colletion. + // The element should contains a propery, whose key/value is given by arg. + // "*element = NULL" indicates nothing can be found. + static bool GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + IAppHostElement** element); + + // Get child's index from given collection, whose key = value. + // *index == -1 indicates no such child. + static bool GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, short* index); + + // Get child element form given child collection. + // The child element should contains a property, whose key/value is given by + // arg. *element = NULL indicates no such child. + static bool GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, IAppHostElement** element); + + // Remove an entry from given section. + static bool RemoveFromCollection(IAppHostWritableAdminManager* manager, + BSTR section, BSTR property_name, BSTR property_value); + + // Load site configuration from an element. + //bool LoadSite(IAppHostElement* site_element); + + static int Iis7Config::GetNativeSystemInfo(SYSTEM_INFO* system_info); +}; + +BSTR Iis7Config::kModuleName = L"ModSecurityIIS"; +BSTR Iis7Config::kConfigName = L"ModSecurity"; +//BSTR Iis7Config::kSiteName = L"ModSecurityIIS Admin Console"; + +#define CHECK_HRESULT(expression, msg) \ +{ \ +HRESULT _hr = (expression); \ +if (FAILED(_hr)) { \ + Log(EVENT_ERROR, msg " (0x%0x)", _hr); \ + break; \ +} \ +} +#define CHECK_BOOL(expression, msg) \ + if (!expression) { \ + Log(EVENT_ERROR, msg); \ + break; \ + } + +bool Iis7Config::Load() { + IAppHostAdminManager* admin_manager = NULL; + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + //BSTR section_name = SysAllocString(L"system.applicationHost/sites"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Initialize com + hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to intialize COM."); + break; + } + + // Create an admin manager + hresult = CoCreateInstance(__uuidof(AppHostAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostAdminManager), + reinterpret_cast(&admin_manager)); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create AppHostAdminManager."); + break; + } +/* + // Get section + hresult = admin_manager->GetAdminSection(section_name, bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access sites configuration."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get sites configuration."); + break; + } + + // Get all the child elements of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access child collection."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get child collection."); + break; + } + + // Iterate all the children of + DWORD element_count = 0; + hresult = collection->get_Count(&element_count); + for(USHORT i = 0; i < element_count; ++i) { + VARIANT item_index; + item_index.vt = VT_I2; + item_index.iVal = i; + + // Get the child element + hresult = collection->get_Item(item_index, &element); + if (FAILED(hresult) || &element == NULL) { + Log(EVENT_ERROR, "Failed to iterate child (%d) of sites.", i); + continue; + } + + if (!LoadSite(element)) { + Log(EVENT_ERROR, "Failed to load site from site (%d).", i); + continue; + } + + element->Release(); + element = NULL; + } +*/ + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + if (admin_manager != NULL) admin_manager->Release(); + + //SysFreeString(section_name); + SysFreeString(bstr_config_path); + CoUninitialize(); + + return result; +} + +bool Iis7Config::InstallFilter(const wchar_t* dll_file) { + IAppHostWritableAdminManager* admin_manager = NULL; + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + // Build the full path of ModSecurity module DLL. + BSTR module_wpath = SysAllocString(dll_file); + BSTR config = SysAllocString(kConfigName); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Initialize com + hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to intialize COM."); + break; + } + + // Create a writable admin manager + hresult = CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create AppHostWritableAdminManager."); + break; + } + + // set commit path + hresult = admin_manager->put_CommitPath(L"MACHINE/WEBROOT/APPHOST"); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to put commit path."); + break; + } + + // Add ModSecurity entry to section + if (!AddToGlobalModules(admin_manager, module_wpath)) { + Log(EVENT_ERROR, "Failed to add entry to globalModules."); + break; + } + + // Add ModSecurity entry to section + if (!AddToModules(admin_manager)) { + Log(EVENT_ERROR, "Failed to add entry to modules."); + break; + } + + if(!AddToConfigSections(admin_manager, config)) { + Log(EVENT_ERROR, "Failed to add entry to config sections."); + break; + } + + // Save changes. + hresult = admin_manager->CommitChanges(); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to save changes to install module."); + break; + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + + SysFreeString(module_wpath); + SysFreeString(bstr_config_path); + SysFreeString(config); + + CoUninitialize(); + + return result; +} + +bool Iis7Config::UninstallFilter() { + IAppHostWritableAdminManager* admin_manager = NULL; + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Initialize com + hresult = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to intialize COM."); + break; + } + + // Create a writable admin manager + hresult = CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create AppHostWritableAdminManager."); + break; + } + + // set commit path + hresult = admin_manager->put_CommitPath(bstr_config_path); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to put commit path."); + break; + } + + // Remove ModSecurity entry from section. + if (!RemoveFromCollection(admin_manager, L"system.webServer/globalModules", L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to remove entry from globalModules."); + break; + } + + // Remove ModSecurity entry from section. + if (!RemoveFromCollection(admin_manager, L"system.webServer/modules", L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to remove entry from modules."); + break; + } + + // Save changes. + hresult = admin_manager->CommitChanges(); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to save changes to install module."); + break; + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + SysFreeString(bstr_config_path); + + CoUninitialize(); + + return result; +} + +// Add section to , like: +//
    +bool Iis7Config::AddToConfigSections(IAppHostWritableAdminManager* manager, + BSTR config) { + HRESULT hr; + bool result = false; + IAppHostConfigManager * pCfgMgr = NULL; + IAppHostConfigFile * pCfgFile = NULL; + IAppHostSectionGroup * pRtSctnGrp = NULL; + IAppHostSectionGroup * pSctnGrp = NULL; + IAppHostSectionDefinitionCollection * pSctnDefCol = NULL; + IAppHostSectionDefinition * pSctnDef = NULL; + + BSTR bstrConfigCommitPath = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstrSctnGrpName = SysAllocString(L"system.webServer"); + BSTR bstrDeny = SysAllocString(L"Allow"); + BSTR bstrAppHostOnly = SysAllocString(L"Everywhere"); + + // Get an IAppHostConfigManager + hr = manager -> get_ConfigManager ( &pCfgMgr ); + if ( FAILED( hr ) || ( &pCfgMgr == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to get a config manager.\n" ); + goto exit; + } + + // Get an IAppHostConfigFile + hr = pCfgMgr -> GetConfigFile ( bstrConfigCommitPath, &pCfgFile ); + if ( FAILED ( hr ) || ( &pCfgFile == NULL ) ) + { + if ( E_ACCESSDENIED == hr ) + { + Log(EVENT_ERROR, "ERROR: Access to configuration denied.\n" ); + Log(EVENT_ERROR, " Run sample as an administrator.\n" ); + } + else + { + Log(EVENT_ERROR, "ERROR: Unable to get config file.\n" ); + } + goto exit; + } + + // Get the root section group + hr = pCfgFile -> get_RootSectionGroup ( &pRtSctnGrp ); + if ( FAILED ( hr ) || ( &pRtSctnGrp == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access root section group\n" ); + goto exit; + } + + ULONG i, cnt; + + hr = pRtSctnGrp ->get_Count(&cnt); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to enumerate root section group\n" ); + goto exit; + } + + for(i = 0; i < cnt; i++) + { + VARIANT v; + BSTR name; + + v.vt = VT_I4; + v.intVal = i; + + hr = pRtSctnGrp->get_Item(v, &pSctnGrp); + if ( FAILED ( hr ) || ( &pSctnGrp == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access root section item\n" ); + goto exit; + } + + hr = pSctnGrp->get_Name(&name); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to get root section name\n" ); + goto exit; + } + + if(wcscmp(name, L"system.webServer") == 0) + break; + + pSctnGrp->Release(); + } + + if(i == cnt) + { + Log(EVENT_ERROR, "ERROR: Unable to find system.webServer section group\n"); + goto exit; + } + + // Get the section collection + hr = pSctnGrp -> get_Sections ( &pSctnDefCol ); + if ( FAILED ( hr ) || ( &pSctnDefCol == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access section collection\n" ); + goto exit; + } + + hr = pSctnDefCol->get_Count(&cnt); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to enumerate section collection\n" ); + goto exit; + } + + for(i = 0; i < cnt; i++) + { + VARIANT v; + BSTR name; + + v.vt = VT_I4; + v.intVal = i; + + hr = pSctnDefCol->get_Item(v, &pSctnDef); + if ( FAILED ( hr ) || ( &pSctnDef == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to access section item\n" ); + goto exit; + } + + hr = pSctnDef->get_Name(&name); + if ( FAILED ( hr )) + { + Log(EVENT_ERROR, "ERROR: Unable to get section name\n" ); + goto exit; + } + + if(wcscmp(name, config) == 0) + break; + + pSctnDef->Release(); + } + + if(i == cnt) + { + // Add the new section + hr = pSctnDefCol -> AddSection ( config, &pSctnDef ); + if ( FAILED ( hr ) || ( &pSctnDef == NULL ) ) + { + Log(EVENT_ERROR, "ERROR: Unable to add new section\n" ); + goto exit; + } + } + + // Set the section attributes + pSctnDef -> put_OverrideModeDefault ( bstrDeny ); + pSctnDef -> put_AllowDefinition ( bstrAppHostOnly ); + + result = true; +exit: + // Exiting / Unwinding + if ( pRtSctnGrp != NULL ) + { + pRtSctnGrp->Release(); + pRtSctnGrp = NULL; + } + if ( pSctnGrp != NULL ) + { + pSctnGrp->Release(); + pSctnGrp = NULL; + } + if ( pSctnDefCol != NULL ) + { + pSctnDefCol->Release(); + pSctnDefCol = NULL; + } + if ( pSctnDef != NULL ) + { + pSctnDef->Release(); + pSctnDef = NULL; + } + if ( pCfgFile != NULL ) + { + pCfgFile->Release(); + pCfgFile = NULL; + } + if ( pCfgMgr != NULL ) + { + pCfgMgr->Release(); + pCfgMgr = NULL; + } + + SysFreeString( bstrConfigCommitPath ); + SysFreeString( bstrSctnGrpName ); + SysFreeString( bstrDeny ); + SysFreeString( bstrAppHostOnly ); + + return result; +} + +// Add an entry to section, like: +// +bool Iis7Config::AddToGlobalModules(IAppHostWritableAdminManager* manager, + BSTR image) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.webServer/globalModules"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstr_element_name = SysAllocString(L"add"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access globalModules configuration."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get globalModules configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access globalModules child collection."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get globalModules child collection."); + break; + } + + // create a global modules child element, like: + // + + // First to detect old modules. + if (!GetFromCollection(collection, L"name", kModuleName, &element)) { + Log(EVENT_ERROR, "Failed to try detect old modules."); + break; + } + + // No old ModSecurity module, create/add one. + if (element == NULL) { + hresult = collection->CreateNewElement(bstr_element_name, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create globalModules/add element."); + break; + } + + if (!SetProperty(element, L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to set name property."); + break; + } + // Add the new element to collection + hresult = collection->AddElement(element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to add globalModule/add element."); + break; + } + } + + if (!SetProperty(element, L"image", image)) { + Log(EVENT_ERROR, "Failed to set image property."); + break; + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + SysFreeString(bstr_element_name); + + return result; +} + + +// Add an entry to section, like: +// +bool Iis7Config::AddToModules(IAppHostWritableAdminManager* manager) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.webServer/modules"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstr_element_name = SysAllocString(L"add"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access modules configuration."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get modules configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access modules child collection."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get modules child collection."); + break; + } + + // Try to detect old ModSecurity module. + if (!GetFromCollection(collection, L"name", kModuleName, &element)) { + Log(EVENT_ERROR, "Failed to try detect old modules."); + break; + } + + // No old ModSecurity module exists. + if (element == NULL) { + hresult = collection->CreateNewElement(bstr_element_name, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create modules/add element."); + break; + } + + if (!SetProperty(element, L"name", kModuleName)) { + Log(EVENT_ERROR, "Failed to set name property."); + break; + } + + // Add the new element to collection + hresult = collection->AddElement(element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to add modules/add element."); + break; + } + } + + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + SysFreeString(bstr_element_name); + + return result; +} + +bool Iis7Config::RemoveFromCollection(IAppHostWritableAdminManager* manager, + BSTR section, BSTR property_name, BSTR property_value) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + IAppHostProperty* name_property = NULL; + + BSTR bstr_section_name = SysAllocString(section); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section contains ModSecurityModule entry + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access section to remove module."); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get section to remove module."); + break; + } + + // Get child collection of the section + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access collection to remove module."); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get collection to remove module."); + break; + } + + // Get number of children. + DWORD count; + hresult = collection->get_Count(&count); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get the count of collection."); + break; + } + + short sitemap_index; + if (!GetFromCollection(collection, property_name, property_value, &sitemap_index)) { + Log(EVENT_ERROR, "Failed to find ModSecurity module."); + break; + } + + if (sitemap_index != -1) { + VARIANT var_index; + var_index.vt = VT_I2; + var_index.iVal = sitemap_index; + + hresult = collection->DeleteElement(var_index); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to remove ModSecurity module."); + } + } else { + Log(EVENT_IMPORTANT, "No ModSecurity module is found."); + } + + result = SUCCEEDED(hresult); + } while (false); + + // Exiting / Unwinding + if (name_property != NULL) name_property->Release(); + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + + return result; +} +/* +bool Iis7Config::LoadSite(IAppHostElement* element) { + std::string name, id, phys_path, log_path; + + // Get name. + VARIANT name_var; + if (!GetProperty(element, L"name", &name_var)) { + Log(EVENT_ERROR, "Failed to get name property value."); + return false; + } else if (name_var.vt != VT_BSTR) { + Log(EVENT_ERROR, "site name should be VT_BSTR"); + return false; + } else { + name = static_cast(bstr_t(name_var.bstrVal)); + } + + // Get id. + VARIANT id_var; + if (!GetProperty(element, L"id", &id_var)) { + Log(EVENT_ERROR, "Failed to get id value."); + return false; + } else if (id_var.vt == VT_I4) { + char buffer[16]; + itoa(id_var.intVal, buffer, 10); + id = buffer; + } else if (id_var.vt == VT_UI4) { + char buffer[16]; + itoa(id_var.uintVal, buffer, 10); + id = buffer; + } else if (id_var.vt == VT_BSTR) { + id = static_cast(bstr_t(id_var.bstrVal)); + } else { + Log(EVENT_ERROR, "site id should be VT_BSTR or VT_I4"); + } + + // Get logfile directory. + IAppHostElement* logfile_element = NULL; + HRESULT hresult = element->GetElementByName(L"logfile", &logfile_element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get logfile element."); + } else { + VARIANT dir_var; + if (!GetProperty(logfile_element, L"directory", &dir_var)) { + Log(EVENT_ERROR, "Failed to get directory value."); + } else if (dir_var.vt == VT_BSTR) { + log_path = static_cast(bstr_t(dir_var.bstrVal)); + log_path.append("\\W3SVC").append(id); + } else { + Log(EVENT_ERROR, "directory should be VT_BSTR"); + } + } + + // Get pysical path. + IAppHostElementCollection* application_collection = NULL; + IAppHostElementCollection* virtualdir_collection = NULL; + IAppHostElement* application_element = NULL; + IAppHostElement* virtualdir_element = NULL; + + hresult = S_OK; + bool result = false; + do { + // Get collection in element + hresult = element->get_Collection(&application_collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get application collection."); + break; + } + + // Find element wiht path="/" + short root_app = -1; + if (!GetFromCollection(application_collection, L"path", L"/", &root_app)) { + Log(EVENT_ERROR, "Failed to get root application index."); + break; + } else if (root_app == -1) { + Log(EVENT_ERROR, "No root application defined."); + break; + } + + // Get the root element + VARIANT var_rootapp; + var_rootapp.vt = VT_I2; + var_rootapp.iVal = root_app; + hresult = application_collection->get_Item(var_rootapp, &application_element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get root application element."); + break; + } + + // Get collection in element. + hresult = application_element->get_Collection(&virtualdir_collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get virtualdir collection."); + break; + } + + // Find element with path="/" + short root_dir = -1; + if (!GetFromCollection(virtualdir_collection, L"path", L"/", &root_dir)) { + Log(EVENT_ERROR, "Failed to get root virtualDirectory index."); + break; + } else if (root_dir == -1) { + Log(EVENT_ERROR, "No root virtualDirectory defined."); + break; + } + + // Get the root element. + VARIANT var_rootdir; + var_rootdir.vt = VT_I2; + var_rootdir.iVal = root_dir; + hresult = virtualdir_collection->get_Item(var_rootdir, &virtualdir_element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get root virtualDirectory element."); + break; + } + + VARIANT var_path; + if (!GetProperty(virtualdir_element, L"physicalPath", &var_path)) { + Log(EVENT_ERROR, "Failed to get physicalPath property."); + break; + } else if (var_path.vt != VT_BSTR) { + Log(EVENT_ERROR, "physicalPath should be instance of VT_BSTR."); + break; + } + + phys_path = static_cast(bstr_t(var_path.bstrVal)); + result = true; + } while (false); + + if (logfile_element != NULL) logfile_element->Release(); + if (application_collection != NULL) application_collection->Release(); + if (virtualdir_collection != NULL) virtualdir_collection->Release(); + if (application_element != NULL) application_element->Release(); + if (virtualdir_element != NULL) virtualdir_element->Release(); + + // At last we store all the values. + if (result) { + site_ids_.push_back(id); + names_.push_back(name); + physical_paths_.push_back(phys_path); + log_paths_.push_back(log_path); + host_urls_.push_back(""); + } + + return result; +} +*/ +bool Iis7Config::GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + short* index) { + IAppHostProperty* property = NULL; + IAppHostElement* element = NULL; + + HRESULT hresult = S_OK; + *index = -1; + do { + // Get number of children. + DWORD count; + hresult = collection->get_Count(&count); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get the count of collection."); + break; + } + + // Iterate every child to check whether it should be removed. + for (USHORT i = 0; i < count && SUCCEEDED(hresult); ++i) { + // Get item at index i. + VARIANT idx; + idx.vt = VT_I2; + idx.iVal = i; + hresult = collection->get_Item(idx, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get item (%d).", i); + break; + } + + // Get name property of item i. + VARIANT var_value; + if (!GetProperty(element, property_key, &var_value)) { + Log(EVENT_ERROR, "Failed to get property value."); + hresult = S_FALSE; + break; + } + + // Check the property value + if (wcscmp(property_value, var_value.bstrVal) == 0) { + *index = static_cast(i); + break; + } + + element->Release(); + element = NULL; + } + } while (false); + + if (property != NULL) property->Release(); + if (element != NULL) element->Release(); + + return SUCCEEDED(hresult); +} + +bool Iis7Config::GetFromCollection(IAppHostElementCollection* collection, + BSTR property_key, BSTR property_value, + IAppHostElement** element) { + short idx; + if (!GetFromCollection(collection, property_key, property_value, &idx)) { + Log(EVENT_ERROR, "Failed to get child from collection."); + } + + if (idx != -1) { + VARIANT idx_var; + idx_var.vt = VT_I2; + idx_var.iVal = idx; + + HRESULT hresult = collection->get_Item(idx_var, element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get element from collection."); + return false; + } else { + return true; + } + } else { + *element = NULL; + return true; + } +} + + +bool Iis7Config::SetProperty(IAppHostElement* element, BSTR name, BSTR value) { + IAppHostProperty* property = NULL; + BSTR bstr_name = SysAllocString(name); + BSTR bstr_value = SysAllocString(value); + + HRESULT hresult = S_OK; + do { + // Get the property by name. + hresult = element->GetPropertyByName(bstr_name, &property); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get property."); + break; + } + + // Set the property value. + VARIANT value_variant; + value_variant.vt = VT_BSTR; + value_variant.bstrVal = bstr_value; + hresult = property->put_Value(value_variant); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to set property value."); + break; + } + } while (false); + + if (property != NULL) property->Release(); + SysFreeString(bstr_name); + SysFreeString(bstr_value); + + return SUCCEEDED(hresult); +} + +bool Iis7Config::GetProperty(IAppHostElement* element, BSTR name, VARIANT* value) { + IAppHostProperty* property = NULL; + BSTR bstr_name = SysAllocString(name); + + HRESULT hresult = S_OK; + do { + // Get the property by name. + hresult = element->GetPropertyByName(bstr_name, &property); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get property."); + break; + } + + // Get the property value. + hresult = property->get_Value(value); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get property value."); + break; + } + } while (false); + + if (property != NULL) property->Release(); + SysFreeString(bstr_name); + + return SUCCEEDED(hresult); +} + + +bool Iis7Config::IsSupported() { + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hr)) { + Log(EVENT_ERROR, "Failed to check IIS 7 compatibility. 0x%0x", hr); + return false; + } + + // Try with IIS 7.0... + IAppHostAdminManager* admin_manager = NULL; + hr = CoCreateInstance(__uuidof(AppHostAdminManager), NULL, + CLSCTX_INPROC_SERVER, __uuidof(IAppHostAdminManager), + reinterpret_cast(&admin_manager)); + if (SUCCEEDED(hr)) { + admin_manager->Release(); + } + + CoUninitialize(); + + return SUCCEEDED(hr); +} + +int Iis7Config::GetNativeSystemInfo(SYSTEM_INFO* system_info) { + HMODULE module = GetModuleHandle(L"Kernel32.dll"); + if (module == NULL) { + Log(EVENT_ERROR, "Failed to GetModuleHandle of Kernel32.dll. (%d)", + GetLastError()); + return -1; + } + + typedef void (WINAPI *Procedure)(LPSYSTEM_INFO); + Procedure proc = (Procedure) GetProcAddress(module, "GetNativeSystemInfo"); + if (proc == NULL) { + if (GetLastError() == ERROR_PROC_NOT_FOUND) { + return 0; + } else { + Log(EVENT_ERROR, "Failed to GetProcAddress (%d).", GetLastError()); + return -1; + } + return 0; + } else { + proc(system_info); + return 1; + } +} + +Iis7Config::AppMode Iis7Config::GetAppMode() { + SYSTEM_INFO sys_info; + if (Iis7Config::GetNativeSystemInfo(&sys_info) != 1) { + Log(EVENT_ERROR, "Failed to GetNativeSystemInfo for IIS7."); + return MODE_FAIL; + } + + if (sys_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { + return MODE_X86; + } else if (sys_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { + return MODE_X64; + } else { + return MODE_UNKNOWN; + } +} +/* +bool Iis7Config::InstallAdminConsole(const char* site_root, + const char* cgi_path) { + IAppHostWritableAdminManager* admin_manager = NULL; + bool result = false; + do { + // Initialize com + CHECK_HRESULT(CoInitializeEx(NULL, COINIT_MULTITHREADED), "Failed to intialize COM."); + + // Create a writable admin manager + CHECK_HRESULT(CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)), + "Failed to create AppHostWritableAdminManager."); + + // set commit path + CHECK_HRESULT(admin_manager->put_CommitPath(L"MACHINE/WEBROOT/APPHOST"), + "Failed to put commit path."); + + + CHECK_BOOL(CreateAdminConsoleSite(admin_manager, bstr_t(site_root)), + "Failed to create AdminConsole Site."); + CHECK_BOOL(SetCGIRestriction(admin_manager, bstr_t(cgi_path)), + "Failed to set CGI restriction."); + CHECK_BOOL(CustomizeAdminConsole(admin_manager, bstr_t(cgi_path)), + "Failed to customize admin console."); + + // Save changes. + CHECK_HRESULT(admin_manager->CommitChanges(), + "Failed to save changes to install module."); + + Log(EVENT_CRITICAL, "Admin Console installed successfully."); + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + + CoUninitialize(); + + return result; +} + +bool Iis7Config::UninstallAdminConsole(const char* site_root, + const char* cgi_path) { + IAppHostWritableAdminManager* admin_manager = NULL; + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + bool result = false; + do { + // Initialize com + CHECK_HRESULT(CoInitializeEx(NULL, COINIT_MULTITHREADED), "Failed to intialize COM."); + + // Create a writable admin manager + CHECK_HRESULT(CoCreateInstance(__uuidof(AppHostWritableAdminManager), NULL, + CLSCTX_INPROC_SERVER, + __uuidof(IAppHostWritableAdminManager), + reinterpret_cast(&admin_manager)), + "Failed to create AppHostWritableAdminManager."); + + // set commit path + CHECK_HRESULT(admin_manager->put_CommitPath(L"MACHINE/WEBROOT/APPHOST"), + "Failed to put commit path."); + + CHECK_BOOL(RemoveFromCollection(admin_manager, L"system.applicationHost/sites", L"name", kSiteName), "Failed to remove Admin console site."); + CHECK_BOOL(RemoveFromCollection(admin_manager, L"system.webServer/security/isapiCgiRestriction", L"path", bstr_t(cgi_path)), + "Failed to remove CGI restriction."); + CHECK_BOOL(RemoveAdminConsoleCustomization(admin_manager), + "Failed to remove customization of admin console."); + + // Save changes. + CHECK_HRESULT(admin_manager->CommitChanges(), + "Failed to save changes to install module."); + + result = true; + } while (false); + + // Exiting / Unwinding + if (admin_manager != NULL) admin_manager->Release(); + + SysFreeString(bstr_config_path); + + CoUninitialize(); + + return result; +} + + +bool Iis7Config::SetCGIRestriction(IAppHostWritableAdminManager* manager, BSTR cgi_path) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.webServer/security/isapiCgiRestriction"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + BSTR bstr_element_name = SysAllocString(L"add"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access isapiCgiRestriction configuration. (0x%0x)", hresult); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get isapiCgiRestriction configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access isapiCgiRestriction child collection. (0x%0x)", hresult); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get isapiCgiRestriction child collection."); + break; + } + + // create a child element, like: + // + + // First to detect old cgi restriction. + if (!GetFromCollection(collection, L"path", cgi_path, &element)) { + Log(EVENT_ERROR, "Failed to try detect old cgi restriction."); + break; + } + + // No old cgi restriction, create one. + if (element == NULL) { + hresult = collection->CreateNewElement(bstr_element_name, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to create isapiCgiRestriction/add element. (0x%0x)", hresult); + break; + } + + if (!SetProperty(element, L"path", cgi_path)) { + Log(EVENT_ERROR, "Failed to set path property."); + break; + } + + if (!SetProperty(element, L"allowed", L"true")) { + Log(EVENT_ERROR, "Failed to set allowed property."); + break; + } + + // Add the new element to collection + hresult = collection->AddElement(element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to add isapiCgiRestriction/add element. (0x%0x)", hresult); + break; + } + } + + Log(EVENT_NORMAL, "CGIRestriction set properly."); + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + SysFreeString(bstr_element_name); + + return result; +} + + +bool Iis7Config::CreateAdminConsoleSite(IAppHostWritableAdminManager *manager, BSTR site_root) { + IAppHostElement* parent = NULL; + IAppHostElementCollection* collection = NULL; + IAppHostElement* element = NULL; + + BSTR bstr_section_name = SysAllocString(L"system.applicationHost/sites"); + BSTR bstr_config_path = SysAllocString(L"MACHINE/WEBROOT/APPHOST"); + + HRESULT hresult = S_OK; + bool result = false; + do { + // Get section + hresult = manager->GetAdminSection(bstr_section_name, + bstr_config_path, &parent); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access sites configuration. (0x%0x)", hresult); + break; + } else if (&parent == NULL) { + Log(EVENT_ERROR, "Unable to get sites configuration."); + break; + } + + // Get collection of + hresult = parent->get_Collection(&collection); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to access sites child collection. (0x%0x)", hresult); + break; + } else if (&collection == NULL) { + Log(EVENT_ERROR, "Unable to get sites child collection."); + break; + } + + // create a element, like: + + // First to detect old site. + if (!GetFromCollection(collection, L"name", kSiteName, &element)) { + Log(EVENT_ERROR, "Failed to try detect old site."); + break; + } + if (element != NULL) { + Log(EVENT_CRITICAL, "Old Admin Console Site exists. Skip."); + result = true; + break; + } + + DWORD site_count; + CHECK_HRESULT(collection->get_Count(&site_count), + "Failed to get site count."); + char site_id[64]; + itoa(site_count + 466453, site_id, 10); + + CHECK_HRESULT(collection->CreateNewElement(L"site", &element), + "Failed to create site element."); + CHECK_BOOL(SetProperty(element, L"name", kSiteName), + "Failed to set site name property."); + CHECK_BOOL(SetProperty(element, L"id", bstr_t(site_id)), + "Failed to set site id property."); + CHECK_HRESULT(collection->AddElement(element), + "Failed to add site element."); + + // Create and set element. + IAppHostElementCollection* app_collection = NULL; + IAppHostElement* app_element = NULL; + CHECK_HRESULT(element->get_Collection(&app_collection), + "Failed to get app collection."); + CHECK_HRESULT(app_collection->CreateNewElement(L"application", &app_element), + "Failed to create application element."); + CHECK_BOOL(SetProperty(app_element, L"path", L"/"), + "Failed to set app path."); + // CHECK_BOOL(SetProperty(app_element, L"applicationPool", kSiteName), "Failed to set app applicationPool."); + CHECK_HRESULT(app_collection->AddElement(app_element), + "Failed to add application element."); + + // Create and set element. + IAppHostElementCollection* dir_collection = NULL; + IAppHostElement* dir_element = NULL; + CHECK_HRESULT(app_element->get_Collection(&dir_collection), + "Failed to get virtualDirectory collection."); + CHECK_HRESULT(dir_collection->CreateNewElement(L"virtualDirectory", &dir_element), + "Failed to create virtualDirectory."); + CHECK_BOOL(SetProperty(dir_element, L"path", L"/"), + "Failed to set dir path."); + CHECK_BOOL(SetProperty(dir_element, L"physicalPath", site_root), + "Failed to set dir physicalPath."); + CHECK_HRESULT(dir_collection->AddElement(dir_element), + "Failed to add virtualDirectory."); + + // Get element. + IAppHostElement* bindings_element = NULL; + CHECK_HRESULT(element->GetElementByName(L"bindings", &bindings_element), + "Failed to get bindings element."); + CHECK_BOOL((bindings_element != NULL), + "Failed to find bindings element."); + + // Create and set element. + IAppHostElementCollection* binding_collection = NULL; + IAppHostElement* binding_element = NULL; + CHECK_HRESULT(bindings_element->get_Collection(&binding_collection), + "Failed to get binding collection."); + CHECK_HRESULT(binding_collection->CreateNewElement(L"binding", &binding_element), + "Failed to create binding."); + CHECK_BOOL(SetProperty(binding_element, L"protocol", L"http"), + "Failed to set binding protocol."); + CHECK_BOOL(SetProperty(binding_element, L"bindingInformation", bstr_t(IisConfig::kAdminConsoleBinding)), + "Failed to set binding bindingInformation."); + CHECK_HRESULT(binding_collection->AddElement(binding_element), + "Failed to add binding element."); + + Log(EVENT_NORMAL, "Admin Console site created successfully."); + result = true; + } while (false); + + // Exiting / Unwinding + if (element != NULL) element->Release(); + if (collection != NULL) collection->Release(); + if (parent != NULL) parent->Release(); + + SysFreeString(bstr_section_name); + SysFreeString(bstr_config_path); + + return result; +} +*/ +bool Iis7Config::GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, short* index) { + IAppHostElement* element = NULL; + + HRESULT hresult = S_OK; + *index = -1; + do { + // Get number of children. + DWORD count; + hresult = children->get_Count(&count); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get the count of children."); + break; + } + + // Iterate every child to check whether it should be removed. + for (USHORT i = 0; i < count && SUCCEEDED(hresult); ++i) { + // Get item at index i. + VARIANT idx; + idx.vt = VT_I2; + idx.iVal = i; + hresult = children->get_Item(idx, &element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Unable to get item [%d]. (0x%0x)", i, hresult); + break; + } + + // Get name property of item i. + + BSTR name = NULL; + if (!element->get_Name(&name)) { + Log(EVENT_ERROR, "Failed to get property value."); + hresult = S_FALSE; + break; + } + + // Check the property value + if (wcscmp(child_name, name) == 0) { + *index = static_cast(i); + SysFreeString(name); + break; + } + + SysFreeString(name); + element->Release(); + element = NULL; + } + } while (false); + + + if (element != NULL) element->Release(); + + return SUCCEEDED(hresult); +} + +bool Iis7Config::GetFromChildren(IAppHostChildElementCollection* children, + BSTR child_name, IAppHostElement** element) { + short idx; + if (!GetFromChildren(children, child_name, &idx)) { + Log(EVENT_ERROR, "Failed to get element from children."); + } + + if (idx != -1) { + VARIANT idx_var; + idx_var.vt = VT_I2; + idx_var.iVal = idx; + + HRESULT hresult = children->get_Item(idx_var, element); + if (FAILED(hresult)) { + Log(EVENT_ERROR, "Failed to get element from children."); + return false; + } else { + return true; + } + } else { + *element = NULL; + return true; + } +} +/* +bool Iis7Config::CustomizeAdminConsole(IAppHostWritableAdminManager* manager, BSTR cgi_path) { + IAppHostConfigManager* config_manager = NULL; + IAppHostConfigFile * config_file = NULL; + IAppHostConfigLocationCollection* location_collection = NULL; + IAppHostConfigLocation* location = NULL; + IAppHostElement* handlers = NULL; + IAppHostElementCollection* handlers_collection = NULL; + IAppHostElement* remove_element = NULL; + IAppHostElement* add_element = NULL; + + bool result = false; + do { + CHECK_HRESULT(manager->get_ConfigManager(&config_manager), + "Failed to get config manager."); + CHECK_HRESULT(config_manager->GetConfigFile(L"MACHINE/WEBROOT/APPHOST", &config_file), + "Failed to get config file."); + CHECK_HRESULT(config_file->get_Locations(&location_collection), + "Failed to get location collection."); + + bool success = false; + do { + // Get number of children. + DWORD count; + CHECK_HRESULT(location_collection->get_Count(&count), "Unable to get the count of collection."); + + // Iterate every child to check whether it should be removed. + USHORT i = 0; + for (; i < count; ++i) { + // Get item at index i. + VARIANT idx; + idx.vt = VT_I2; + idx.iVal = i; + CHECK_HRESULT(location_collection->get_Item(idx, &location), "Unable to get item."); + + BSTR location_path; + CHECK_HRESULT(location->get_Path(&location_path), "Failed to get location path."); + if (wcscmp(location_path, kSiteName) == 0) { + success = true; + break; + } + + SysFreeString(location_path); + location->Release(); + location = NULL; + } + + if (i == count) success = true; + } while (false); + + if (!success) { + break; + } + + if (location != NULL) { + Log(EVENT_CRITICAL, "Location already exists. Skip."); + result = true; + break; + } + + CHECK_HRESULT(location_collection->AddLocation(kSiteName, &location), + "Failed to add location."); + CHECK_HRESULT(location->AddConfigSection(L"system.webServer/handlers", &handlers), + "Failed to add config section."); + CHECK_HRESULT(SetProperty(handlers, L"accessPolicy", L"Read, Execute, Script"), + "Failed to set accessPolicy."); + + CHECK_HRESULT(handlers->get_Collection(&handlers_collection), + "Failed to get handlers collection."); + CHECK_HRESULT(handlers_collection->CreateNewElement(L"remove", &remove_element), + "Failed to create remove element."); + CHECK_BOOL(SetProperty(remove_element, L"name", L"CGI-exe"), + "Failed to set name of remove element."); + CHECK_HRESULT(handlers_collection->AddElement(remove_element), + "Failed to add remove element."); + + CHECK_HRESULT(handlers_collection->CreateNewElement(L"add", &add_element), + "Failed to create add element."); + CHECK_BOOL(SetProperty(add_element, L"name", L"CGI-exe"), + "Failed to set name of add element."); + CHECK_BOOL(SetProperty(add_element, L"path", L"*.cgi"), + "Failed to set path of add element."); + CHECK_BOOL(SetProperty(add_element, L"verb", L"*"), + "Failed to set verb of element."); + CHECK_BOOL(SetProperty(add_element, L"modules", L"CgiModule"), + "Failed to set modules of add element."); + CHECK_BOOL(SetProperty(add_element, L"scriptProcessor", cgi_path), + "Failed to set scriptProcessor of add element."); + CHECK_BOOL(SetProperty(add_element, L"resourceType", L"File"), + "Failed to set resourceType of add element."); + CHECK_BOOL(SetProperty(add_element, L"requireAccess", L"Execute"), + "Failed to set requireAccess of add element."); + CHECK_BOOL(SetProperty(add_element, L"allowPathInfo", L"true"), + "Failed to set naallowPathInfome of add element."); + CHECK_HRESULT(handlers_collection->AddElement(add_element), + "Failed to add add element."); + + Log(EVENT_NORMAL, "Admin Console site customized successfully."); + result = true; + } while (false); + + return result; +} + + +bool Iis7Config::RemoveAdminConsoleCustomization(IAppHostWritableAdminManager* manager) { + IAppHostConfigManager* config_manager = NULL; + IAppHostConfigFile * config_file = NULL; + IAppHostConfigLocationCollection* location_collection = NULL; + IAppHostConfigLocation* location = NULL; + + bool result = false; + do { + CHECK_HRESULT(manager->get_ConfigManager(&config_manager), "Failed to get config manager."); + CHECK_HRESULT(config_manager->GetConfigFile(L"MACHINE/WEBROOT/APPHOST", &config_file), "Failed to get config file."); + CHECK_HRESULT(config_file->get_Locations(&location_collection), "Failed to get location collection."); + + bool success = false; + VARIANT idx; + do { + // Get number of children. + DWORD count; + CHECK_HRESULT(location_collection->get_Count(&count), "Unable to get the count of collection."); + + // Iterate every child to check whether it should be removed. + USHORT i = 0; + for (; i < count; ++i) { + // Get item at index i. + idx.vt = VT_I2; + idx.iVal = i; + CHECK_HRESULT(location_collection->get_Item(idx, &location), "Unable to get item."); + + BSTR location_path; + CHECK_HRESULT(location->get_Path(&location_path), "Failed to get location path."); + if (wcscmp(location_path, kSiteName) == 0) { + success = true; + break; + } + + SysFreeString(location_path); + location->Release(); + location = NULL; + } + + if (i == count) success = true; + } while (false); + + if (!success) { + break; + } + + if (location == NULL) { + Log(EVENT_CRITICAL, "No location exists. Skip."); + result = true; + break; + } + + CHECK_HRESULT(location_collection->DeleteLocation(idx), "Failed to remove location."); + + result = true; + } while (false); + + return result; +} +*/ + +void CopyBinaries(wchar_t *srcpath, wchar_t *dstpath) +{ + wchar_t *files[] = { L"libapr-1.dll", L"libapriconv-1.dll", L"libaprutil-1.dll", + L"libxml2.dll", L"lua5.1.dll", L"pcre.dll", L"zlib1.dll", L"ModSecurityIIS.dll" }; + + for(int i = 0; i < 8; i++) + { + wchar_t src[4096]; + wchar_t dst[4096]; + + wcsncpy(src,srcpath,4096); + wcsncat(src,files[i],4096); + wcsncpy(dst,dstpath,4096); + wcsncat(dst,files[i],4096); + + if(!CopyFile(src, dst, FALSE)) + { + Iis7Config::Log(EVENT_ERROR, L"Failed to copy binary: %s %s", src, dst); + } + } +} + +void fixpath(wchar_t *srcdir) +{ + int l = wcslen(srcdir); + + if(srcdir[l-1] != '\\') + { + srcdir[l] = '\\'; + srcdir[l + 1] = 0; + } +} + +int _tmain(int argc, _TCHAR* argv[]) +{ + if(argc < 2) + return -1; + + if(wcscmp(argv[1], L"uninstall") == 0) + { + Iis7Config::UninstallFilter(); + return 0; + } + + wchar_t srcdir[4096]; + + if(argv[1][0] == '"') + wcsncpy(srcdir, &argv[1][1], 4096); + else + wcsncpy(srcdir, argv[1], 4096); + + int l = wcslen(srcdir); + + if(srcdir[l-1] == '"') + srcdir[l-1] = 0; + + fixpath(srcdir); + + wchar_t windir[4096]; + + GetWindowsDirectory(windir, 4096); + + fixpath(windir); + + wchar_t crsdir[4096]; + + crsdir[0] = windir[0]; + crsdir[1] = windir[1]; + crsdir[2] = windir[2]; + crsdir[3] = 0; + + wcsncat(crsdir, L"inetpub", 4096); + + CreateDirectory(crsdir, NULL); + + wcsncat(crsdir, L"\\wwwroot", 4096); + + CreateDirectory(crsdir, NULL); + + wchar_t zipdir[4096]; + + wcsncpy(zipdir, srcdir, 4096); + wcsncat(zipdir, L"owasp_crs.zip", 4096); + + HZIP hzip = OpenZip(crsdir, zipdir, 0, ZIP_FILENAME); + + if(hzip != 0) + { + int z = 0; + ZIPENTRYW ze; + + while(GetZipItemW(hzip, z, &ze) == ZR_OK) + { + UnzipItem(hzip, z++, ze.name, 0, ZIP_FILENAME); + } + + CloseZip(hzip); + } + + wchar_t sys32[4096]; + wchar_t syswow[4096]; + wchar_t sysnat[4096]; + + wcsncpy(sys32, windir, 4096); + wcsncpy(syswow, windir, 4096); + wcsncpy(sysnat, windir, 4096); + + wcsncat(sys32, L"system32\\inetsrv\\", 4096); + wcsncat(syswow, L"SysWow64\\inetsrv\\", 4096); + wcsncat(sysnat, L"SysNative\\inetsrv\\", 4096); + + wchar_t src32[4096]; + wchar_t src64[4096]; + + wcsncpy(src32, srcdir, 4096); + wcsncpy(src64, srcdir, 4096); + wcsncat(src32, L"x86\\", 4096); + wcsncat(src64, L"amd64\\", 4096); + + wchar_t dstconf[4096]; + + if(Iis7Config::GetAppMode() == Iis7Config::MODE_X64) + { + CopyBinaries(src32, syswow); + CopyBinaries(src64, sysnat); + wcsncpy(dstconf, sysnat, 4096); + } + else + { + CopyBinaries(src32, sys32); + wcsncpy(dstconf, sys32, 4096); + } + + wcsncat(dstconf, L"config\\schema\\ModSecurity.xml", 4096); + + wcsncat(srcdir, L"ModSecurity.xml", 4096); + + if(!CopyFile(srcdir, dstconf, FALSE)) + { + Iis7Config::Log(EVENT_ERROR, L"Failed to copy config XML: %s %s", srcdir, dstconf); + } + + wcsncat(sys32, L"ModSecurityIIS.dll", 4096); + + Iis7Config::InstallFilter(sys32); + + return 0; +} diff --git a/iis/ModSecurityIIS/Installer/Installer.vcxproj b/iis/ModSecurityIIS/Installer/Installer.vcxproj new file mode 100644 index 0000000000..c584c349b7 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.vcxproj @@ -0,0 +1,96 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C} + Win32Proj + Installer + + + + Application + true + Unicode + + + Application + false + true + Unicode + Static + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + + + Console + true + true + true + ole32.lib;oleaut32.lib;advapi32.lib + RequireAdministrator + + + + + + + + + + + + + + Create + Create + + + + + + + \ No newline at end of file diff --git a/iis/ModSecurityIIS/Installer/Installer.vcxproj.filters b/iis/ModSecurityIIS/Installer/Installer.vcxproj.filters new file mode 100644 index 0000000000..cc4e9ce41c --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/iis/ModSecurityIIS/Installer/Installer.vcxproj.user b/iis/ModSecurityIIS/Installer/Installer.vcxproj.user new file mode 100644 index 0000000000..6a61bbba94 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/Installer.vcxproj.user @@ -0,0 +1,7 @@ + + + + "c:\Program Files (x86)\ModSecurity IIS" + WindowsLocalDebugger + + \ No newline at end of file diff --git a/iis/ModSecurityIIS/Installer/ReadMe.txt b/iis/ModSecurityIIS/Installer/ReadMe.txt new file mode 100644 index 0000000000..87d59aebc8 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : Installer Project Overview +======================================================================== + +AppWizard has created this Installer application for you. + +This file contains a summary of what you will find in each of the files that +make up your Installer application. + + +Installer.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +Installer.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +Installer.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named Installer.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/iis/ModSecurityIIS/Installer/XUnzip.cpp b/iis/ModSecurityIIS/Installer/XUnzip.cpp new file mode 100644 index 0000000000..d84710a7ad --- /dev/null +++ b/iis/ModSecurityIIS/Installer/XUnzip.cpp @@ -0,0 +1,4384 @@ +// XUnzip.cpp Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +// Version 1.3: - Corrected size bug introduced by 1.2 +// +// Version 1.2: - Many bug fixes. See CodeProject article for list. +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + + +#define _USE_32BIT_TIME_T //+++1.2 + + +#define STRICT +#define WIN32_LEAN_AND_MEAN +#include "stdafx.h" +#include +#include +#include +#include +#include +#include +#include "XUnzip.h" + +#pragma warning(disable : 4996) // disable bogus deprecation warning + +// THIS FILE is almost entirely based upon code by Jean-loup Gailly +// and Mark Adler. It has been modified by Lucian Wischik. +// The original code may be found at http://www.gzip.org/zlib/ +// The original copyright text follows. +// +// +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.1.3, July 9th, 1998 +// +// Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly Mark Adler +// jloup@gzip.org madler@alumni.caltech.edu +// +// +// The data format used by the zlib library is described by RFCs (Request for +// Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt +// (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +// +// +// The 'zlib' compression library provides in-memory compression and +// decompression functions, including integrity checks of the uncompressed +// data. This version of the library supports only one compression method +// (deflation) but other algorithms will be added later and will have the same +// stream interface. +// +// Compression can be done in a single step if the buffers are large +// enough (for example if an input file is mmap'ed), or can be done by +// repeated calls of the compression function. In the latter case, the +// application must provide more input and/or consume the output +// (providing more output space) before each call. +// +// The library also supports reading and writing files in gzip (.gz) format +// with an interface similar to that of stdio. +// +// The library does not install any signal handler. The decoder checks +// the consistency of the compressed data, so the library should never +// crash even in case of corrupted input. +// +// for more info about .ZIP format, see ftp://ftp.cdrom.com/pub/infozip/doc/appnote-970311-iz.zip +// PkWare has also a specification at ftp://ftp.pkware.com/probdesc.zip + +#define zmalloc(len) malloc(len) + +#define zfree(p) free(p) + +/* +void *zmalloc(unsigned int len) +{ char *buf = new char[len+32]; + for (int i=0; i<16; i++) + { buf[i]=i; + buf[len+31-i]=i; + } + *((unsigned int*)buf) = len; + char c[1000]; wsprintf(c,"malloc 0x%lx - %lu",buf+16,len); + OutputDebugString(c); + return buf+16; +} + +void zfree(void *buf) +{ char c[1000]; wsprintf(c,"free 0x%lx",buf); + OutputDebugString(c); + char *p = ((char*)buf)-16; + unsigned int len = *((unsigned int*)p); + bool blown=false; + for (int i=0; i<16; i++) + { char lo = p[i]; + char hi = p[len+31-i]; + if (hi!=i || (lo!=i && i>4)) blown=true; + } + if (blown) + { OutputDebugString("BLOWN!!!"); + } + delete[] p; +} +*/ + +#pragma warning(disable : 4702) // unreachable code + +static ZRESULT zopenerror = ZR_OK; //+++1.2 + +typedef struct tm_unz_s +{ unsigned int tm_sec; // seconds after the minute - [0,59] + unsigned int tm_min; // minutes after the hour - [0,59] + unsigned int tm_hour; // hours since midnight - [0,23] + unsigned int tm_mday; // day of the month - [1,31] + unsigned int tm_mon; // months since January - [0,11] + unsigned int tm_year; // years - [1980..2044] +} tm_unz; + + +// unz_global_info structure contain global data about the ZIPfile +typedef struct unz_global_info_s +{ unsigned long number_entry; // total number of entries in the central dir on this disk + unsigned long size_comment; // size of the global comment of the zipfile +} unz_global_info; + +// unz_file_info contain information about a file in the zipfile +typedef struct unz_file_info_s +{ unsigned long version; // version made by 2 bytes + unsigned long version_needed; // version needed to extract 2 bytes + unsigned long flag; // general purpose bit flag 2 bytes + unsigned long compression_method; // compression method 2 bytes + unsigned long dosDate; // last mod file date in Dos fmt 4 bytes + unsigned long crc; // crc-32 4 bytes + unsigned long compressed_size; // compressed size 4 bytes + unsigned long uncompressed_size; // uncompressed size 4 bytes + unsigned long size_filename; // filename length 2 bytes + unsigned long size_file_extra; // extra field length 2 bytes + unsigned long size_file_comment; // file comment length 2 bytes + unsigned long disk_num_start; // disk number start 2 bytes + unsigned long internal_fa; // internal file attributes 2 bytes + unsigned long external_fa; // external file attributes 4 bytes + tm_unz tmu_date; +} unz_file_info; + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + + + + + + + +#define ZLIB_VERSION "1.1.3" + + +// Allowed flush values; see deflate() for details +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + + +// compression levels +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) + +// compression strategy; see deflateInit2() for details +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +// Possible values of the data_type field +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 + +// The deflate compression method (the only one supported in this version) +#define Z_DEFLATED 8 + +// for initializing zalloc, zfree, opaque +#define Z_NULL 0 + +// case sensitivity when searching for filenames +#define CASE_SENSITIVE 1 +#define CASE_INSENSITIVE 2 + + +// Return codes for the compression/decompression functions. Negative +// values are errors, positive values are used for special but normal events. +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) + + + +// Basic data types +typedef unsigned char Byte; // 8 bits +typedef unsigned int uInt; // 16 bits or more +typedef unsigned long uLong; // 32 bits or more +typedef void *voidpf; +typedef void *voidp; +typedef long z_off_t; + + + + + + + + + + + + +typedef voidpf (*alloc_func) (voidpf opaque, uInt items, uInt size); +typedef void (*free_func) (voidpf opaque, voidpf address); + +struct internal_state; + +typedef struct z_stream_s { + Byte *next_in; // next input byte + uInt avail_in; // number of bytes available at next_in + uLong total_in; // total nb of input bytes read so far + + Byte *next_out; // next output byte should be put there + uInt avail_out; // remaining free space at next_out + uLong total_out; // total nb of bytes output so far + + char *msg; // last error message, NULL if no error + struct internal_state *state; // not visible by applications + + alloc_func zalloc; // used to allocate the internal state + free_func zfree; // used to free the internal state + voidpf opaque; // private data object passed to zalloc and zfree + + int data_type; // best guess about the data type: ascii or binary + uLong adler; // adler32 value of the uncompressed data + uLong reserved; // reserved for future use +} z_stream; + +typedef z_stream *z_streamp; + + +// The application must update next_in and avail_in when avail_in has +// dropped to zero. It must update next_out and avail_out when avail_out +// has dropped to zero. The application must initialize zalloc, zfree and +// opaque before calling the init function. All other fields are set by the +// compression library and must not be updated by the application. +// +// The opaque value provided by the application will be passed as the first +// parameter for calls of zalloc and zfree. This can be useful for custom +// memory management. The compression library attaches no meaning to the +// opaque value. +// +// zalloc must return Z_NULL if there is not enough memory for the object. +// If zlib is used in a multi-threaded application, zalloc and zfree must be +// thread safe. +// +// The fields total_in and total_out can be used for statistics or +// progress reports. After compression, total_in holds the total size of +// the uncompressed data and may be saved for use in the decompressor +// (particularly if the decompressor wants to decompress everything in +// a single step). +// + + +// basic functions + +const char *zlibVersion (); +// The application can compare zlibVersion and ZLIB_VERSION for consistency. +// If the first character differs, the library code actually used is +// not compatible with the zlib.h header file used by the application. +// This check is automatically made by inflateInit. + + + + + + +int inflate (z_streamp strm, int flush); +// +// inflate decompresses as much data as possible, and stops when the input +// buffer becomes empty or the output buffer becomes full. It may some +// introduce some output latency (reading input without producing any output) +// except when forced to flush. +// +// The detailed semantics are as follows. inflate performs one or both of the +// following actions: +// +// - Decompress more input starting at next_in and update next_in and avail_in +// accordingly. If not all input can be processed (because there is not +// enough room in the output buffer), next_in is updated and processing +// will resume at this point for the next call of inflate(). +// +// - Provide more output starting at next_out and update next_out and avail_out +// accordingly. inflate() provides as much output as possible, until there +// is no more input data or no more space in the output buffer (see below +// about the flush parameter). +// +// Before the call of inflate(), the application should ensure that at least +// one of the actions is possible, by providing more input and/or consuming +// more output, and updating the next_* and avail_* values accordingly. +// The application can consume the uncompressed output when it wants, for +// example when the output buffer is full (avail_out == 0), or after each +// call of inflate(). If inflate returns Z_OK and with zero avail_out, it +// must be called again after making room in the output buffer because there +// might be more output pending. +// +// If the parameter flush is set to Z_SYNC_FLUSH, inflate flushes as much +// output as possible to the output buffer. The flushing behavior of inflate is +// not specified for values of the flush parameter other than Z_SYNC_FLUSH +// and Z_FINISH, but the current implementation actually flushes as much output +// as possible anyway. +// +// inflate() should normally be called until it returns Z_STREAM_END or an +// error. However if all decompression is to be performed in a single step +// (a single call of inflate), the parameter flush should be set to +// Z_FINISH. In this case all pending input is processed and all pending +// output is flushed; avail_out must be large enough to hold all the +// uncompressed data. (The size of the uncompressed data may have been saved +// by the compressor for this purpose.) The next operation on this stream must +// be inflateEnd to deallocate the decompression state. The use of Z_FINISH +// is never required, but can be used to inform inflate that a faster routine +// may be used for the single inflate() call. +// +// If a preset dictionary is needed at this point (see inflateSetDictionary +// below), inflate sets strm-adler to the adler32 checksum of the +// dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise +// it sets strm->adler to the adler32 checksum of all output produced +// so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or +// an error code as described below. At the end of the stream, inflate() +// checks that its computed adler32 checksum is equal to that saved by the +// compressor and returns Z_STREAM_END only if the checksum is correct. +// +// inflate() returns Z_OK if some progress has been made (more input processed +// or more output produced), Z_STREAM_END if the end of the compressed data has +// been reached and all uncompressed output has been produced, Z_NEED_DICT if a +// preset dictionary is needed at this point, Z_DATA_ERROR if the input data was +// corrupted (input stream not conforming to the zlib format or incorrect +// adler32 checksum), Z_STREAM_ERROR if the stream structure was inconsistent +// (for example if next_in or next_out was NULL), Z_MEM_ERROR if there was not +// enough memory, Z_BUF_ERROR if no progress is possible or if there was not +// enough room in the output buffer when Z_FINISH is used. In the Z_DATA_ERROR +// case, the application may then call inflateSync to look for a good +// compression block. +// + + +int inflateEnd (z_streamp strm); +// +// All dynamically allocated data structures for this stream are freed. +// This function discards any unprocessed input and does not flush any +// pending output. +// +// inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state +// was inconsistent. In the error case, msg may be set but then points to a +// static string (which must not be deallocated). + + // Advanced functions + +// The following functions are needed only in some special applications. + + + + + +int inflateSetDictionary (z_streamp strm, + const Byte *dictionary, + uInt dictLength); +// +// Initializes the decompression dictionary from the given uncompressed byte +// sequence. This function must be called immediately after a call of inflate +// if this call returned Z_NEED_DICT. The dictionary chosen by the compressor +// can be determined from the Adler32 value returned by this call of +// inflate. The compressor and decompressor must use exactly the same +// dictionary. +// +// inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a +// parameter is invalid (such as NULL dictionary) or the stream state is +// inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the +// expected one (incorrect Adler32 value). inflateSetDictionary does not +// perform any decompression: this will be done by subsequent calls of +// inflate(). + + +int inflateSync (z_streamp strm); +// +// Skips invalid compressed data until a full flush point can be found, or until all +// available input is skipped. No output is provided. +// +// inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR +// if no more input was provided, Z_DATA_ERROR if no flush point has been found, +// or Z_STREAM_ERROR if the stream structure was inconsistent. In the success +// case, the application may save the current current value of total_in which +// indicates where valid compressed data was found. In the error case, the +// application may repeatedly call inflateSync, providing more input each time, +// until success or end of the input data. + + +int inflateReset (z_streamp strm); +// This function is equivalent to inflateEnd followed by inflateInit, +// but does not free and reallocate all the internal decompression state. +// The stream will keep attributes that may have been set by inflateInit2. +// +// inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source +// stream state was inconsistent (such as zalloc or state being NULL). +// + + + +// checksum functions +// These functions are not related to compression but are exported +// anyway because they might be useful in applications using the +// compression library. + +uLong adler32 (uLong adler, const Byte *buf, uInt len); +// Update a running Adler-32 checksum with the bytes buf[0..len-1] and +// return the updated checksum. If buf is NULL, this function returns +// the required initial value for the checksum. +// An Adler-32 checksum is almost as reliable as a CRC32 but can be computed +// much faster. Usage example: +// +// uLong adler = adler32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// adler = adler32(adler, buffer, length); +// } +// if (adler != original_adler) error(); + +uLong ucrc32 (uLong crc, const Byte *buf, uInt len); +// Update a running crc with the bytes buf[0..len-1] and return the updated +// crc. If buf is NULL, this function returns the required initial value +// for the crc. Pre- and post-conditioning (one's complement) is performed +// within this function so it shouldn't be done by the application. +// Usage example: +// +// uLong crc = crc32(0L, Z_NULL, 0); +// +// while (read_buffer(buffer, length) != EOF) { +// crc = crc32(crc, buffer, length); +// } +// if (crc != original_crc) error(); + + + + +const char *zError (int err); +int inflateSyncPoint (z_streamp z); +const uLong *get_crc_table (void); + + + +typedef unsigned char uch; +typedef uch uchf; +typedef unsigned short ush; +typedef ush ushf; +typedef unsigned long ulg; + + + +const char * const z_errmsg[10] = { // indexed by 2-zlib_error +"need dictionary", // Z_NEED_DICT 2 +"stream end", // Z_STREAM_END 1 +"", // Z_OK 0 +"file error", // Z_ERRNO (-1) +"stream error", // Z_STREAM_ERROR (-2) +"data error", // Z_DATA_ERROR (-3) +"insufficient memory", // Z_MEM_ERROR (-4) +"buffer error", // Z_BUF_ERROR (-5) +"incompatible version",// Z_VERSION_ERROR (-6) +""}; + + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +// To be used only when the state is known to be valid + + // common constants + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + +#define PRESET_DICT 0x20 // preset dictionary flag in zlib header + + // target dependencies + +#define OS_CODE 0x0b // Window 95 & Windows NT + + + + // functions + +#define zmemzero(dest, len) memset(dest, 0, len) + +// Diagnostic functions +#undef Assert +#undef Trace +#undef Tracev +#undef Tracevv +#undef Tracec +#undef Tracecv + +#ifdef DEBUG + + int z_verbose = 0; + void z_error (char *m) {fprintf(stderr, "%s\n", m); exit(1);} + +#define Assert(cond,msg) {if(!(cond)) z_error(msg);} +#define Trace(x) {if (z_verbose>=0) fprintf x ;} +#define Tracev(x) {if (z_verbose>0) fprintf x ;} +#define Tracevv(x) {if (z_verbose>1) fprintf x ;} +#define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +#define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} + +#else + +#ifndef __noop +#if _MSC_VER < 1300 +#define __noop ((void)0) +#endif +#endif + +#define Assert(cond,msg) __noop +#define Trace(x) __noop +#define Tracev(x) __noop +#define Tracevv(x) __noop +#define Tracec(c,x) __noop +#define Tracecv(c,x) __noop + +#endif + + +typedef uLong (*check_func) (uLong check, const Byte *buf, uInt len); +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size); +void zcfree (voidpf opaque, voidpf ptr); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) + +//void ZFREE(z_streamp strm,voidpf addr) +//{ *((strm)->zfree))((strm)->opaque, addr); +//} + +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + + + + +// Huffman code lookup table entry--this entry is four bytes for machines +// that have 16-bit pointers (e.g. PC's in the small or medium model). + + +typedef struct inflate_huft_s inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; // number of extra bits or operation + Byte Bits; // number of bits in this code or subcode + } what; + uInt pad; // pad structure to a power of 2 (4 bytes for + } word; // 16-bit, 8 bytes for 32-bit int's) + uInt base; // literal, length base, distance base, or table offset +}; + +// Maximum size of dynamic tree. The maximum found in a long but non- +// exhaustive search was 1004 huft structures (850 for length/literals +// and 154 for distances, the latter actually the result of an +// exhaustive search). The actual maximum is not known, but the +// value below is more than safe. +#define MANY 1440 + +int inflate_trees_bits ( + uInt *, // 19 code lengths + uInt *, // bits tree desired/actual depth + inflate_huft * *, // bits tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_dynamic ( + uInt, // number of literal/length codes + uInt, // number of distance codes + uInt *, // that many (total) code lengths + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + inflate_huft * *, // literal/length tree result + inflate_huft * *, // distance tree result + inflate_huft *, // space for trees + z_streamp); // for messages + +int inflate_trees_fixed ( + uInt *, // literal desired/actual bit depth + uInt *, // distance desired/actual bit depth + const inflate_huft * *, // literal/length tree result + const inflate_huft * *, // distance tree result + z_streamp); // for memory allocation + + + + + +struct inflate_blocks_state; +typedef struct inflate_blocks_state inflate_blocks_statef; + +inflate_blocks_statef * inflate_blocks_new ( + z_streamp z, + check_func c, // check function + uInt w); // window size + +int inflate_blocks ( + inflate_blocks_statef *, + z_streamp , + int); // initial return code + +void inflate_blocks_reset ( + inflate_blocks_statef *, + z_streamp , + uLong *); // check value on output + +int inflate_blocks_free ( + inflate_blocks_statef *, + z_streamp); + +void inflate_set_dictionary ( + inflate_blocks_statef *s, + const Byte *d, // dictionary + uInt n); // dictionary length + +int inflate_blocks_sync_point ( + inflate_blocks_statef *s); + + + + +struct inflate_codes_state; +typedef struct inflate_codes_state inflate_codes_statef; + +inflate_codes_statef *inflate_codes_new ( + uInt, uInt, + const inflate_huft *, const inflate_huft *, + z_streamp ); + +int inflate_codes ( + inflate_blocks_statef *, + z_streamp , + int); + +void inflate_codes_free ( + inflate_codes_statef *, + z_streamp ); + + + + +typedef enum { + IBM_TYPE, // get type bits (3, including end bit) + IBM_LENS, // get lengths for stored + IBM_STORED, // processing stored block + IBM_TABLE, // get table lengths + IBM_BTREE, // get bit lengths tree for a dynamic block + IBM_DTREE, // get length, distance trees for a dynamic block + IBM_CODES, // processing fixed or dynamic block + IBM_DRY, // output remaining window bytes + IBM_DONE, // finished last block, done + IBM_BAD} // got a data error--stuck here +inflate_block_mode; + +// inflate blocks semi-private state +struct inflate_blocks_state { + + // mode + inflate_block_mode mode; // current inflate_block mode + + // mode dependent information + union { + uInt left; // if STORED, bytes left to copy + struct { + uInt table; // table lengths (14 bits) + uInt index; // index into blens (or border) + uInt *blens; // bit lengths of codes + uInt bb; // bit length tree depth + inflate_huft *tb; // bit length decoding tree + } trees; // if DTREE, decoding info for trees + struct { + inflate_codes_statef + *codes; + } decode; // if CODES, current state + } sub; // submode + uInt last; // true if this block is the last block + + // mode independent information + uInt bitk; // bits in bit buffer + uLong bitb; // bit buffer + inflate_huft *hufts; // single malloc for tree space + Byte *window; // sliding window + Byte *end; // one byte after sliding window + Byte *read; // window read pointer + Byte *write; // window write pointer + check_func checkfn; // check function + uLong check; // check on output + +}; + + +// defines for inflate input/output +// update pointers and return +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=(uLong)(p-z->next_in);z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +// get bytes and bits +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<>=(j);k-=(j);} +// output bytes +#define WAVAIL (uInt)(qread?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=(uInt)WAVAIL;m;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=(uInt)WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +// load local pointers +#define LOAD {LOADIN LOADOUT} + +// masks for lower bits (size given to avoid silly warnings with Visual C++) +// And'ing with mask[n] masks the lower n bits +const uInt inflate_mask[17] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +// copy as much as possible from the sliding window to the output area +int inflate_flush (inflate_blocks_statef *, z_streamp, int); + +int inflate_fast (uInt, uInt, const inflate_huft *, const inflate_huft *, inflate_blocks_statef *, z_streamp ); + + + +const uInt fixed_bl = 9; +const uInt fixed_bd = 5; +const inflate_huft fixed_tl[] = { + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},192}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},160}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},224}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},144}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},208}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},176}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},240}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},200}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},168}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},232}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},152}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},216}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},184}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},248}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},196}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},164}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},228}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},148}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},212}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},180}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},244}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},204}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},172}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},236}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},156}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},220}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},188}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},252}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},194}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},162}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},226}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},146}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},210}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},178}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},242}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},202}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},170}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},234}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},154}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},218}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},186}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},250}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},198}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},166}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},230}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},150}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},214}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},182}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},246}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},206}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},174}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},238}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},158}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},222}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},190}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},254}, + {{{96,7}},256}, {{{0,8}},80}, {{{0,8}},16}, {{{84,8}},115}, + {{{82,7}},31}, {{{0,8}},112}, {{{0,8}},48}, {{{0,9}},193}, + {{{80,7}},10}, {{{0,8}},96}, {{{0,8}},32}, {{{0,9}},161}, + {{{0,8}},0}, {{{0,8}},128}, {{{0,8}},64}, {{{0,9}},225}, + {{{80,7}},6}, {{{0,8}},88}, {{{0,8}},24}, {{{0,9}},145}, + {{{83,7}},59}, {{{0,8}},120}, {{{0,8}},56}, {{{0,9}},209}, + {{{81,7}},17}, {{{0,8}},104}, {{{0,8}},40}, {{{0,9}},177}, + {{{0,8}},8}, {{{0,8}},136}, {{{0,8}},72}, {{{0,9}},241}, + {{{80,7}},4}, {{{0,8}},84}, {{{0,8}},20}, {{{85,8}},227}, + {{{83,7}},43}, {{{0,8}},116}, {{{0,8}},52}, {{{0,9}},201}, + {{{81,7}},13}, {{{0,8}},100}, {{{0,8}},36}, {{{0,9}},169}, + {{{0,8}},4}, {{{0,8}},132}, {{{0,8}},68}, {{{0,9}},233}, + {{{80,7}},8}, {{{0,8}},92}, {{{0,8}},28}, {{{0,9}},153}, + {{{84,7}},83}, {{{0,8}},124}, {{{0,8}},60}, {{{0,9}},217}, + {{{82,7}},23}, {{{0,8}},108}, {{{0,8}},44}, {{{0,9}},185}, + {{{0,8}},12}, {{{0,8}},140}, {{{0,8}},76}, {{{0,9}},249}, + {{{80,7}},3}, {{{0,8}},82}, {{{0,8}},18}, {{{85,8}},163}, + {{{83,7}},35}, {{{0,8}},114}, {{{0,8}},50}, {{{0,9}},197}, + {{{81,7}},11}, {{{0,8}},98}, {{{0,8}},34}, {{{0,9}},165}, + {{{0,8}},2}, {{{0,8}},130}, {{{0,8}},66}, {{{0,9}},229}, + {{{80,7}},7}, {{{0,8}},90}, {{{0,8}},26}, {{{0,9}},149}, + {{{84,7}},67}, {{{0,8}},122}, {{{0,8}},58}, {{{0,9}},213}, + {{{82,7}},19}, {{{0,8}},106}, {{{0,8}},42}, {{{0,9}},181}, + {{{0,8}},10}, {{{0,8}},138}, {{{0,8}},74}, {{{0,9}},245}, + {{{80,7}},5}, {{{0,8}},86}, {{{0,8}},22}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},118}, {{{0,8}},54}, {{{0,9}},205}, + {{{81,7}},15}, {{{0,8}},102}, {{{0,8}},38}, {{{0,9}},173}, + {{{0,8}},6}, {{{0,8}},134}, {{{0,8}},70}, {{{0,9}},237}, + {{{80,7}},9}, {{{0,8}},94}, {{{0,8}},30}, {{{0,9}},157}, + {{{84,7}},99}, {{{0,8}},126}, {{{0,8}},62}, {{{0,9}},221}, + {{{82,7}},27}, {{{0,8}},110}, {{{0,8}},46}, {{{0,9}},189}, + {{{0,8}},14}, {{{0,8}},142}, {{{0,8}},78}, {{{0,9}},253}, + {{{96,7}},256}, {{{0,8}},81}, {{{0,8}},17}, {{{85,8}},131}, + {{{82,7}},31}, {{{0,8}},113}, {{{0,8}},49}, {{{0,9}},195}, + {{{80,7}},10}, {{{0,8}},97}, {{{0,8}},33}, {{{0,9}},163}, + {{{0,8}},1}, {{{0,8}},129}, {{{0,8}},65}, {{{0,9}},227}, + {{{80,7}},6}, {{{0,8}},89}, {{{0,8}},25}, {{{0,9}},147}, + {{{83,7}},59}, {{{0,8}},121}, {{{0,8}},57}, {{{0,9}},211}, + {{{81,7}},17}, {{{0,8}},105}, {{{0,8}},41}, {{{0,9}},179}, + {{{0,8}},9}, {{{0,8}},137}, {{{0,8}},73}, {{{0,9}},243}, + {{{80,7}},4}, {{{0,8}},85}, {{{0,8}},21}, {{{80,8}},258}, + {{{83,7}},43}, {{{0,8}},117}, {{{0,8}},53}, {{{0,9}},203}, + {{{81,7}},13}, {{{0,8}},101}, {{{0,8}},37}, {{{0,9}},171}, + {{{0,8}},5}, {{{0,8}},133}, {{{0,8}},69}, {{{0,9}},235}, + {{{80,7}},8}, {{{0,8}},93}, {{{0,8}},29}, {{{0,9}},155}, + {{{84,7}},83}, {{{0,8}},125}, {{{0,8}},61}, {{{0,9}},219}, + {{{82,7}},23}, {{{0,8}},109}, {{{0,8}},45}, {{{0,9}},187}, + {{{0,8}},13}, {{{0,8}},141}, {{{0,8}},77}, {{{0,9}},251}, + {{{80,7}},3}, {{{0,8}},83}, {{{0,8}},19}, {{{85,8}},195}, + {{{83,7}},35}, {{{0,8}},115}, {{{0,8}},51}, {{{0,9}},199}, + {{{81,7}},11}, {{{0,8}},99}, {{{0,8}},35}, {{{0,9}},167}, + {{{0,8}},3}, {{{0,8}},131}, {{{0,8}},67}, {{{0,9}},231}, + {{{80,7}},7}, {{{0,8}},91}, {{{0,8}},27}, {{{0,9}},151}, + {{{84,7}},67}, {{{0,8}},123}, {{{0,8}},59}, {{{0,9}},215}, + {{{82,7}},19}, {{{0,8}},107}, {{{0,8}},43}, {{{0,9}},183}, + {{{0,8}},11}, {{{0,8}},139}, {{{0,8}},75}, {{{0,9}},247}, + {{{80,7}},5}, {{{0,8}},87}, {{{0,8}},23}, {{{192,8}},0}, + {{{83,7}},51}, {{{0,8}},119}, {{{0,8}},55}, {{{0,9}},207}, + {{{81,7}},15}, {{{0,8}},103}, {{{0,8}},39}, {{{0,9}},175}, + {{{0,8}},7}, {{{0,8}},135}, {{{0,8}},71}, {{{0,9}},239}, + {{{80,7}},9}, {{{0,8}},95}, {{{0,8}},31}, {{{0,9}},159}, + {{{84,7}},99}, {{{0,8}},127}, {{{0,8}},63}, {{{0,9}},223}, + {{{82,7}},27}, {{{0,8}},111}, {{{0,8}},47}, {{{0,9}},191}, + {{{0,8}},15}, {{{0,8}},143}, {{{0,8}},79}, {{{0,9}},255} + }; +const inflate_huft fixed_td[] = { + {{{80,5}},1}, {{{87,5}},257}, {{{83,5}},17}, {{{91,5}},4097}, + {{{81,5}},5}, {{{89,5}},1025}, {{{85,5}},65}, {{{93,5}},16385}, + {{{80,5}},3}, {{{88,5}},513}, {{{84,5}},33}, {{{92,5}},8193}, + {{{82,5}},9}, {{{90,5}},2049}, {{{86,5}},129}, {{{192,5}},24577}, + {{{80,5}},2}, {{{87,5}},385}, {{{83,5}},25}, {{{91,5}},6145}, + {{{81,5}},7}, {{{89,5}},1537}, {{{85,5}},97}, {{{93,5}},24577}, + {{{80,5}},4}, {{{88,5}},769}, {{{84,5}},49}, {{{92,5}},12289}, + {{{82,5}},13}, {{{90,5}},3073}, {{{86,5}},193}, {{{192,5}},24577} + }; + + + + + + + +// copy as much as possible from the sliding window to the output area +int inflate_flush(inflate_blocks_statef *s,z_streamp z,int r) +{ + uInt n; + Byte *p; + Byte *q; + + // local copies of source and destination pointers + p = z->next_out; + q = s->read; + + // compute number of bytes to copy as far as end of window + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy as far as end of window + if (n!=0) // check for n!=0 to avoid waking up CodeGuard + { memcpy(p, q, n); + p += n; + q += n; + } + + // see if more to copy at beginning of window + if (q == s->end) + { + // wrap pointers + q = s->window; + if (s->write == s->end) + s->write = s->window; + + // compute bytes to copy + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + // update counters + z->avail_out -= n; + z->total_out += n; + + // update check information + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(s->check, q, n); + + // copy + memcpy(p, q, n); + p += n; + q += n; + } + + // update pointers + z->next_out = p; + s->read = q; + + // done + return r; +} + + + + + + +// simplify the use of the inflate_huft type with some defines +#define exop word.what.Exop +#define bits word.what.Bits + +typedef enum { // waiting for "i:"=input, "o:"=output, "x:"=nothing + START, // x: set up for LEN + LEN, // i: get length/literal/eob next + LENEXT, // i: getting length extra (have base) + DIST, // i: get distance next + DISTEXT, // i: getting distance extra + COPY, // o: copying bytes in window, waiting for space + LIT, // o: got literal, waiting for output space + WASH, // o: got eob, possibly still output waiting + END, // x: got eob and all data flushed + BADCODE} // x: got error +inflate_codes_mode; + +// inflate codes private state +struct inflate_codes_state { + + // mode + inflate_codes_mode mode; // current inflate_codes mode + + // mode dependent information + uInt len; + union { + struct { + const inflate_huft *tree; // pointer into tree + uInt need; // bits needed + } code; // if LEN or DIST, where in tree + uInt lit; // if LIT, literal + struct { + uInt get; // bits to get for extra + uInt dist; // distance back to copy from + } copy; // if EXT or COPY, where and how much + } sub; // submode + + // mode independent information + Byte lbits; // ltree bits decoded per branch + Byte dbits; // dtree bits decoder per branch + const inflate_huft *ltree; // literal/length/eob tree + const inflate_huft *dtree; // distance tree + +}; + + +inflate_codes_statef *inflate_codes_new( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +z_streamp z) +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +int inflate_codes(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt j; // temporary storage + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + Byte *f; // pointer to copy strings from + inflate_codes_statef *c = s->sub.decode.codes; // codes state + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input and output based on current state + for(;;) switch (c->mode) + { // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif // !SLOW + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: // i: get length/literal/eob next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) // literal + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) // length + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + if (e & 32) // end of block + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: // i: getting length extra (have base) + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: // i: get distance next + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) // distance + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) // next table + { + c->sub.code.need = e; + c->sub.code.tree = t + t->base; + break; + } + c->mode = BADCODE; // invalid code + z->msg = (char*)"invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: // i: getting distance extra + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: // o: got literal, waiting for output space + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) // return unused byte, if any + { + Assert(k < 16, "inflate_codes grabbed too many bytes"); + k -= 8; + n++; + p--; // can always return one + } + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: // x: got error + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +void inflate_codes_free(inflate_codes_statef *c,z_streamp z) +{ ZFREE(z, c); + Tracev((stderr, "inflate: codes free\n")); +} + + + +// infblock.c -- interpret and process block types to last block +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + + +// Table for deflate from PKZIP's appnote.txt. +const uInt border[] = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +// +// Notes beyond the 1.93a appnote.txt: +// +// 1. Distance pointers never point before the beginning of the output stream. +// 2. Distance pointers can point back across blocks, up to 32k away. +// 3. There is an implied maximum of 7 bits for the bit length table and +// 15 bits for the actual data. +// 4. If only one code exists, then it is encoded using one bit. (Zero +// would be more efficient, but perhaps a little confusing.) If two +// codes exist, they are coded using one bit each (0 and 1). +// 5. There is no way of sending zero distance codes--a dummy must be +// sent if there are none. (History: a pre 2.0 version of PKZIP would +// store blocks with no distance codes, but this was discovered to be +// too harsh a criterion.) Valid only for 1.93a. 2.04c does allow +// zero distance codes, which is sent as one code of zero bits in +// length. +// 6. There are up to 286 literal/length codes. Code 256 represents the +// end-of-block. Note however that the static length tree defines +// 288 codes just to fill out the Huffman codes. Codes 286 and 287 +// cannot be used though, since there is no length base or extra bits +// defined for them. Similarily, there are up to 30 distance codes. +// However, static trees define 32 codes (all 5 bits) to fill out the +// Huffman codes, but the last two had better not show up in the data. +// 7. Unzip can check dynamic Huffman blocks for complete code sets. +// The exception is that a single code would not be complete (see #4). +// 8. The five bits following the block type is really the number of +// literal codes sent minus 257. +// 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits +// (1+6+6). Therefore, to output three times the length, you output +// three codes (1+1+1), whereas to output four times the same length, +// you only need two codes (1+3). Hmm. +//10. In the tree reconstruction algorithm, Code = Code + Increment +// only if BitLength(i) is not zero. (Pretty obvious.) +//11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) +//12. Note: length code 284 can represent 227-258, but length code 285 +// really is 258. The last length deserves its own, short code +// since it gets used a lot in very redundant files. The length +// 258 is special since 258 - 3 (the min match length) is 255. +//13. The literal/length and distance code bit lengths are read as a +// single stream of lengths. It is possible (and advantageous) for +// a repeat code (16, 17, or 18) to go across the boundary between +// the two sets of lengths. + + +void inflate_blocks_reset(inflate_blocks_statef *s, z_streamp z, uLong *c) +{ + if (c != Z_NULL) + *c = s->check; + if (s->mode == IBM_BTREE || s->mode == IBM_DTREE) + ZFREE(z, s->sub.trees.blens); + if (s->mode == IBM_CODES) + inflate_codes_free(s->sub.decode.codes, z); + s->mode = IBM_TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + z->adler = s->check = (*s->checkfn)(0L, (const Byte *)Z_NULL, 0); + Tracev((stderr, "inflate: blocks reset\n")); +} + + +inflate_blocks_statef *inflate_blocks_new(z_streamp z, check_func c, uInt w) +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->hufts = + (inflate_huft *)ZALLOC(z, sizeof(inflate_huft), MANY)) == Z_NULL) + { + ZFREE(z, s); + return Z_NULL; + } + if ((s->window = (Byte *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s->hufts); + ZFREE(z, s); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = IBM_TYPE; + Tracev((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, Z_NULL); + return s; +} + + +int inflate_blocks(inflate_blocks_statef *s, z_streamp z, int r) +{ + uInt t; // temporary storage + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + LOAD + + // process input based on current state + for(;;) switch (s->mode) + { + case IBM_TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: // stored + Tracev((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; // go to byte boundary + DUMPBITS(t) + s->mode = IBM_LENS; // get length of stored block + break; + case 1: // fixed + Tracev((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + const inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td, z); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + } + DUMPBITS(3) + s->mode = IBM_CODES; + break; + case 2: // dynamic + Tracev((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = IBM_TABLE; + break; + case 3: // illegal + DUMPBITS(3) + s->mode = IBM_BAD; + z->msg = (char*)"invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case IBM_LENS: + NEEDBITS(32) + if ((((~b) >> 16) & 0xffff) != (b & 0xffff)) + { + s->mode = IBM_BAD; + z->msg = (char*)"invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; // dump bits + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? IBM_STORED : (s->last ? IBM_DRY : IBM_TYPE); + break; + case IBM_STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + memcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? IBM_DRY : IBM_TYPE; + break; + case IBM_TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; + // remove this section to workaround bug in pkzip + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = IBM_BAD; + z->msg = (char*)"too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } + // end remove + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if ((s->sub.trees.blens = (uInt*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = IBM_BTREE; + case IBM_BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, s->hufts, z); + if (t != Z_OK) + { + ZFREE(z, s->sub.trees.blens); + r = t; + if (r == Z_DATA_ERROR) + s->mode = IBM_BAD; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = IBM_DTREE; + case IBM_DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->bits; + c = h->base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else // c == 16..18 + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + ZFREE(z, s->sub.trees.blens); + s->mode = IBM_BAD; + z->msg = (char*)"invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; // must be <= 9 for lookahead assumptions + bd = 6; // must be <= 9 for lookahead assumptions + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, + s->hufts, z); + ZFREE(z, s->sub.trees.blens); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = IBM_BAD; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.codes = c; + } + s->mode = IBM_CODES; + case IBM_CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = IBM_TYPE; + break; + } + s->mode = IBM_DRY; + case IBM_DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = IBM_DONE; + case IBM_DONE: + r = Z_STREAM_END; + LEAVE + case IBM_BAD: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +int inflate_blocks_free(inflate_blocks_statef *s, z_streamp z) +{ + inflate_blocks_reset(s, z, Z_NULL); + ZFREE(z, s->window); + ZFREE(z, s->hufts); + ZFREE(z, s); + Tracev((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + + + +// inftrees.c -- generate Huffman trees for efficient decoding +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + + +extern const char inflate_copyright[] = + " ";//inflate 1.1.3 Copyright 1995-1998 Mark Adler "; +// If you use the zlib library in a product, an acknowledgment is welcome +// in the documentation of your product. If for some reason you cannot +// include such an acknowledgment, I would appreciate that you keep this +// copyright string in the executable of your product. + + + +int huft_build ( + uInt *, // code lengths in bits + uInt, // number of codes + uInt, // number of "simple" codes + const uInt *, // list of base values for non-simple codes + const uInt *, // list of extra bits for non-simple codes + inflate_huft **,// result: starting table + uInt *, // maximum lookup bits (returns actual) + inflate_huft *, // space for trees + uInt *, // hufts used in space + uInt * ); // space for values + +// Tables for deflate from PKZIP's appnote.txt. +const uInt cplens[31] = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + // see note #13 above about 258 +const uInt cplext[31] = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; // 112==invalid +const uInt cpdist[30] = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +const uInt cpdext[30] = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +// +// Huffman code decoding is performed using a multi-level table lookup. +// The fastest way to decode is to simply build a lookup table whose +// size is determined by the longest code. However, the time it takes +// to build this table can also be a factor if the data being decoded +// is not very long. The most common codes are necessarily the +// shortest codes, so those codes dominate the decoding time, and hence +// the speed. The idea is you can have a shorter table that decodes the +// shorter, more probable codes, and then point to subsidiary tables for +// the longer codes. The time it costs to decode the longer codes is +// then traded against the time it takes to make longer tables. +// +// This results of this trade are in the variables lbits and dbits +// below. lbits is the number of bits the first level table for literal/ +// length codes can decode in one step, and dbits is the same thing for +// the distance codes. Subsequent tables are also less than or equal to +// those sizes. These values may be adjusted either when all of the +// codes are shorter than that, in which case the longest code length in +// bits is used, or when the shortest code is *longer* than the requested +// table size, in which case the length of the shortest code in bits is +// used. +// +// There are two different values for the two tables, since they code a +// different number of possibilities each. The literal/length table +// codes 286 possible values, or in a flat code, a little over eight +// bits. The distance table codes 30 possible values, or a little less +// than five bits, flat. The optimum values for speed end up being +// about one bit more than those, so lbits is 8+1 and dbits is 5+1. +// The optimum values may differ though from machine to machine, and +// possibly even between compilers. Your mileage may vary. +// + + +// If BMAX needs to be larger than 16, then h and x[] should be uLong. +#define BMAX 15 // maximum bit length of any code + +int huft_build( +uInt *b, // code lengths in bits (all assumed <= BMAX) +uInt n, // number of codes (assumed <= 288) +uInt s, // number of simple-valued codes (0..s-1) +const uInt *d, // list of base values for non-simple codes +const uInt *e, // list of extra bits for non-simple codes +inflate_huft * *t, // result: starting table +uInt *m, // maximum lookup bits, returns actual +inflate_huft *hp, // space for trees +uInt *hn, // hufts used in space +uInt *v) // working area: values in order of bit length +// Given a list of code lengths and a maximum table size, make a set of +// tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR +// if the given code set is incomplete (the tables are still built in this +// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of +// lengths), or Z_MEM_ERROR if not enough memory. +{ + + uInt a; // counter for codes of length k + uInt c[BMAX+1]; // bit length count table + uInt f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + register uInt i; // counter, current code + register uInt j; // counter + register int k; // number of bits in current code + int l; // bits per table (returned in m) + uInt mask; // (1 << w) - 1, to avoid cc -O bug on HP + register uInt *p; // pointer into c[], b[], or v[] + inflate_huft *q; // points to current table + struct inflate_huft_s r; // table entry for structure assignment + inflate_huft *u[BMAX]; // table stack + register int w; // bits before this table == (l * h) + uInt x[BMAX+1]; // bit offsets, then code stack + uInt *xp; // pointer into x + int y; // number of dummy codes added + uInt z; // number of entries in current table + + + // Generate counts for each bit length + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4; p; // clear c[]--assume BMAX+1 is 16 + p = b; i = n; + do { + c[*p++]++; // assume all entries <= BMAX + } while (--i); + if (c[0] == n) // null input--all zero length codes + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + // Find minimum and maximum length, bound *m by those + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; // minimum code length + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; // maximum code length + if ((uInt)l > i) + l = i; + *m = l; + + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { // note that i == g from above + *xp++ = (j += *p++); + } + + + // Make a table of values in order of bit lengths + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + n = x[g]; // set n to length of v + + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = v; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = (inflate_huft *)Z_NULL; // just to keep compilers happy + q = (inflate_huft *)Z_NULL; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + + // compute minimum size table less than or equal to l bits + z = g - w; + z = z > (uInt)l ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) // try a k-w bit table + { // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = c + k; + if (j < z) + while (++j < z) // try smaller tables up to z bits + { + if ((f <<= 1) <= *++xp) + break; // enough codes to use up j bits + f -= *xp; // else deduct codes from patterns + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (*hn + z > MANY) // (note: doesn't matter for fixed) + return Z_MEM_ERROR; // not enough memory + u[h] = q = hp + *hn; + *hn += z; + + // connect to last table, if there is one + if (h) + { + x[h] = i; // save pattern for backing up + r.bits = (Byte)l; // bits to dump before this table + r.exop = (Byte)j; // bits in this table + j = i >> (w - l); + r.base = (uInt)(q - u[h-1] - j); // offset to this table + u[h-1][j] = r; // connect to last table + } + else + *t = q; // first table is returned result + } + + // set up table entry in r + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; // out of values--invalid code + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); // 256 is end-of-block + r.base = *p++; // simple code is just the value + } + else + { + r.exop = (Byte)(e[*p - s] + 16 + 64);// non-simple--look up in lists + r.base = d[*p++ - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + + + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +int inflate_trees_bits( +uInt *c, // 19 code lengths +uInt *bb, // bits tree desired/actual depth +inflate_huft * *tb, // bits tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + if ((v = (uInt*)ZALLOC(z, 19, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + r = huft_build(c, 19, 19, (uInt*)Z_NULL, (uInt*)Z_NULL, + tb, bb, hp, &hn, v); + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR || *bb == 0) + { + z->msg = (char*)"incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; +} + + +int inflate_trees_dynamic( +uInt nl, // number of literal/length codes +uInt nd, // number of distance codes +uInt *c, // that many (total) code lengths +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +inflate_huft * *tl, // literal/length tree result +inflate_huft * *td, // distance tree result +inflate_huft *hp, // space for trees +z_streamp z) // for messages +{ + int r; + uInt hn = 0; // hufts used in space + uInt *v; // work area for huft_build + + // allocate work area + if ((v = (uInt*)ZALLOC(z, 288, sizeof(uInt))) == Z_NULL) + return Z_MEM_ERROR; + + // build literal/length tree + r = huft_build(c, nl, 257, cplens, cplext, tl, bl, hp, &hn, v); + if (r != Z_OK || *bl == 0) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed literal/length tree"; + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // build distance tree + r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, hp, &hn, v); + if (r != Z_OK || (*bd == 0 && nl > 257)) + { + if (r == Z_DATA_ERROR) + z->msg = (char*)"oversubscribed distance tree"; + else if (r == Z_BUF_ERROR) { + z->msg = (char*)"incomplete distance tree"; + r = Z_DATA_ERROR; + } + else if (r != Z_MEM_ERROR) + { + z->msg = (char*)"empty distance tree with lengths"; + r = Z_DATA_ERROR; + } + ZFREE(z, v); + return r; + } + + // done + ZFREE(z, v); + return Z_OK; +} + + + + + +int inflate_trees_fixed( +uInt *bl, // literal desired/actual bit depth +uInt *bd, // distance desired/actual bit depth +const inflate_huft * * tl, // literal/length tree result +const inflate_huft * *td, // distance tree result +z_streamp ) // for memory allocation +{ + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +// inffast.c -- process literals and length/distance pairs fast +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h +// + + +//struct inflate_codes_state {int dummy;}; // for buggy compilers + + +// macros for bit input with no checking and for returning unused bytes +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<avail_in-n;c=(k>>3)>3:c;n+=c;p-=c;k-=c<<3;} + +// Called with number of bytes left to write in window at least 258 +// (the maximum string length) and number of input bytes available +// at least ten. The ten bytes are six bytes for the longest length/ +// distance pair plus four bytes for overloading the bit buffer. + +int inflate_fast( +uInt bl, uInt bd, +const inflate_huft *tl, +const inflate_huft *td, // need separate declaration for Borland C++ +inflate_blocks_statef *s, +z_streamp z) +{ + const inflate_huft *t; // temporary pointer + uInt e; // extra bits or operation + uLong b; // bit buffer + uInt k; // bits in bit buffer + Byte *p; // input data pointer + uInt n; // bytes available there + Byte *q; // output window write pointer + uInt m; // bytes to end of window or read pointer + uInt ml; // mask for literal/length tree + uInt md; // mask for distance tree + uInt c; // bytes to copy + uInt d; // distance back to copy from + Byte *r; // copy source pointer + + // load input, output, bit values + LOAD + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + GRABBITS(20) // max bits for literal/length code + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits for length + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + // decode distance base of block to copy + GRABBITS(15); // max bits for distance code + e = (t = td + ((uInt)b & md))->exop; + for (;;) { + DUMPBITS(t->bits) + if (e & 16) + { + // get extra bits to add to distance base + e &= 15; + GRABBITS(e) // get extra bits (up to 13) + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + // do the copy + m -= c; + if ((uInt)(q - s->window) >= d) // offset before dest + { // just copy + r = q - d; + *q++ = *r++; c--; // minimum count is three, + *q++ = *r++; c--; // so unroll loop a little + } + else // else offset after destination + { + e = d - (uInt)(q - s->window); // bytes from offset to end + r = s->end - e; // pointer to offset + if (c > e) // if source crosses, + { + c -= e; // copy to end of window + do { + *q++ = *r++; + } while (--e); + r = s->window; // copy rest from start of window + } + } + do { // copy all or what's left + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + { + t += t->base; + e = (t += ((uInt)b & inflate_mask[e]))->exop; + } + else + { + z->msg = (char*)"invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + break; + } + if ((e & 64) == 0) + { + t += t->base; + if ((e = (t += ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = (char*)"invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + }; + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + UNGRAB + UPDATE + return Z_OK; +} + + + + + + +// crc32.c -- compute the CRC-32 of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + + + + + +// Table of CRC-32's of all single-byte values (made by make_crc_table) +const uLong crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +const uLong * get_crc_table() +{ return (const uLong *)crc_table; +} + +#define CRC_DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); +#define CRC_DO2(buf) CRC_DO1(buf); CRC_DO1(buf); +#define CRC_DO4(buf) CRC_DO2(buf); CRC_DO2(buf); +#define CRC_DO8(buf) CRC_DO4(buf); CRC_DO4(buf); + +uLong ucrc32(uLong crc, const Byte *buf, uInt len) +{ if (buf == Z_NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {CRC_DO8(buf); len -= 8;} + if (len) do {CRC_DO1(buf);} while (--len); + return crc ^ 0xffffffffL; +} + + +// adler32.c -- compute the Adler-32 checksum of a data stream +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +// @(#) $Id$ + + +#define BASE 65521L // largest prime smaller than 65536 +#define NMAX 5552 +// NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + +#define AD_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define AD_DO2(buf,i) AD_DO1(buf,i); AD_DO1(buf,i+1); +#define AD_DO4(buf,i) AD_DO2(buf,i); AD_DO2(buf,i+2); +#define AD_DO8(buf,i) AD_DO4(buf,i); AD_DO4(buf,i+4); +#define AD_DO16(buf) AD_DO8(buf,0); AD_DO8(buf,8); + +// ========================================================================= +uLong adler32(uLong adler, const Byte *buf, uInt len) +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + AD_DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} + + + +// zutil.c -- target dependent utility functions for the compression library +// Copyright (C) 1995-1998 Jean-loup Gailly. +// For conditions of distribution and use, see copyright notice in zlib.h +// @(#) $Id$ + + + + + + +const char * zlibVersion() +{ + return ZLIB_VERSION; +} + +// exported to allow conversion of error code to string for compress() and +// uncompress() +const char * zError(int err) +{ return ERR_MSG(err); +} + + + + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) items += size - size; // make compiler happy + return (voidpf)calloc(items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + zfree(ptr); + if (opaque) return; // make compiler happy +} + + + +// inflate.c -- zlib interface to inflate modules +// Copyright (C) 1995-1998 Mark Adler +// For conditions of distribution and use, see copyright notice in zlib.h + +//struct inflate_blocks_state {int dummy;}; // for buggy compilers + +typedef enum { + IM_METHOD, // waiting for method byte + IM_FLAG, // waiting for flag byte + IM_DICT4, // four dictionary check bytes to go + IM_DICT3, // three dictionary check bytes to go + IM_DICT2, // two dictionary check bytes to go + IM_DICT1, // one dictionary check byte to go + IM_DICT0, // waiting for inflateSetDictionary + IM_BLOCKS, // decompressing blocks + IM_CHECK4, // four check bytes to go + IM_CHECK3, // three check bytes to go + IM_CHECK2, // two check bytes to go + IM_CHECK1, // one check byte to go + IM_DONE, // finished check, done + IM_BAD} // got an error--stay here +inflate_mode; + +// inflate private state +struct internal_state { + + // mode + inflate_mode mode; // current inflate mode + + // mode dependent information + union { + uInt method; // if IM_FLAGS, method byte + struct { + uLong was; // computed check value + uLong need; // stream check value + } check; // if CHECK, check values to compare + uInt marker; // if IM_BAD, inflateSync's marker bytes count + } sub; // submode + + // mode independent information + int nowrap; // flag for no wrapper + uInt wbits; // log2(window size) (8..15, defaults to 15) + inflate_blocks_statef + *blocks; // current inflate_blocks state + +}; + +int inflateReset(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? IM_BLOCKS : IM_METHOD; + inflate_blocks_reset(z->state->blocks, z, Z_NULL); + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int inflateEnd(z_streamp z) +{ + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z); + ZFREE(z, z->state); + z->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z_streamp z) +{ const char *version = ZLIB_VERSION; int stream_size = sizeof(z_stream); + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || stream_size != sizeof(z_stream)) return Z_VERSION_ERROR; + + int w = -15; // MAX_WBITS: 32K LZ77 window. + // Warning: reducing MAX_WBITS makes minigzip unable to extract .gz files created by gzip. + // The memory requirements for deflate are (in bytes): + // (1 << (windowBits+2)) + (1 << (memLevel+9)) + // that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + // plus a few kilobytes for small objects. For example, if you want to reduce + // the default memory requirements from 256K to 128K, compile with + // make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + // Of course this will generally degrade compression (there's no free lunch). + // + // The memory requirements for inflate are (in bytes) 1 << windowBits + // that is, 32K for windowBits=15 (default value) plus a few kilobytes + // for small objects. + + // initialize state + if (z == Z_NULL) return Z_STREAM_ERROR; + z->msg = Z_NULL; + if (z->zalloc == Z_NULL) + { + z->zalloc = zcalloc; + z->opaque = (voidpf)0; + } + if (z->zfree == Z_NULL) z->zfree = zcfree; + if ((z->state = (struct internal_state *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + // handle undocumented nowrap option (no zlib header or check) + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + // set window size + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + // create inflate_blocks state + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, (uInt)1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Tracev((stderr, "inflate: allocated\n")); + + // reset state + inflateReset(z); + return Z_OK; +} + + + +#define IM_NEEDBYTE {if(z->avail_in==0)return r;r=f;} +#define IM_NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z_streamp z, int f) +{ + int r; + uInt b; + + if (z == Z_NULL || z->state == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + for (;;) switch (z->state->mode) + { + case IM_METHOD: + IM_NEEDBYTE + if (((z->state->sub.method = IM_NEXTBYTE) & 0xf) != Z_DEFLATED) + { + z->state->mode = IM_BAD; + z->msg = (char*)"unknown compression method"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = IM_BAD; + z->msg = (char*)"invalid window size"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + z->state->mode = IM_FLAG; + case IM_FLAG: + IM_NEEDBYTE + b = IM_NEXTBYTE; + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect header check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib header ok\n")); + if (!(b & PRESET_DICT)) + { + z->state->mode = IM_BLOCKS; + break; + } + z->state->mode = IM_DICT4; + case IM_DICT4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_DICT3; + case IM_DICT3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_DICT2; + case IM_DICT2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_DICT1; + case IM_DICT1: + IM_NEEDBYTE; r; + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + z->adler = z->state->sub.check.need; + z->state->mode = IM_DICT0; + return Z_NEED_DICT; + case IM_DICT0: + z->state->mode = IM_BAD; + z->msg = (char*)"need dictionary"; + z->state->sub.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case IM_BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (r == Z_DATA_ERROR) + { + z->state->mode = IM_BAD; + z->state->sub.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) + r = f; + if (r != Z_STREAM_END) + return r; + r = f; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = IM_DONE; + break; + } + z->state->mode = IM_CHECK4; + case IM_CHECK4: + IM_NEEDBYTE + z->state->sub.check.need = (uLong)IM_NEXTBYTE << 24; + z->state->mode = IM_CHECK3; + case IM_CHECK3: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 16; + z->state->mode = IM_CHECK2; + case IM_CHECK2: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE << 8; + z->state->mode = IM_CHECK1; + case IM_CHECK1: + IM_NEEDBYTE + z->state->sub.check.need += (uLong)IM_NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = IM_BAD; + z->msg = (char*)"incorrect data check"; + z->state->sub.marker = 5; // can't try inflateSync + break; + } + Tracev((stderr, "inflate: zlib check ok\n")); + z->state->mode = IM_DONE; + case IM_DONE: + return Z_STREAM_END; + case IM_BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } +} + + + +#ifdef _UNICODE + +static int GetAnsiFileName(LPCWSTR name, char * buf, int nBufSize) +{ + memset(buf, 0, nBufSize); + + int n = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + name, // wide-character string + -1, // number of chars in string + buf, // buffer for new string + nBufSize, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + return n; +} + +static int GetUnicodeFileName(const char * name, LPWSTR buf, int nBufSize) +{ + memset(buf, 0, nBufSize*sizeof(TCHAR)); + + int n = MultiByteToWideChar(CP_ACP, // code page + 0, // character-type options + name, // string to map + -1, // number of bytes in string + buf, // wide-character buffer + nBufSize); // size of buffer + + return n; +} + +#endif + + +// unzip.c -- IO on .zip files using zlib +// Version 0.15 beta, Mar 19th, 1998, +// Read unzip.h for more info + + + + +#define UNZ_BUFSIZE (16384) +#define UNZ_MAXFILENAMEINZIP (256) +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = " ";//unzip 0.15 Copyright 1998 Gilles Vollant "; + +// unz_file_info_interntal contain internal info about a file in zipfile +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;// relative offset of local header 4 bytes +} unz_file_info_internal; + + +typedef struct +{ bool is_handle; // either a handle or memory + bool canseek; + // for handles: + HANDLE h; bool herr; unsigned long initial_offset; + // for memory: + void *buf; unsigned int len,pos; // if it's a memory block +} LUFILE; + + +LUFILE *lufopen(void *z,unsigned int len,DWORD flags,ZRESULT *err) +{ + if (flags!=ZIP_HANDLE && flags!=ZIP_FILENAME && flags!=ZIP_MEMORY) + { + *err=ZR_ARGS; + return NULL; + } + // + HANDLE h=0; bool canseek=false; *err=ZR_OK; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + if (flags==ZIP_HANDLE) + { + HANDLE hf = z; + + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&h,0,FALSE,DUPLICATE_SAME_ACCESS); + + if (!res) + { + *err=ZR_NODUPH; + return NULL; + } + } + else + { + h = CreateFile((const TCHAR *)z, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (h == INVALID_HANDLE_VALUE) + { + *err = ZR_NOFILE; + return NULL; + } + } + DWORD type = GetFileType(h); + canseek = (type==FILE_TYPE_DISK); + } + LUFILE *lf = new LUFILE; + if (flags==ZIP_HANDLE||flags==ZIP_FILENAME) + { + lf->is_handle=true; + lf->canseek=canseek; + lf->h=h; lf->herr=false; + lf->initial_offset=0; + if (canseek) + lf->initial_offset = SetFilePointer(h,0,NULL,FILE_CURRENT); + } + else + { + lf->is_handle=false; + lf->canseek=true; + lf->buf=z; + lf->len=len; + lf->pos=0; + lf->initial_offset=0; + } + *err=ZR_OK; + return lf; +} + + +int lufclose(LUFILE *stream) +{ if (stream==NULL) return EOF; + if (stream->is_handle) CloseHandle(stream->h); + delete stream; + return 0; +} + +int luferror(LUFILE *stream) +{ if (stream->is_handle && stream->herr) return 1; + else return 0; +} + +long int luftell(LUFILE *stream) +{ if (stream->is_handle && stream->canseek) return SetFilePointer(stream->h,0,NULL,FILE_CURRENT)-stream->initial_offset; + else if (stream->is_handle) return 0; + else return stream->pos; +} + +int lufseek(LUFILE *stream, long offset, int whence) +{ if (stream->is_handle && stream->canseek) + { if (whence==SEEK_SET) SetFilePointer(stream->h,stream->initial_offset+offset,0,FILE_BEGIN); + else if (whence==SEEK_CUR) SetFilePointer(stream->h,offset,NULL,FILE_CURRENT); + else if (whence==SEEK_END) SetFilePointer(stream->h,offset,NULL,FILE_END); + else return 19; // EINVAL + return 0; + } + else if (stream->is_handle) return 29; // ESPIPE + else + { if (whence==SEEK_SET) stream->pos=offset; + else if (whence==SEEK_CUR) stream->pos+=offset; + else if (whence==SEEK_END) stream->pos=stream->len+offset; + return 0; + } +} + + +size_t lufread(void *ptr,size_t size,size_t n,LUFILE *stream) +{ unsigned int toread = (unsigned int)(size*n); + if (stream->is_handle) + { DWORD red; BOOL res = ReadFile(stream->h,ptr,toread,&red,NULL); + if (!res) stream->herr=true; + return red/size; + } + if (stream->pos+toread > stream->len) toread = stream->len-stream->pos; + memcpy(ptr, (char*)stream->buf + stream->pos, toread); DWORD red = toread; + stream->pos += red; + return red/size; +} + + + + +// file_in_zip_read_info_s contain internal information about a file in zipfile, +// when reading and decompress it +typedef struct +{ + char *read_buffer; // internal buffer for compressed data + z_stream stream; // zLib stream structure for inflate + + uLong pos_in_zipfile; // position in byte on the zipfile, for fseek + uLong stream_initialised; // flag set if stream structure is initialised + + uLong offset_local_extrafield;// offset of the local extra field + uInt size_local_extrafield;// size of the local extra field + uLong pos_local_extrafield; // position in the local extra field in read + + uLong crc32; // crc32 of all data uncompressed + uLong crc32_wait; // crc32 we must obtain after decompress all + uLong rest_read_compressed; // number of byte to be decompressed + uLong rest_read_uncompressed;//number of byte to be obtained after decomp + LUFILE* file; // io structore of the zipfile + uLong compression_method; // compression method (0==store) + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) +} file_in_zip_read_info_s; + + +// unz_s contain internal information about the zipfile +typedef struct +{ + LUFILE* file; // io structore of the zipfile + unz_global_info gi; // public global information + uLong byte_before_the_zipfile;// byte before the zipfile, (>0 for sfx) + uLong num_file; // number of the current file in the zipfile + uLong pos_in_central_dir; // pos of the current file in the central dir + uLong current_file_ok; // flag about the usability of the current file + uLong central_pos; // position of the beginning of the central dir + + uLong size_central_dir; // size of the central directory + uLong offset_central_dir; // offset of start of central directory with respect to the starting disk number + + unz_file_info cur_file_info; // public info about the current file in zip + unz_file_info_internal cur_file_info_internal; // private info about it + file_in_zip_read_info_s* pfile_in_zip_read; // structure about the current file if we are decompressing it +} unz_s, *unzFile; + + +int unzStringFileNameCompare (const char* fileName1,const char* fileName2,int iCaseSensitivity); +// Compare two filename (fileName1,fileName2). + +z_off_t unztell (unzFile file); +// Give the current position in uncompressed data + +int unzeof (unzFile file); +// return 1 if the end of file was reached, 0 elsewhere + +int unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len); +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// +// if buf==NULL, it return the size of the local extra field +// +// if buf!=NULL, len is the size of the buffer, the extra header is copied in +// buf. +// the return value is the number of bytes copied in buf, or (if <0) +// the error code + + + +// =========================================================================== +// Read a byte from a gz_stream; update next_in and avail_in. Return EOF +// for end of file. +// IN assertion: the stream s has been sucessfully opened for reading. + +int unzlocal_getByte(LUFILE *fin,int *pi) +{ unsigned char c; + int err = (int)lufread(&c, 1, 1, fin); + if (err==1) + { *pi = (int)c; + return UNZ_OK; + } + else + { if (luferror(fin)) return UNZ_ERRNO; + else return UNZ_EOF; + } +} + + +// =========================================================================== +// Reads a long in LSB order from the given gz_stream. Sets +int unzlocal_getShort (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +int unzlocal_getLong (LUFILE *fin,uLong *pX) +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(fin,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(fin,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +// My own strcmpi / strcasecmp +int strcmpcasenosensitive_internal (const char* fileName1,const char *fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= (char)0x20; + if ((c2>='a') && (c2<='z')) + c2 -= (char)0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + + + +// +// Compare two filename (fileName1,fileName2). +// If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) +// If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi or strcasecmp) +// +int unzStringFileNameCompare (const char*fileName1,const char*fileName2,int iCaseSensitivity) +{ if (iCaseSensitivity==1) return strcmp(fileName1,fileName2); + else return strcmpcasenosensitive_internal(fileName1,fileName2); +} + +#define BUFREADCOMMENT (0x400) + + +// Locate the Central directory of a zipfile (at the end, just before +// the global comment) +uLong unzlocal_SearchCentralDir(LUFILE *fin) +{ if (lufseek(fin,0,SEEK_END) != 0) return 0; + uLong uSizeFile = luftell(fin); + + uLong uMaxBack=0xffff; // maximum size of global comment + if (uMaxBack>uSizeFile) uMaxBack = uSizeFile; + + unsigned char *buf = (unsigned char*)zmalloc(BUFREADCOMMENT+4); + if (buf==NULL) return 0; + uLong uPosFound=0; + + uLong uBackRead = 4; + while (uBackReaduMaxBack) uBackRead = uMaxBack; + else uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (lufseek(fin,uReadPos,SEEK_SET)!=0) break; + if (lufread(buf,(uInt)uReadSize,1,fin)!=1) break; + for (i=(int)uReadSize-3; (i--)>0;) + { if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { uPosFound = uReadPos+i; break; + } + } + if (uPosFound!=0) break; + } + if (buf) zfree(buf); + return uPosFound; +} + + +int unzGoToFirstFile (unzFile file); +int unzCloseCurrentFile (unzFile file); + +// Open a Zip file. +// If the zipfile cannot be opened (file don't exist or in not valid), return NULL. +// Otherwise, the return value is a unzFile Handle, usable with other unzip functions +unzFile unzOpenInternal(LUFILE *fin) +{ + zopenerror = ZR_OK; //+++1.2 + if (fin==NULL) { zopenerror = ZR_ARGS; return NULL; } //+++1.2 + if (unz_copyright[0]!=' ') {lufclose(fin); zopenerror = ZR_CORRUPT; return NULL; } //+++1.2 + + int err=UNZ_OK; + unz_s us; + uLong central_pos,uL; + central_pos = unzlocal_SearchCentralDir(fin); + if (central_pos==0) err=UNZ_ERRNO; + if (lufseek(fin,central_pos,SEEK_SET)!=0) err=UNZ_ERRNO; + // the signature, already checked + if (unzlocal_getLong(fin,&uL)!=UNZ_OK) err=UNZ_ERRNO; + // number of this disk + uLong number_disk; // number of the current dist, used for spanning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk)!=UNZ_OK) err=UNZ_ERRNO; + // number of the disk with the start of the central directory + uLong number_disk_with_CD; // number the the disk with central dir, used for spaning ZIP, unsupported, always 0 + if (unzlocal_getShort(fin,&number_disk_with_CD)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir on this disk + if (unzlocal_getShort(fin,&us.gi.number_entry)!=UNZ_OK) err=UNZ_ERRNO; + // total number of entries in the central dir + uLong number_entry_CD; // total number of entries in the central dir (same than number_entry on nospan) + if (unzlocal_getShort(fin,&number_entry_CD)!=UNZ_OK) err=UNZ_ERRNO; + if ((number_entry_CD!=us.gi.number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) err=UNZ_BADZIPFILE; + // size of the central directory + if (unzlocal_getLong(fin,&us.size_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // offset of start of central directory with respect to the starting disk number + if (unzlocal_getLong(fin,&us.offset_central_dir)!=UNZ_OK) err=UNZ_ERRNO; + // zipfile comment length + if (unzlocal_getShort(fin,&us.gi.size_comment)!=UNZ_OK) err=UNZ_ERRNO; + if ((central_pos+fin->initial_offsetinitial_offset - (us.offset_central_dir+us.size_central_dir); + us.central_pos = central_pos; + us.pfile_in_zip_read = NULL; + fin->initial_offset = 0; // since the zipfile itself is expected to handle this + + unz_s *s = (unz_s*)zmalloc(sizeof(unz_s)); + *s=us; + unzGoToFirstFile((unzFile)s); + return (unzFile)s; +} + + + +// Close a ZipFile opened with unzipOpen. +// If there is files inside the .Zip opened with unzipOpenCurrentFile (see later), +// these files MUST be closed with unzipCloseCurrentFile before call unzipClose. +// return UNZ_OK if there is no problem. +int unzClose (unzFile file) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + if (s->pfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + lufclose(s->file); + if (s) zfree(s); // unused s=0; + return UNZ_OK; +} + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetGlobalInfo (unzFile file,unz_global_info *pglobal_info) +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +// Translate date/time from Dos format to tm_unz (readable more easilty) +void unzlocal_DosDateToTmuDate (uLong ulDosDate, tm_unz* ptm) +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +// Get Info about the current file in the zipfile, with internal only info +int unzlocal_GetCurrentFileInfoInternal (unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize); + +int unzlocal_GetCurrentFileInfoInternal (unzFile file, unz_file_info *pfile_info, + unz_file_info_internal *pfile_info_internal, char *szFileName, + uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (lufseek(s->file,s->pos_in_central_dir+s->byte_before_the_zipfile,SEEK_SET)!=0) + err=UNZ_ERRNO; + + + // we check the magic + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(s->file,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (lufread(szFileName,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extrafile,lSeek,SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (lufread(extraField,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentfile,lSeek,SEEK_CUR)==0) + {} // unused lSeek=0; + else + err=UNZ_ERRNO; + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (lufread(szComment,(uInt)uSizeRead,1,s->file)!=1) + err=UNZ_ERRNO; + //unused lSeek+=file_info.size_file_comment - uSizeRead; + } + else {} //unused lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +// Write info about the ZipFile in the *pglobal_info structure. +// No preparation of the structure is needed +// return UNZ_OK if there is no problem. +int unzGetCurrentFileInfo (unzFile file, unz_file_info *pfile_info, + char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, + char *szComment, uLong commentBufferSize) +{ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, szComment,commentBufferSize); +} + + +// Set the current file of the zipfile to the first file. +// return UNZ_OK if there is no problem +int unzGoToFirstFile (unzFile file) +{ + int err; + unz_s* s; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Set the current file of the zipfile to the next file. +// return UNZ_OK if there is no problem +// return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +int unzGoToNextFile (unzFile file) +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +// Try locate the file szFileName in the zipfile. +// For the iCaseSensitivity signification, see unzStringFileNameCompare +// return value : +// UNZ_OK if the file is found. It becomes the current file. +// UNZ_END_OF_LIST_OF_FILE if the file is not found +int unzLocateFile (unzFile file, const TCHAR *szFileName, int iCaseSensitivity) +{ + unz_s* s; + int err; + + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + if (file==NULL) + return UNZ_PARAMERROR; + + if (_tcslen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + char szFileNameA[MAX_PATH]; + +#ifdef _UNICODE + GetAnsiFileName(szFileName, szFileNameA, MAX_PATH-1); +#else + strcpy(szFileNameA, szFileName); +#endif + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (unzStringFileNameCompare(szCurrentFileName,szFileNameA,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + return err; +} + + +// Read the local header of the current zipfile +// Check the coherency of the local header and info in the end of central +// directory about this file +// store in *piSizeVar the size of extra info in local header +// (filename and size of extra field data) +int unzlocal_CheckCurrentFileCoherencyHeader (unz_s *s,uInt *piSizeVar, + uLong *poffset_local_extrafield, uInt *psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (lufseek(s->file,s->cur_file_info_internal.offset_curfile + s->byte_before_the_zipfile,SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + if (unzlocal_getLong(s->file,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; +// else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) +// err=UNZ_BADZIPFILE; + if (unzlocal_getShort(s->file,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(s->file,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // date/time + err=UNZ_ERRNO; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // crc + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size compr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(s->file,&uData) != UNZ_OK) // size uncompr + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(s->file,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(s->file,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + + + + + +// Open for reading data the current file in the zipfile. +// If there is no error and the file is opened, the return value is UNZ_OK. +int unzOpenCurrentFile (unzFile file) +{ + int err; + int Store; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; // offset of the local extra field + uInt size_local_extrafield; // size of the local extra field + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*)zmalloc(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)zmalloc(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); //unused pfile_in_zip_read_info=0; + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if ((s->cur_file_info.compression_method!=0) && (s->cur_file_info.compression_method!=Z_DEFLATED)) + { // unused err=UNZ_BADZIPFILE; + } + Store = s->cur_file_info.compression_method==0; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->file=s->file; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if (!Store) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + + err=inflateInit2(&pfile_in_zip_read_info->stream); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + // windowBits is passed < 0 to tell that there is no zlib header. + // Note that in this case inflate *requires* an extra "dummy" byte + // after the compressed stream in order to complete decompression and + // return Z_STREAM_END. + // In unzip, i don't wait absolutely Z_STREAM_END because I known the + // size of both compressed and uncompressed data + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + + s->pfile_in_zip_read = pfile_in_zip_read_info; + return UNZ_OK; +} + + +// Read bytes from the current file. +// buf contain buffer where data must be copied +// len the size of buf. +// return the number of byte copied if somes bytes are copied +// return 0 if the end of file was reached +// return <0 with error code if there is an error +// (UNZ_ERRNO for IO error, or zLib error for uncompress error) +int unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ int err=UNZ_OK; + uInt iRead = 0; + + unz_s *s = (unz_s*)file; + if (s==NULL) return UNZ_PARAMERROR; + + file_in_zip_read_info_s* pfile_in_zip_read_info = s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) return UNZ_PARAMERROR; + if ((pfile_in_zip_read_info->read_buffer == NULL)) return UNZ_END_OF_LIST_OF_FILE; + if (len==0) return 0; + + pfile_in_zip_read_info->stream.next_out = (Byte*)buf; + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if (len>pfile_in_zip_read_info->rest_read_uncompressed) + { pfile_in_zip_read_info->stream.avail_out = (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + } + + while (pfile_in_zip_read_info->stream.avail_out>0) + { if ((pfile_in_zip_read_info->stream.avail_in==0) && (pfile_in_zip_read_info->rest_read_compressed>0)) + { uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) return UNZ_EOF; + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->pos_in_zipfile + pfile_in_zip_read_info->byte_before_the_zipfile,SEEK_SET)!=0) return UNZ_ERRNO; + if (lufread(pfile_in_zip_read_info->read_buffer,uReadThis,1,pfile_in_zip_read_info->file)!=1) return UNZ_ERRNO; + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + pfile_in_zip_read_info->stream.next_in = (Byte*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if (pfile_in_zip_read_info->compression_method==0) + { uInt uDoCopy,i ; + if (pfile_in_zip_read_info->stream.avail_out < pfile_in_zip_read_info->stream.avail_in) + { uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + } + else + { uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + } + for (i=0;istream.next_out+i) = *(pfile_in_zip_read_info->stream.next_in+i); + } + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,pfile_in_zip_read_info->stream.next_out,uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { uLong uTotalOutBefore,uTotalOutAfter; + const Byte *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + err=inflate(&pfile_in_zip_read_info->stream,flush); + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + pfile_in_zip_read_info->crc32 = ucrc32(pfile_in_zip_read_info->crc32,bufBefore,(uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + if (err==Z_STREAM_END) return (iRead==0) ? UNZ_EOF : iRead; //+++1.3 + //if (err==Z_STREAM_END) return (iRead==len) ? UNZ_EOF : iRead; //+++1.2 + + if (err != Z_OK) break; + } + } + + if (err==Z_OK) return iRead; + + return iRead; +} + + +// Give the current position in uncompressed data +z_off_t unztell (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +// return 1 if the end of file was reached, 0 elsewhere +int unzeof (unzFile file) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +// Read extra field from the current file (opened by unzOpenCurrentFile) +// This is the local-header version of the extra field (sometimes, there is +// more info in the local-header version than in the central-header) +// if buf==NULL, it return the size of the local extra field that can be read +// if buf!=NULL, len is the size of the buffer, the extra header is copied in buf. +// the return value is the number of bytes copied in buf, or (if <0) the error code +int unzGetLocalExtrafield (unzFile file,voidp buf,unsigned len) +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (lufseek(pfile_in_zip_read_info->file, pfile_in_zip_read_info->offset_local_extrafield + pfile_in_zip_read_info->pos_local_extrafield,SEEK_SET)!=0) + return UNZ_ERRNO; + + if (lufread(buf,(uInt)size_to_read,1,pfile_in_zip_read_info->file)!=1) + return UNZ_ERRNO; + + return (int)read_now; +} + +// Close the file in zip opened with unzipOpenCurrentFile +// Return UNZ_CRCERROR if all the file was read but the CRC is not good +int unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + if (pfile_in_zip_read_info->read_buffer!=0) + { void *buf = pfile_in_zip_read_info->read_buffer; + zfree(buf); + pfile_in_zip_read_info->read_buffer=0; + } + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + if (pfile_in_zip_read_info!=0) zfree(pfile_in_zip_read_info); // unused pfile_in_zip_read_info=0; + + s->pfile_in_zip_read=NULL; + + return err; +} + + +// Get the global comment string of the ZipFile, in the szComment buffer. +// uSizeBuf is the size of the szComment buffer. +// return the number of byte copied or an error code <0 +int unzGetGlobalComment (unzFile file, char *szComment, uLong uSizeBuf) +{ //int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) return UNZ_PARAMERROR; + s=(unz_s*)file; + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) uReadThis = s->gi.size_comment; + if (lufseek(s->file,s->central_pos+22,SEEK_SET)!=0) return UNZ_ERRNO; + if (uReadThis>0) + { *szComment='\0'; + if (lufread(szComment,(uInt)uReadThis,1,s->file)!=1) return UNZ_ERRNO; + } + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + + + + + +int unzOpenCurrentFile (unzFile file); +int unzReadCurrentFile (unzFile file, void *buf, unsigned len); +int unzCloseCurrentFile (unzFile file); + + +FILETIME timet2filetime(time_t timer) +{ + struct tm *tm = gmtime(&timer); + SYSTEMTIME st; + st.wYear = (WORD)(tm->tm_year+1900); + st.wMonth = (WORD)(tm->tm_mon+1); + st.wDay = (WORD)(tm->tm_mday); + st.wHour = (WORD)(tm->tm_hour); + st.wMinute = (WORD)(tm->tm_min); + st.wSecond = (WORD)(tm->tm_sec); + st.wMilliseconds=0; + FILETIME ft; + SystemTimeToFileTime(&st,&ft); + return ft; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +class TUnzip +{ public: + TUnzip() : uf(0), currentfile(-1), czei(-1) {} + + unzFile uf; int currentfile; ZIPENTRY cze; int czei; + TCHAR rootdir[MAX_PATH]; + + ZRESULT Open(void *rd, void *z,unsigned int len,DWORD flags); + ZRESULT Get(int index,ZIPENTRY *ze); + ZRESULT Find(const TCHAR *name,bool ic,int *index,ZIPENTRY *ze); + ZRESULT Unzip(int index,void *dst,unsigned int len,DWORD flags); + ZRESULT Close(); +}; + + +ZRESULT TUnzip::Open(void *rd, void *z,unsigned int len,DWORD flags) +{ + if (uf!=0 || currentfile!=-1) + return ZR_NOTINITED; + //GetCurrentDirectory(MAX_PATH,rootdir); + _tcscpy(rootdir, (const wchar_t *)rd); + _tcscat(rootdir,_T("\\")); + if (flags==ZIP_HANDLE) + { + DWORD type = GetFileType(z); + if (type!=FILE_TYPE_DISK) + return ZR_SEEK; + } + ZRESULT e; + LUFILE *f = lufopen(z,len,flags,&e); + if (f==NULL) + return e; + uf = unzOpenInternal(f); + //return ZR_OK; + return zopenerror; //+++1.2 +} + +ZRESULT TUnzip::Get(int index,ZIPENTRY *ze) +{ if (index<-1 || index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index==czei && index!=-1) {memcpy(ze,&cze,sizeof(ZIPENTRY)); return ZR_OK;} + if (index==-1) + { ze->index = uf->gi.number_entry; + ze->name[0]=0; + ze->attr=0; + ze->atime.dwLowDateTime=0; ze->atime.dwHighDateTime=0; + ze->ctime.dwLowDateTime=0; ze->ctime.dwHighDateTime=0; + ze->mtime.dwLowDateTime=0; ze->mtime.dwHighDateTime=0; + ze->comp_size=0; + ze->unc_size=0; + return ZR_OK; + } + if (index<(int)uf->num_file) unzGoToFirstFile(uf); + while ((int)uf->num_filefile,offset,SEEK_SET)!=0) return ZR_READ; + char *extra = new char[extralen]; + if (lufread(extra,1,(uInt)extralen,uf->file)!=extralen) {delete[] extra; return ZR_READ;} + // + ze->index=uf->num_file; + strcpy(ze->name,fn); + // zip has an 'attribute' 32bit value. Its lower half is windows stuff + // its upper half is standard unix attr. + unsigned long a = ufi.external_fa; + bool uisdir = (a&0x40000000)!=0; + //bool uwriteable= (a&0x08000000)!=0; + bool uwriteable= (a&0x00800000)!=0; // ***hd*** + //bool ureadable= (a&0x01000000)!=0; + //bool uexecutable=(a&0x00400000)!=0; + bool wreadonly= (a&0x00000001)!=0; + bool whidden= (a&0x00000002)!=0; + bool wsystem= (a&0x00000004)!=0; + bool wisdir= (a&0x00000010)!=0; + bool warchive= (a&0x00000020)!=0; + ze->attr=FILE_ATTRIBUTE_NORMAL; + if (uisdir || wisdir) ze->attr |= FILE_ATTRIBUTE_DIRECTORY; + if (warchive) ze->attr|=FILE_ATTRIBUTE_ARCHIVE; + if (whidden) ze->attr|=FILE_ATTRIBUTE_HIDDEN; + if (!uwriteable||wreadonly) ze->attr|=FILE_ATTRIBUTE_READONLY; + if (wsystem) ze->attr|=FILE_ATTRIBUTE_SYSTEM; + ze->comp_size = ufi.compressed_size; + ze->unc_size = ufi.uncompressed_size; + // + WORD dostime = (WORD)(ufi.dosDate&0xFFFF); + WORD dosdate = (WORD)((ufi.dosDate>>16)&0xFFFF); + FILETIME ft; + DosDateTimeToFileTime(dosdate,dostime,&ft); + ze->atime=ft; ze->ctime=ft; ze->mtime=ft; + // the zip will always have at least that dostime. But if it also has + // an extra header, then we'll instead get the info from that. + unsigned int epos=0; + while (epos+4mtime = timet2filetime(mtime); + } + if (hasatime) + { time_t atime = *(time_t*)(extra+epos); epos+=4; + ze->atime = timet2filetime(atime); + } + if (hasctime) + { time_t ctime = *(time_t*)(extra+epos); + ze->ctime = timet2filetime(ctime); + } + break; + } + // + if (extra!=0) delete[] extra; + memcpy(&cze,ze,sizeof(ZIPENTRY)); czei=index; + return ZR_OK; +} + +ZRESULT TUnzip::Find(const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + int res = unzLocateFile(uf,name,ic?CASE_INSENSITIVE:CASE_SENSITIVE); + if (res!=UNZ_OK) + { + if (index!=0) + *index=-1; + if (ze!=NULL) + { + ZeroMemory(ze,sizeof(ZIPENTRY)); ze->index=-1; + } + return ZR_NOTFOUND; + } + if (currentfile!=-1) + unzCloseCurrentFile(uf); currentfile=-1; + int i = (int)uf->num_file; + if (index!=NULL) + *index=i; + if (ze!=NULL) + { + ZRESULT zres = Get(i,ze); + if (zres!=ZR_OK) + return zres; + } + return ZR_OK; +} + +void EnsureDirectory(const TCHAR *rootdir, const TCHAR *dir) +{ + if (dir==NULL || dir[0] == _T('\0')) + return; + const TCHAR *lastslash = dir, *c = lastslash; + while (*c != _T('\0')) + { + if (*c==_T('/') || *c==_T('\\')) + lastslash=c; + c++; + } + const TCHAR *name=lastslash; + if (lastslash!=dir) + { + TCHAR tmp[MAX_PATH]; + _tcsncpy(tmp, dir, lastslash-dir); + tmp[lastslash-dir] = _T('\0'); + EnsureDirectory(rootdir,tmp); + name++; + } + TCHAR cd[MAX_PATH]; + _tcscpy(cd,rootdir); + //_tcscat(cd,name); + _tcscat(cd,dir); //+++1.2 + CreateDirectory(cd,NULL); +} + +ZRESULT TUnzip::Unzip(int index,void *dst,unsigned int len,DWORD flags) +{ + if (flags!=ZIP_MEMORY && flags!=ZIP_FILENAME && flags!=ZIP_HANDLE) + return ZR_ARGS; + if (flags==ZIP_MEMORY) + { + if (index!=currentfile) + { + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index>=(int)uf->gi.number_entry) + return ZR_ARGS; + if (index<(int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_file0) + return ZR_MORE; + unzCloseCurrentFile(uf); + currentfile=-1; + if (res==0) + return ZR_OK; + else + return ZR_FLATE; + } + + // otherwise we're writing to a handle or a file + if (currentfile!=-1) + unzCloseCurrentFile(uf); + currentfile=-1; + if (index >= (int)uf->gi.number_entry) + return ZR_ARGS; + if (index < (int)uf->num_file) + unzGoToFirstFile(uf); + while ((int)uf->num_filelen) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + +typedef struct +{ DWORD flag; + TUnzip *unz; +} TUnzipHandleData; + +HZIP OpenZipU(void *rd, void *z,unsigned int len,DWORD flags) +{ + TUnzip *unz = new TUnzip(); + lasterrorU = unz->Open(rd, z,len,flags); + if (lasterrorU!=ZR_OK) + { + delete unz; + return 0; + } + TUnzipHandleData *han = new TUnzipHandleData; + han->flag=1; + han->unz=unz; + return (HZIP)han; +} + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Get(index,ze); + return lasterrorU; +} + +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Get(index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + return lasterrorU; +} + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Find(name,ic,index,ze); + return lasterrorU; +} + +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *zew) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + ZIPENTRY ze; + lasterrorU = unz->Find(name,ic,index,&ze); + if (lasterrorU == ZR_OK) + { + zew->index = ze.index; + zew->attr = ze.attr; + zew->atime = ze.atime; + zew->ctime = ze.ctime; + zew->mtime = ze.mtime; + zew->comp_size = ze.comp_size; + zew->unc_size = ze.unc_size; +#ifdef _UNICODE + GetUnicodeFileName(ze.name, zew->name, MAX_PATH-1); +#else + strcpy(zew->name, ze.name); +#endif + } + + return lasterrorU; +} + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags) +{ + if (hz==0) + { + lasterrorU=ZR_ARGS; + return ZR_ARGS; + } + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) + { + lasterrorU=ZR_ZMODE; + return ZR_ZMODE; + } + TUnzip *unz = han->unz; + lasterrorU = unz->Unzip(index,dst,len,flags); + return lasterrorU; +} + +ZRESULT CloseZipU(HZIP hz) +{ if (hz==0) {lasterrorU=ZR_ARGS;return ZR_ARGS;} + TUnzipHandleData *han = (TUnzipHandleData*)hz; + if (han->flag!=1) {lasterrorU=ZR_ZMODE;return ZR_ZMODE;} + TUnzip *unz = han->unz; + lasterrorU = unz->Close(); + delete unz; + delete han; + return lasterrorU; +} + +bool IsZipHandleU(HZIP hz) +{ if (hz==0) return true; + TUnzipHandleData *han = (TUnzipHandleData*)hz; + return (han->flag==1); +} + + diff --git a/iis/ModSecurityIIS/Installer/XUnzip.h b/iis/ModSecurityIIS/Installer/XUnzip.h new file mode 100644 index 0000000000..baf5020c63 --- /dev/null +++ b/iis/ModSecurityIIS/Installer/XUnzip.h @@ -0,0 +1,382 @@ +// XUnzip.h Version 1.3 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich@gmail.com +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XUNZIP_H +#define XUNZIP_H + + +#ifndef XZIP_H +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that has been opened +#endif + +typedef DWORD ZRESULT; +// return codes from any of the zip functions. Listed later. + +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 + +typedef struct +{ int index; // index of this file within the zip + char name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRY; + +typedef struct +{ int index; // index of this file within the zip + TCHAR name[MAX_PATH]; // filename within the zip + DWORD attr; // attributes, as in GetFileAttributes. + FILETIME atime,ctime,mtime;// access, create, modify filetimes + long comp_size; // sizes of item, compressed and uncompressed. These + long unc_size; // may be -1 if not yet known (e.g. being streamed in) +} ZIPENTRYW; + + +/////////////////////////////////////////////////////////////////////////////// +// +// OpenZip() +// +// Purpose: Open an existing zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive opened ok, otherwise 0 +// +HZIP OpenZip(void *rd, void *z, unsigned int len, DWORD flags); +// OpenZip - opens a zip file and returns a handle with which you can +// subsequently examine its contents. You can open a zip file from: +// from a pipe: OpenZip(hpipe_read,0, ZIP_HANDLE); +// from a file (by handle): OpenZip(hfile,0, ZIP_HANDLE); +// from a file (by name): OpenZip("c:\\test.zip",0, ZIP_FILENAME); +// from a memory block: OpenZip(bufstart, buflen, ZIP_MEMORY); +// If the file is opened through a pipe, then items may only be +// accessed in increasing order, and an item may only be unzipped once, +// although GetZipItem can be called immediately before and after unzipping +// it. If it's opened i n any other way, then full random access is possible. +// Note: pipe input is not yet implemented. + + +/////////////////////////////////////////////////////////////////////////////// +// +// GetZipItem() +// +// Purpose: Get information about an item in an open zip archive +// +// Parameters: hz - handle of open zip archive +// index - index number (0 based) of item in zip +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define GetZipItem GetZipItemW +#else +#define GetZipItem GetZipItemA +#endif + +ZRESULT GetZipItemA(HZIP hz, int index, ZIPENTRY *ze); +ZRESULT GetZipItemW(HZIP hz, int index, ZIPENTRYW *ze); +// GetZipItem - call this to get information about an item in the zip. +// If index is -1 and the file wasn't opened through a pipe, +// then it returns information about the whole zipfile +// (and in particular ze.index returns the number of index items). +// Note: the item might be a directory (ze.attr & FILE_ATTRIBUTE_DIRECTORY) +// See below for notes on what happens when you unzip such an item. +// Note: if you are opening the zip through a pipe, then random access +// is not possible and GetZipItem(-1) fails and you can't discover the number +// of items except by calling GetZipItem on each one of them in turn, +// starting at 0, until eventually the call fails. Also, in the event that +// you are opening through a pipe and the zip was itself created into a pipe, +// then then comp_size and sometimes unc_size as well may not be known until +// after the item has been unzipped. + + +/////////////////////////////////////////////////////////////////////////////// +// +// FindZipItem() +// +// Purpose: Find item by name and return information about it +// +// Parameters: hz - handle of open zip archive +// name - name of file to look for inside zip archive +// ic - TRUE = case insensitive +// index - pointer to index number returned, or -1 +// ze - pointer to a ZIPENTRY (if ANSI) or ZIPENTRYW struct +// (if Unicode) +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +#ifdef _UNICODE +#define FindZipItem FindZipItemW +#else +#define FindZipItem FindZipItemA +#endif + +ZRESULT FindZipItemA(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRY *ze); +ZRESULT FindZipItemW(HZIP hz, const TCHAR *name, bool ic, int *index, ZIPENTRYW *ze); +// FindZipItem - finds an item by name. ic means 'insensitive to case'. +// It returns the index of the item, and returns information about it. +// If nothing was found, then index is set to -1 and the function returns +// an error code. + + +/////////////////////////////////////////////////////////////////////////////// +// +// UnzipItem() +// +// Purpose: Find item by index and unzip it +// +// Parameters: hz - handle of open zip archive +// index - index number of file to unzip +// dst - target file name of unzipped file +// len - for memory (ZIP_MEMORY. length of buffer; +// otherwise 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// + +ZRESULT UnzipItem(HZIP hz, int index, void *dst, unsigned int len, DWORD flags); +// UnzipItem - given an index to an item, unzips it. You can unzip to: +// to a pipe: UnzipItem(hz,i, hpipe_write,0,ZIP_HANDLE); +// to a file (by handle): UnzipItem(hz,i, hfile,0,ZIP_HANDLE); +// to a file (by name): UnzipItem(hz,i, ze.name,0,ZIP_FILENAME); +// to a memory block: UnzipItem(hz,i, buf,buflen,ZIP_MEMORY); +// In the final case, if the buffer isn't large enough to hold it all, +// then the return code indicates that more is yet to come. If it was +// large enough, and you want to know precisely how big, GetZipItem. +// Note: zip files are normally stored with relative pathnames. If you +// unzip with ZIP_FILENAME a relative pathname then the item gets created +// relative to the current directory - it first ensures that all necessary +// subdirectories have been created. Also, the item may itself be a directory. +// If you unzip a directory with ZIP_FILENAME, then the directory gets created. +// If you unzip it to a handle or a memory block, then nothing gets created +// and it emits 0 bytes. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + + + +// e.g. +// +// SetCurrentDirectory("c:\\docs\\stuff"); +// HZIP hz = OpenZip("c:\\stuff.zip",0,ZIP_FILENAME); +// ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index; +// for (int i=0; i +#include + + + +// TODO: reference additional headers your program requires here diff --git a/iis/ModSecurityIIS/Installer/targetver.h b/iis/ModSecurityIIS/Installer/targetver.h new file mode 100644 index 0000000000..90e767bfce --- /dev/null +++ b/iis/ModSecurityIIS/Installer/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/iis/ModSecurityIIS/ModSecurityIIS.sln b/iis/ModSecurityIIS/ModSecurityIIS.sln index 35ead3fc18..e7961d5dab 100644 --- a/iis/ModSecurityIIS/ModSecurityIIS.sln +++ b/iis/ModSecurityIIS/ModSecurityIIS.sln @@ -3,35 +3,51 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "ModSecurityIIS", "ModSecurityIIS\ModSecurityIIS.vdproj", "{3352AEF1-9F2A-47CD-9F63-658553063040}" ProjectSection(ProjectDependencies) = postProject - {023E10BD-4FF6-4401-9A40-AED9717073F2} = {023E10BD-4FF6-4401-9A40-AED9717073F2} + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C} = {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "configure", "ModSecurityIIS\installer project\configure.csproj", "{023E10BD-4FF6-4401-9A40-AED9717073F2}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Installer", "Installer\Installer.vcxproj", "{990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Mixed Platforms.ActiveCfg = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Mixed Platforms.Build.0 = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Win32.ActiveCfg = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|Win32.Build.0 = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x64.ActiveCfg = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x64.Build.0 = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x86.ActiveCfg = Debug {3352AEF1-9F2A-47CD-9F63-658553063040}.Debug|x86.Build.0 = Debug + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Mixed Platforms.ActiveCfg = Release + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Mixed Platforms.Build.0 = Release + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Win32.ActiveCfg = Release + {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|Win32.Build.0 = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x64.ActiveCfg = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x64.Build.0 = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x86.ActiveCfg = Release {3352AEF1-9F2A-47CD-9F63-658553063040}.Release|x86.Build.0 = Release - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x64.ActiveCfg = Debug|x64 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x64.Build.0 = Debug|x64 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x86.ActiveCfg = Debug|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Debug|x86.Build.0 = Debug|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x64.ActiveCfg = Release|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x64.Build.0 = Release|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x86.ActiveCfg = Release|x86 - {023E10BD-4FF6-4401-9A40-AED9717073F2}.Release|x86.Build.0 = Release|x86 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Win32.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|Win32.Build.0 = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|x64.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Debug|x86.ActiveCfg = Debug|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Mixed Platforms.Build.0 = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Win32.ActiveCfg = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|Win32.Build.0 = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|x64.ActiveCfg = Release|Win32 + {990BB195-6716-4DE3-B5E4-DCFCB1BD7D9C}.Release|x86.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf b/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf index c2d06d3ff3..39ca023452 100644 --- a/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf +++ b/iis/ModSecurityIIS/ModSecurityIIS/readme.rtf @@ -58,10 +58,10 @@ \leveltemplateid1225817962\'01\'95;}{\levelnumbers;}\f1\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext \leveltemplateid1897401684\'01\'95;}{\levelnumbers;}\f1\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid652101753}}{\*\listoverridetable{\listoverride\listid625282538\listoverridecount0\ls1}{\listoverride\listid652101753 \listoverridecount0\ls2}}{\*\pgptbl {\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp6\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp8\itap0\li0\ri0\sb0 -\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp7\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid1197496\rsid2315486\rsid3168183\rsid4593034\rsid6385683\rsid6967166\rsid9512544 -\rsid10098429\rsid12742482\rsid13639667\rsid16350125\rsid16593896}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Suha Can}{\operator Greg} -{\creatim\yr2012\mo6\dy4\hr11\min43}{\revtim\yr2012\mo7\dy12\hr17\min47}{\version7}{\edmins6}{\nofpages1}{\nofwords134}{\nofchars767}{\*\company Microsoft Corporation}{\nofcharsws900}{\vern49273}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/w -ord/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp7\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp10\itap0\li720\ri0\sb0\sa240}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid1197496\rsid2315486\rsid3168183\rsid4593034\rsid6385683\rsid6633290\rsid6754893\rsid6967166 +\rsid9512544\rsid10098429\rsid12742482\rsid13639667\rsid16350125\rsid16593896}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Suha Can}{\operator Greg} +{\creatim\yr2012\mo6\dy4\hr11\min43}{\revtim\yr2013\mo1\dy17\hr16\min15}{\version9}{\edmins12}{\nofpages1}{\nofwords173}{\nofchars992}{\*\company Microsoft Corporation}{\nofcharsws1163}{\vern49275}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office +/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect \widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen \expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1440\dgvorigin1440\dghshow1\dgvshow1 \jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct @@ -75,38 +75,44 @@ ord/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb144 \par }\pard\plain \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid16350125 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 { \rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 ModSecurity home page }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "http://www.modsecurity.org"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f007700770077002e006d006f006400730065006300750072006900740079002e006f00720067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}} -}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://www.modsecurity.org}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f007700770077002e006d006f006400730065006300750072006900740079002e006f00720067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000} +}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://www.modsecurity.org}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 HYPERLINK "http://engineering/" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4000000068007400740070003a002f002f0065006e00670069006e0065006500720069006e0067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 -\ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4000000068007400740070003a002f002f0065006e00670069006e0065006500720069006e0067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 +\af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 \par OWASP Core Rule Set for ModSecurity: }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 HYPERLINK "https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90bba000000680074007400700073003a002f002f007700770077002e006f0077006100730070002e006f00720067002f0069006e006400650078002e007000680070002f00430061007400650067006f00720079003a00 -4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 +4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab00000016}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 https://}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90bba000000680074007400700073003a002f002f007700770077002e006f0077006100730070002e006f00720067002f0069006e006400650078002e007000680070002f00430061007400650067006f00720079003a00 -4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 +4f0057004100530050005f004d006f006400530065006300750072006900740079005f0043006f00720065005f00520075006c0065005f005300650074005f00500072006f006a006500630074000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290 The OWASP CRS was installed on your system drive, under }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid6633290\charrsid6633290 inetpub\\wwwroot\\owasp_crs}{\rtlch\fcs1 \af0\afs24 +\ltrch\fcs0 \f0\fs24\insrsid6633290 +\par You can include it in your website by adding to your }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid6633290\charrsid6633290 web.config}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290 file, in }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 +\b\f0\fs24\insrsid6633290\charrsid6633290 system.webServer}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290 section: +\par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6633290\charrsid6633290 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 MSRC blog }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 HYPERLINK "http://blogs.technet.com/b/srd/" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5800000068007400740070003a002f002f0062006c006f00670073002e0074006500630068006e00650074002e0063006f006d002f0062002f007300720064002f000000795881f43b1d7f48af2c825dc48527630000 -0000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 http://blogs.technet.com/b/srd/}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj { -\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 +0000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 http://blogs.technet.com/b/srd/}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj { +\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid16350125 }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\lang1045\langfe1033\langnp1045\insrsid6385683\charrsid6633290 \par }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 Trustwave SpiderLabs blog: }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "http://blog.spiderlabs.com/"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}} -}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://blog.spiderlabs.com}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000} +}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 http://blog.spiderlabs.com}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 HYPERLINK "http://blog.spiderlabs.com/" }{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield -00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}} -}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5000000068007400740070003a002f002f0062006c006f0067002e007300700069006400650072006c006100620073002e0063006f006d002f000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000} +}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 /}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 \par Trustwave Commercial Rule Set for ModSecurity: }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486 HYPERLINK "https://www.trustwave.com/modsecurity-rules-support.php"}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid2315486\charrsid16350125 {\*\datafield 00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b88000000680074007400700073003a002f002f007700770077002e007400720075007300740077006100760065002e0063006f006d002f006d006f006400730065006300750072006900740079002d00720075006c00 -650073002d0073007500700070006f00720074002e007000680070000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 +650073002d0073007500700070006f00720074002e007000680070000000795881f43b1d7f48af2c825dc485276300000000a5ab00000000}}}{\fldrslt {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \cs16\f0\fs24\ul\cf2\insrsid6385683\charrsid16350125 https://www.trustwave.com/modsecurity-rules-support.php}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid6385683\charrsid16350125 \par }\pard \ltrpar\ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6967166 \par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a @@ -120,43 +126,43 @@ c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725 a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b 4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b -4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210030dd4329a8060000a41b0000160000007468656d652f7468656d652f -7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 -615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad -79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b -5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab -999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 -699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 -8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 -0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f -9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be -15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 -3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d -32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a -f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 -e877f0034e16bafb0e258ebb4faf06b769e888340b103d331115bebc4eb813bf83291b63624a0d1475a756c734f9bbc2cd28546ecbe1e20a3794ca175f3fae90 -fb6d2dd99bb07b55e5ccf68942bd0877b23c77b908e8db5f9db7f024d9239010f35bd4bbe2fcae387bfff9e2bc289f2fbe24cfaa301468dd8bd846dbb4ddf1c2 -ae7b4c191ba8292337a469bc25ec3d411f06f53a73e224c5292c8de0516732307070a1c0660d125c7d44553488700a4d7bddd3444299910e254ab984c3a219ae -a4adf1d0f82b7bd46cea4388ad1c12ab5d1ed8e1153d9c9f350a3246aad01c6873462b9ac05999ad5cc988826eafc3acae853a33b7ba11cd1445875ba1b236b1 -399483c90bd560b0b0263435085a21b0f22a9cf9356b38ec6046026d77eba3dc2dc60b17e92219e180643ed27acffba86e9c94c7ca9c225a0f1b0cfae0788ad5 -4adc5a9aec1b703b8b93caec1a0bd8e5de7b132fe5113cf312503b998e2c2927274bd051db6b35979b1ef271daf6c6704e86c73805af4bdd476216c26593af84 -0dfb5393d964f9cc9bad5c313709ea70f561ed3ea7b053075221d51696910d0d339585004b34272bff7213cc7a510a5454a3b349b1b206c1f0af490176745d4b -c663e2abb2b34b23da76f6352ba57ca2881844c1111ab189d8c7e07e1daaa04f40255c77988aa05fe06e4e5bdb4cb9c5394bbaf28d98c1d971ccd20867e556a7 -689ec9166e0a522183792b8907ba55ca6e943bbf2a26e52f48957218ffcf54d1fb09dc3eac04da033e5c0d0b8c74a6b43d2e54c4a10aa511f5fb021a07533b20 -5ae07e17a621a8e082dafc17e450ffb739676998b48643a4daa7211214f623150942f6a02c99e83b85583ddbbb2c4996113211551257a656ec1139246ca86be0 -aadedb3d1441a89b6a929501833b197fee7b9641a3503739e57c732a59b1f7da1cf8a73b1f9bcca0945b874d4393dbbf10b1680f66bbaa5d6f96e77b6f59113d -316bb31a795600b3d256d0cad2fe354538e7566b2bd69cc6cbcd5c38f0e2bcc63058344429dc2121fd07f63f2a7c66bf76e80d75c8f7a1b622f878a18941d840 -545fb28d07d205d20e8ea071b283369834296bdaac75d256cb37eb0bee740bbe278cad253b8bbfcf69eca23973d939b97891c6ce2cecd8da8e2d343578f6648a -c2d0383fc818c798cf64e52f597c740f1cbd05df0c264c49134cf09d4a60e8a107260f20f92d47b374e32f000000ffff0300504b030414000600080000002100 -0dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f7 -8277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89 -d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd500 -1996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0f -bfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6 -a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a -0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d00140006000800000021 -0030dd4329a8060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d001400060008 -00000021000dd1909fb60000001b0100002700000000000000000000000000b20900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ad0a00000000} +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100da7e1364a9060000a41b0000160000007468656d652f7468656d652f +7468656d65312e786d6cec594d6f1b4518be23f11f467b6f6327761a4775aad8b11b48d346b15bd4e37877bc3bcdecce6a669cd437d41e9190100571a012370e +08a8d44a5ccaaf09144191fa1778676677bd13af49d24650417b68bcb3cffbfd31efcc5ebd763f66e890084979d2f6ea976b1e2289cf039a846deff6b07f69cd +4352e124c08c27a4ed4d89f4ae6dbcffde55bcae22121304f4895cc76d2f522a5d5f5a923e2c637999a7248177632e62ace051844b81c047c037664bcbb5daea +528c69e2a104c7c0f6d6784c7d82869aa5b79133ef31784c94d40b3e1303cd9a3814061b1cd435424e659709748859db0339013f1a92fbca430c4b052fda5ead +d6efd76aded2c6d525bc9e1131b580d6a1abc1bf8c2e23080e968d4c118e0aa1f57ea37565abe06f004ccde37abd5eb7572ff81900f67db0d4ea52e6d9e8afd5 +3b39cf12c8fe9ce7ddad356b0d175fe2bf32a773abd3e9345b992e96a901d99f8d39fc5a6db5b1b9ece00dc8e29b73f84667b3db5d75f00664f1ab73f8fe95d6 +6ac3c51b50c468723087d671e9f733ee0564ccd976257c0de06b79206728c88622bbb488314fd4a25c8bf13d2efa00d04086154d909aa6648c7dc8e22e8e4782 +62ad0f5e27b8f4c62ef9726e49cb42d21734556defc3144345ccf8bd7afefdabe74fd1f18367c70f7e3a7ef8f0f8c18f969143b58d93b04cf5f2dbcffe7cfc31 +fae3e9372f1f7d518d9765fcaf3f7cf2cbcf9f5703a17c66eabcf8f2c96fcf9ebcf8ead3dfbf7b5401df147854860f694c24ba498ed03e8fc130e31557733212 +e7a31846989629369350e2046b2915fc7b2a72d037a79865d171f4e810d7837704b48f2ae0f5c93d47e14124268a5648de896207b8cb39eb7051e9851d2dabe4 +e6e12409ab858b4919b78ff16195ec2e4e9cf8f62629f4cd3c2d1dc3bb1171d4dc6338513824095148bfe307845458779752c7afbbd4175cf2b1427729ea605a +e992211d39d93423daa631c4655a6533c4dbf1cdee1dd4e1accaea2d72e822a12a30ab507e4898e3c6eb78a2705cc57288635676f80daca22a250753e197713d +a920d221611cf502226515cd2d01f69682be83a16355867d974d631729143da8e27903735e466ef1836e84e3b40a3ba04954c67e200f204531dae3aa0abecbdd +0ad1cf10079c2c0cf71d4a9c709fde0d6ed3d051699620facd4454c4f23ae14efe0ea66c8c896935d0d49d5e1dd3e4ef1a37a3d0b9ad848b6bdcd02a5f7cfdb8 +42efb7b5656fc2ee555533db271af522dcc9f6dce522a06f7f77dec293648f4041cc6f51ef9af3bbe6ecfde79bf3a27abef8963cebc2d0a0f52c62076d3376c7 +0ba7ee31656ca0a68cdc9066f096b0f7047d58d474e6c4498a53581ac14f5dc920c0c185021b1a24b8fa88aa6810e11486f6baa7998432631d4a9472098745b3 +5cc95be361f057f6a8d9d48710db392456bb3cb0cb2b7a393f6b146c8c56a139d0e682563483b30a5bb9923105db5e47585d2b75666975a39a698a8eb4c264ed +627328079717a6c162e14d186a108c42e0e55538f36bd170d8c18c04daef364679584c142e324432c201c962a4ed9e8f51dd0429cf953943b41d3619f4c1f114 +af95a4b534db379076962095c5351688cba3f72651ca33781625e076b21c59522e4e96a0a3b6d76a2e373de4e3b4ed8de19c0c3fe314a22ef51c895908974dbe +1236ed4f2d6653e5b368b672c3dc22a8c3d587f5fb9cc14e1f4885545b58463635ccab2c0558a22559fd979be0d68b32a0a21b9d4d8b953548867f4d0bf0a31b +5a321e135f95835d5ad1beb38f592be51345c4200a8ed0884dc43e86f0eb54057b022ae1bac37404fd007773dadbe695db9cb3a22bdf88199c5dc72c8d70d66e +7589e6956ce1a621153a98a7927a605ba5eec6b8f39b624afe824c29a7f1ffcc14bd9fc0edc34aa023e0c3d5b0c048574adbe342451cba501a51bf2f607030bd +03b205ee77e13524155c509bbf821ceabfb6e62c0f53d6708854fb344482c27ea42241c81eb425937da730ab677b9765c9324626a34aeacad4aa3d2287840d75 +0f5cd57bbb87224875d34db236607027f3cf7dce2a6814ea21a75c6f4e272bf65e5b03fff4e4638b198c72fbb0196872ff172a16e3c16c57b5f4863cdf7bcb86 +e817b331ab915705082b6d05adacec5f5385736eb5b663cd59bcdccc958328ce5b0c8bc54094c21d12d2ffc1fe4785cfecd70ebda10ef93ef456041f2f343348 +1bc8ea4b76f040ba41dac5110c4e76d1269366655d9b8d4eda6bf9667dc1936e21f784b3b5666789f7399d5d0c67ae38a7162fd2d999871d5fdbb585ae86c89e +2c51581ae707191318f399acfc258b8fee41a0b7e09bc18429699209be53090c33f4c0d40114bf95684837fe020000ffff0300504b0304140006000800000021 +000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484 +f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e +89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5 +001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de +0fbfff0000001c0200001300000000000000000000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5 +d6a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b79961683000000 +8a0000001c00000000000000000000000000190200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000 +2100da7e1364a9060000a41b00001600000000000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d0014000600 +0800000021000dd1909fb60000001b0100002700000000000000000000000000b30900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000ae0a00000000} {\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d 617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 @@ -214,8 +220,8 @@ fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000301c -57149160cd01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000f0d5 +16df10f5cd01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index 164a26be99..c2b63b4179 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -367,8 +367,12 @@ HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf) { bytesRead = (DWORD)chunk->FromFileHandle.ByteRange.Length.QuadPart; } + if ((bytesTotal + bytesRead) > chunk->FromFileHandle.ByteRange.Length.QuadPart) + { + bytesRead = chunk->FromFileHandle.ByteRange.Length.QuadPart - bytesTotal; + } - memcpy(buf, pIoBuffer, bytesRead); + memcpy(buf, pIoBuffer + dwDataStartOffset, bytesRead); buf += bytesRead; bytesTotal += bytesRead; @@ -403,6 +407,8 @@ CMyHttpModule::OnSendResponse( EnterCriticalSection(&m_csLock); + // here we must check if response body processing is enabled + // if(rsc == NULL || rsc->m_pRequestRec == NULL || rsc->m_pResponseBuffer != NULL || !modsecIsResponseBodyAccessEnabled(rsc->m_pRequestRec)) { goto Exit; @@ -501,9 +507,6 @@ CMyHttpModule::OnSendResponse( *(const char **)apr_array_push(r->content_languages) = lng; } - // here we must check if response body processing is enabled - // - // Disable kernel caching for this response // Probably we don't have to do it for ModSecurity @@ -765,17 +768,33 @@ CMyHttpModule::OnBeginRequest( pConfig->m_Config = modsecGetDefaultConfig(); + PCWSTR servpath = pHttpContext->GetApplication()->GetApplicationPhysicalPath(); + char *apppath; + USHORT apppathlen; + + hr = pConfig->GlobalWideCharToMultiByte((WCHAR *)servpath, wcslen(servpath), &apppath, &apppathlen); + + if ( FAILED( hr ) ) + { + delete path; + hr = E_UNEXPECTED; + goto Finished; + } + if(path[0] != 0) { - const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path); + const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path, apppath); if(err != NULL) { WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE); + delete apppath; + delete path; + goto Finished; } } + delete apppath; } - delete path; } @@ -788,6 +807,11 @@ CMyHttpModule::OnBeginRequest( r = modsecNewRequest(c, (directory_config *)pConfig->m_Config); + // on IIS we force input stream inspection flag, because its absence does not add any performance gain + // it's because on IIS request body must be restored each time it was read + // + modsecSetConfigForIISRequestBody(r); + REQUEST_STORED_CONTEXT *rsc = new REQUEST_STORED_CONTEXT(); rsc->m_pConnRec = c; diff --git a/nginx/modsecurity/ngx_http_modsecurity.c b/nginx/modsecurity/ngx_http_modsecurity.c index 8945508ca1..a78a153524 100644 --- a/nginx/modsecurity/ngx_http_modsecurity.c +++ b/nginx/modsecurity/ngx_http_modsecurity.c @@ -664,7 +664,7 @@ ngx_http_modsecurity_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) mscf->config = modsecGetDefaultConfig(); - msg = modsecProcessConfig(mscf->config, (const char *)value[1].data); + msg = modsecProcessConfig(mscf->config, (const char *)value[1].data, ""); if (msg != NULL) { ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "modSecurity: modsecProcessConfig() %s", msg); return NGX_CONF_ERROR; diff --git a/standalone/api.c b/standalone/api.c index 966d824bd4..10cd65f080 100644 --- a/standalone/api.c +++ b/standalone/api.c @@ -1,536 +1,578 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include - -#include "http_core.h" -#include "http_request.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "http_main.h" -#include "http_connection.h" - -#include "apr_optional.h" -#include "mod_log_config.h" - -#include "msc_logging.h" -#include "msc_util.h" - -#include "ap_mpm.h" -#include "scoreboard.h" - -#include "apr_version.h" - -#include "apr_lib.h" -#include "ap_config.h" -#include "http_config.h" - - -extern void *modsecLogObj; -extern void (*modsecLogHook)(void *obj, int level, char *str); -extern int (*modsecDropAction)(request_rec *r); -apr_status_t (*modsecReadBody)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); -apr_status_t (*modsecReadResponse)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); -apr_status_t (*modsecWriteBody)(request_rec *r, char *buf, unsigned int length); -apr_status_t (*modsecWriteResponse)(request_rec *r, char *buf, unsigned int length); - -extern const char *process_command_config(server_rec *s, - void *mconfig, - apr_pool_t *p, - apr_pool_t *ptemp, - const char *filename); - -#define DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \ -extern ns##_HOOK_##name##_t *hookfn_##name; - -#define DECLARE_HOOK(ret,name,args) \ - DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args) - -DECLARE_HOOK(int,pre_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp)) -DECLARE_HOOK(int,post_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp,server_rec *s)) -DECLARE_HOOK(void,child_init,(apr_pool_t *pchild, server_rec *s)) -DECLARE_HOOK(int,process_connection,(conn_rec *c)) -DECLARE_HOOK(int,post_read_request,(request_rec *r)) -DECLARE_HOOK(int,fixups,(request_rec *r)) -DECLARE_HOOK(void, error_log, (const char *file, int line, int level, - apr_status_t status, const server_rec *s, - const request_rec *r, apr_pool_t *pool, - const char *errstr)) -DECLARE_HOOK(int,log_transaction,(request_rec *r)) -DECLARE_HOOK(void,insert_filter,(request_rec *r)) -DECLARE_HOOK(void,insert_error_filter,(request_rec *r)) - -char *sa_name = "standalone"; -server_rec *server; -apr_pool_t *pool = NULL; - -apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, - ap_input_mode_t mode, apr_read_type_e block, - apr_off_t readbytes); -apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b); - -server_rec *modsecInit() { - apr_initialize(); - - apr_pool_create(&pool, NULL); - - apr_hook_global_pool = pool; - - server = apr_palloc(pool, sizeof(server_rec)); - - server->addrs = apr_palloc(pool, sizeof(server_addr_rec)); - server->addrs->host_addr = apr_palloc(pool, sizeof(apr_sockaddr_t)); - server->addrs->host_addr->addr_str_len = 16; - server->addrs->host_addr->family = AF_INET; - server->addrs->host_addr->hostname = sa_name; -#ifdef WIN32 - server->addrs->host_addr->ipaddr_len = sizeof(IN_ADDR); -#else - server->addrs->host_addr->ipaddr_len = sizeof(struct in_addr); -#endif - server->addrs->host_addr->ipaddr_ptr = &server->addrs->host_addr->sa.sin.sin_addr; - server->addrs->host_addr->pool = pool; - server->addrs->host_addr->port = 80; -#ifdef WIN32 - server->addrs->host_addr->sa.sin.sin_addr.S_un.S_addr = 0x0100007f; -#else - server->addrs->host_addr->sa.sin.sin_addr.s_addr = 0x0100007f; -#endif - server->addrs->host_addr->sa.sin.sin_family = AF_INET; - server->addrs->host_addr->sa.sin.sin_port = 80; - server->addrs->host_addr->salen = sizeof(server->addrs->host_addr->sa); - server->addrs->host_addr->servname = sa_name; - server->addrs->host_port = 80; - server->error_fname = "error.log"; - server->error_log = NULL; - server->limit_req_fields = 1024; - server->limit_req_fieldsize = 1024; - server->limit_req_line = 1024; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - server->loglevel = APLOG_DEBUG; -#endif - server->lookup_defaults = NULL; - server->module_config = NULL; - server->names = NULL; -#ifdef WIN32 - server->path = "c:\\inetpub\\wwwroot"; -#else - server->path = "/var/www"; -#endif - server->pathlen = strlen(server->path); - server->port = 80; - server->process = apr_palloc(pool, sizeof(process_rec)); - server->process->argc = 1; - server->process->argv = &sa_name; - server->process->pconf = pool; - server->process->pool = pool; - server->process->short_name = sa_name; - server->server_admin = sa_name; - server->server_hostname = sa_name; - server->server_scheme = ""; - server->timeout = 60 * 1000000;// 60 seconds - server->wild_names = NULL; - server->is_virtual = 0; - - ap_server_config_defines = apr_array_make(pool, 1, sizeof(char *)); - - // here we should add scoreboard handling for multiple processes and threads - // - ap_scoreboard_image = (scoreboard *)apr_palloc(pool, sizeof(scoreboard)); - - memset(ap_scoreboard_image, 0, sizeof(scoreboard)); - - // ---------- - - security2_module.module_index = 0; - - security2_module.register_hooks(pool); - - ap_register_input_filter("HTTP_IN", ap_http_in_filter, NULL, AP_FTYPE_RESOURCE); - ap_register_output_filter("HTTP_OUT", ap_http_out_filter, NULL, AP_FTYPE_CONTENT_SET); - - return server; -} - -apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, - ap_input_mode_t mode, apr_read_type_e block, - apr_off_t readbytes) { - char *tmp = NULL; - apr_bucket *e = NULL; - unsigned int readcnt = 0; - int is_eos = 0; - - if(modsecReadBody == NULL) - return AP_NOBODY_READ; - - tmp = (char *)apr_palloc(f->r->pool, readbytes); - modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); - - e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(b, e); - - if(is_eos) { - e = apr_bucket_eos_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(b, e); - } - - return APR_SUCCESS; -} - -apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b) { - modsec_rec *msr = (modsec_rec *)f->ctx; - apr_status_t rc; - - // is there a way to tell whether the response body was modified or not? - // - if((msr->txcfg->content_injection_enabled || msr->content_prepend_len != 0 || msr->content_append_len != 0) - && modsecWriteResponse != NULL && msr->txcfg->resbody_access) { - char *data = NULL; - apr_size_t length; - - rc = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp); - - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, - get_apr_error(msr->mp, rc)); - return -1; - } - - modsecWriteResponse(msr->r, data, msr->stream_output_length); - } - - return APR_SUCCESS; -} - -void modsecTerminate() { - apr_pool_destroy(pool); - pool = NULL; - apr_terminate(); -} - -void modsecStartConfig() { - apr_pool_t *ptemp = NULL; - - apr_pool_create(&ptemp, pool); - - hookfn_pre_config(pool, pool, ptemp); - - apr_pool_destroy(ptemp); -} - -directory_config *modsecGetDefaultConfig() { - return (directory_config *)security2_module.create_dir_config(pool, NULL); -} - -const char *modsecProcessConfig(directory_config *config, const char *dir) { - apr_pool_t *ptemp = NULL; - const char *err; - - apr_pool_create(&ptemp, pool); - - err = process_command_config(server, config, pool, ptemp, dir); - - apr_pool_destroy(ptemp); - - return err; -} - -void modsecFinalizeConfig() { - apr_pool_t *ptemp = NULL; - - apr_pool_create(&ptemp, pool); - - hookfn_post_config(pool, pool, ptemp, server); - hookfn_post_config(pool, pool, ptemp, server); - - apr_pool_destroy(ptemp); -} - -void modsecInitProcess() { - hookfn_child_init(pool, server); -} - -conn_rec *modsecNewConnection() { - conn_rec *c; - apr_pool_t *pc = NULL; - - apr_pool_create(&pc, pool); - - c = apr_pcalloc(pc, sizeof(conn_rec)); - - c->base_server = server; - c->id = 1; - c->local_addr = server->addrs->host_addr; - c->local_host = sa_name; - c->local_ip = "127.0.0.1"; - c->pool = pc; - c->remote_host = sa_name; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - c->remote_ip = "127.0.0.1"; - c->remote_addr = server->addrs->host_addr; -#else - c->client_ip = "127.0.0.1"; - c->client_addr = server->addrs->host_addr; -#endif - c->input_filters = NULL; - c->output_filters = NULL; - c->bucket_alloc = apr_bucket_alloc_create(pc); - - return c; -} - -void modsecProcessConnection(conn_rec *c) { - hookfn_process_connection(c); -} - -request_rec *modsecNewRequest(conn_rec *connection, directory_config *config) { - request_rec *r; - apr_pool_t *pr = NULL; - - apr_pool_create(&pr, connection->pool); - - r = apr_pcalloc(pr, sizeof(request_rec)); - - r->connection = connection; - r->server = server; - r->pool = pr; - r->main = NULL; - r->next = NULL; - r->notes = apr_table_make(pr, 10); - r->per_dir_config = apr_palloc(pr, sizeof(void *)); - ((void **)r->per_dir_config)[0] = config; - r->prev = NULL; - r->subprocess_env = apr_table_make(pr, 10); - apr_table_setn(r->subprocess_env, "UNIQUE_ID", "unique_id"); - r->user = NULL; - - r->headers_in = apr_table_make(pr, 10); - r->headers_out = apr_table_make(pr, 10); - r->err_headers_out = apr_table_make(pr, 10); - //apr_table_setn(r->headers_in, "Host", "www.google.com"); - //apr_table_setn(r->headers_in, "", ""); - - r->the_request = "GET /../../index.html HTTP/1.1"; - r->method = "GET"; - r->method_number = M_GET; - r->protocol = "HTTP/1.1"; - r->uri = "http://www.google.com/../../index.html"; - r->args = ""; - r->filename = "/../../index.html"; - r->handler = "IIS"; - - r->parsed_uri.scheme = "http"; - r->parsed_uri.path = "/../../index.html"; - r->parsed_uri.hostname = "www.google.com"; - r->parsed_uri.is_initialized = 1; - r->parsed_uri.port = 1234; - r->parsed_uri.port_str = "1234"; - r->parsed_uri.query = ""; - r->parsed_uri.dns_looked_up = 0; - r->parsed_uri.dns_resolved = 0; - r->parsed_uri.password = NULL; - r->parsed_uri.user = NULL; - r->parsed_uri.fragment = ""; - - r->input_filters = NULL; - r->output_filters = NULL; - - return r; -} - -static modsec_rec *retrieve_msr(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; - } - - /* If this is a subrequest then look in the main request. */ - if (r->main != NULL) { - msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - } - - /* If the request was redirected then look in the previous requests. */ - rx = r->prev; - while(rx != NULL) { - msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); - if (msr != NULL) { - msr->r = r; - return msr; - } - rx = rx->prev; - } - - return NULL; -} - -int modsecProcessRequest(request_rec *r) { - int status = DECLINED; - modsec_rec *msr = NULL; - - ap_filter_t *f = ap_add_input_filter("HTTP_IN", NULL, r, r->connection); - - status = hookfn_post_read_request(r); - status = hookfn_fixups(r); - - ap_remove_input_filter(f); - - hookfn_insert_filter(r); - - /* Find the transaction context first. */ - msr = retrieve_msr(r); - - if (msr == NULL) - return status; - - if(msr->stream_input_data != NULL && modsecWriteBody != NULL) - { - // target is responsible for copying the data into correctly managed buffer - // - modsecWriteBody(r, msr->stream_input_data, msr->stream_input_length); - - free(msr->stream_input_data); - - msr->stream_input_data = NULL; - } - - // leftover code possibly for future use - // - //if(r->input_filters != NULL && r->input_filters->frec->filter_init_func != NULL) - //r->input_filters->frec->filter_init_func(r->input_filters); - //if(r->input_filters != NULL && r->input_filters->frec->filter_func.in_func != NULL) - //r->input_filters->frec->filter_func.in_func(r->input_filters, NULL, 0, 0, 0); - - return status; -} - -int modsecIsResponseBodyAccessEnabled(request_rec *r) -{ - modsec_rec *msr = retrieve_msr(r); - - if(msr == NULL || msr->txcfg == NULL) - return 0; - - return msr->txcfg->resbody_access; -} - -int modsecProcessResponse(request_rec *r) { - int status = DECLINED; - - if(r->output_filters != NULL) { - modsec_rec *msr = (modsec_rec *)r->output_filters->ctx; - char buf[8192]; - char *tmp = NULL; - apr_bucket *e = NULL; - unsigned int readcnt = 0; - int is_eos = 0; - ap_filter_t *f = NULL; - apr_bucket_brigade *bb = NULL; - - if (msr == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, - "ModSecurity: Internal Error: msr is null in output filter."); - ap_remove_output_filter(r->output_filters); - return send_error_bucket(msr, r->output_filters, HTTP_INTERNAL_SERVER_ERROR); - } - - bb = apr_brigade_create(msr->mp, r->connection->bucket_alloc); - - if (bb == NULL) { - msr_log(msr, 1, "Process response: Failed to create brigade."); - return -1; - } - - msr->r = r; - - if(modsecReadResponse == NULL) - return AP_NOBODY_WROTE; - - f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection); - - while(!is_eos) { - modsecReadResponse(r, buf, 8192, &readcnt, &is_eos); - - if(readcnt > 0) { - tmp = (char *)apr_palloc(r->pool, readcnt); - memcpy(tmp, buf, readcnt); - - e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, e); - } - - if(is_eos) { - e = apr_bucket_eos_create(r->connection->bucket_alloc); - - APR_BRIGADE_INSERT_TAIL(bb, e); - } - } - - status = ap_pass_brigade(r->output_filters, bb); - - ap_remove_output_filter(f); - } - - return status; -} - -int modsecFinishRequest(request_rec *r) { - // run output filter - //if(r->output_filters != NULL && r->output_filters->frec->filter_init_func != NULL) - //r->output_filters->frec->filter_init_func(r->output_filters); - - hookfn_log_transaction(r); - - // make sure you cleanup before calling apr_terminate() - // otherwise double-free might occur, because of the request body pool cleanup function - // - apr_pool_destroy(r->connection->pool); - - return DECLINED; -} - -void modsecSetLogHook(void *obj, void (*hook)(void *obj, int level, char *str)) { - modsecLogObj = obj; - modsecLogHook = hook; -} - -void modsecSetReadBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { - modsecReadBody = func; -} - -void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { - modsecReadResponse = func; -} - -void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { - modsecWriteBody = func; -} - -void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { - modsecWriteResponse = func; -} - -void modsecSetDropAction(int (*func)(request_rec *r)) { - modsecDropAction = func; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include +#include + +#include "http_core.h" +#include "http_request.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "http_main.h" +#include "http_connection.h" + +#include "apr_optional.h" +#include "mod_log_config.h" + +#include "msc_logging.h" +#include "msc_util.h" + +#include "ap_mpm.h" +#include "scoreboard.h" + +#include "apr_version.h" + +#include "apr_lib.h" +#include "ap_config.h" +#include "http_config.h" + + +extern void *modsecLogObj; +extern void (*modsecLogHook)(void *obj, int level, char *str); +extern int (*modsecDropAction)(request_rec *r); +apr_status_t (*modsecReadBody)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); +apr_status_t (*modsecReadResponse)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos); +apr_status_t (*modsecWriteBody)(request_rec *r, char *buf, unsigned int length); +apr_status_t (*modsecWriteResponse)(request_rec *r, char *buf, unsigned int length); + +extern const char *process_command_config(server_rec *s, + void *mconfig, + apr_pool_t *p, + apr_pool_t *ptemp, + const char *filename); + +#define DECLARE_EXTERNAL_HOOK(ns,link,ret,name,args) \ +extern ns##_HOOK_##name##_t *hookfn_##name; + +#define DECLARE_HOOK(ret,name,args) \ + DECLARE_EXTERNAL_HOOK(ap,AP,ret,name,args) + +DECLARE_HOOK(int,pre_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp)) +DECLARE_HOOK(int,post_config,(apr_pool_t *pconf,apr_pool_t *plog, apr_pool_t *ptemp,server_rec *s)) +DECLARE_HOOK(void,child_init,(apr_pool_t *pchild, server_rec *s)) +DECLARE_HOOK(int,process_connection,(conn_rec *c)) +DECLARE_HOOK(int,post_read_request,(request_rec *r)) +DECLARE_HOOK(int,fixups,(request_rec *r)) +DECLARE_HOOK(void, error_log, (const char *file, int line, int level, + apr_status_t status, const server_rec *s, + const request_rec *r, apr_pool_t *pool, + const char *errstr)) +DECLARE_HOOK(int,log_transaction,(request_rec *r)) +DECLARE_HOOK(void,insert_filter,(request_rec *r)) +DECLARE_HOOK(void,insert_error_filter,(request_rec *r)) + +char *sa_name = "standalone"; +server_rec *server; +apr_pool_t *pool = NULL; + +apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, + ap_input_mode_t mode, apr_read_type_e block, + apr_off_t readbytes); +apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b); + +server_rec *modsecInit() { + apr_initialize(); + + apr_pool_create(&pool, NULL); + + apr_hook_global_pool = pool; + + server = apr_palloc(pool, sizeof(server_rec)); + + server->addrs = apr_palloc(pool, sizeof(server_addr_rec)); + server->addrs->host_addr = apr_palloc(pool, sizeof(apr_sockaddr_t)); + server->addrs->host_addr->addr_str_len = 16; + server->addrs->host_addr->family = AF_INET; + server->addrs->host_addr->hostname = sa_name; +#ifdef WIN32 + server->addrs->host_addr->ipaddr_len = sizeof(IN_ADDR); +#else + server->addrs->host_addr->ipaddr_len = sizeof(struct in_addr); +#endif + server->addrs->host_addr->ipaddr_ptr = &server->addrs->host_addr->sa.sin.sin_addr; + server->addrs->host_addr->pool = pool; + server->addrs->host_addr->port = 80; +#ifdef WIN32 + server->addrs->host_addr->sa.sin.sin_addr.S_un.S_addr = 0x0100007f; +#else + server->addrs->host_addr->sa.sin.sin_addr.s_addr = 0x0100007f; +#endif + server->addrs->host_addr->sa.sin.sin_family = AF_INET; + server->addrs->host_addr->sa.sin.sin_port = 80; + server->addrs->host_addr->salen = sizeof(server->addrs->host_addr->sa); + server->addrs->host_addr->servname = sa_name; + server->addrs->host_port = 80; + server->error_fname = "error.log"; + server->error_log = NULL; + server->limit_req_fields = 1024; + server->limit_req_fieldsize = 1024; + server->limit_req_line = 1024; +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + server->loglevel = APLOG_DEBUG; +#endif + server->lookup_defaults = NULL; + server->module_config = NULL; + server->names = NULL; +#ifdef WIN32 + server->path = "c:\\inetpub\\wwwroot"; +#else + server->path = "/var/www"; +#endif + server->pathlen = strlen(server->path); + server->port = 80; + server->process = apr_palloc(pool, sizeof(process_rec)); + server->process->argc = 1; + server->process->argv = &sa_name; + server->process->pconf = pool; + server->process->pool = pool; + server->process->short_name = sa_name; + server->server_admin = sa_name; + server->server_hostname = sa_name; + server->server_scheme = ""; + server->timeout = 60 * 1000000;// 60 seconds + server->wild_names = NULL; + server->is_virtual = 0; + + ap_server_config_defines = apr_array_make(pool, 1, sizeof(char *)); + + // here we should add scoreboard handling for multiple processes and threads + // + ap_scoreboard_image = (scoreboard *)apr_palloc(pool, sizeof(scoreboard)); + + memset(ap_scoreboard_image, 0, sizeof(scoreboard)); + + // ---------- + + security2_module.module_index = 0; + + security2_module.register_hooks(pool); + + ap_register_input_filter("HTTP_IN", ap_http_in_filter, NULL, AP_FTYPE_RESOURCE); + ap_register_output_filter("HTTP_OUT", ap_http_out_filter, NULL, AP_FTYPE_CONTENT_SET); + + return server; +} + +apr_status_t ap_http_in_filter(ap_filter_t *f, apr_bucket_brigade *b, + ap_input_mode_t mode, apr_read_type_e block, + apr_off_t readbytes) { + char *tmp = NULL; + apr_bucket *e = NULL; + unsigned int readcnt = 0; + int is_eos = 0; + + if(modsecReadBody == NULL) + return AP_NOBODY_READ; + + tmp = (char *)apr_palloc(f->r->pool, readbytes); + modsecReadBody(f->r, tmp, readbytes, &readcnt, &is_eos); + + e = apr_bucket_pool_create(tmp, readcnt, f->r->pool, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(b, e); + + if(is_eos) { + e = apr_bucket_eos_create(f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(b, e); + } + + return APR_SUCCESS; +} + +apr_status_t ap_http_out_filter(ap_filter_t *f, apr_bucket_brigade *b) { + modsec_rec *msr = (modsec_rec *)f->ctx; + apr_status_t rc; + + // is there a way to tell whether the response body was modified or not? + // + if((msr->txcfg->content_injection_enabled || msr->content_prepend_len != 0 || msr->content_append_len != 0) + && modsecWriteResponse != NULL && msr->txcfg->resbody_access) { + char *data = NULL; + apr_size_t length; + + rc = apr_brigade_pflatten(msr->of_brigade, &data, &length, msr->mp); + + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc, + get_apr_error(msr->mp, rc)); + return -1; + } + + modsecWriteResponse(msr->r, data, msr->stream_output_length); + } + + return APR_SUCCESS; +} + +void modsecTerminate() { + apr_pool_destroy(pool); + pool = NULL; + apr_terminate(); +} + +void modsecStartConfig() { + apr_pool_t *ptemp = NULL; + + apr_pool_create(&ptemp, pool); + + hookfn_pre_config(pool, pool, ptemp); + + apr_pool_destroy(ptemp); +} + +directory_config *modsecGetDefaultConfig() { + return (directory_config *)security2_module.create_dir_config(pool, NULL); +} + +const char *modsecProcessConfig(directory_config *config, const char *file, const char *dir) { + apr_pool_t *ptemp = NULL; + const char *err; + apr_status_t status; + const char *rootpath, *incpath; + + if(dir == NULL || strlen(dir) == 0) +#ifdef WIN32 + dir = "\\"; +#else + dir = "/"; +#endif + + incpath = file; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &incpath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, pool); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + int li = strlen(dir) - 1; + + if(dir[li] != '/' && dir[li] != '\\') +#ifdef WIN32 + file = apr_pstrcat(pool, dir, "\\", file, NULL); +#else + file = apr_pstrcat(pool, dir, "/", file, NULL); +#endif + else + file = apr_pstrcat(pool, dir, file, NULL); + } + else if (APR_EBADPATH == status) { + return apr_pstrcat(pool, "Config file has a bad path, ", file, NULL); + } + + apr_pool_create(&ptemp, pool); + + err = process_command_config(server, config, pool, ptemp, file); + + apr_pool_destroy(ptemp); + + return err; +} + +void modsecFinalizeConfig() { + apr_pool_t *ptemp = NULL; + + apr_pool_create(&ptemp, pool); + + hookfn_post_config(pool, pool, ptemp, server); + hookfn_post_config(pool, pool, ptemp, server); + + apr_pool_destroy(ptemp); +} + +void modsecInitProcess() { + hookfn_child_init(pool, server); +} + +conn_rec *modsecNewConnection() { + conn_rec *c; + apr_pool_t *pc = NULL; + + apr_pool_create(&pc, pool); + + c = apr_pcalloc(pc, sizeof(conn_rec)); + + c->base_server = server; + c->id = 1; + c->local_addr = server->addrs->host_addr; + c->local_host = sa_name; + c->local_ip = "127.0.0.1"; + c->pool = pc; + c->remote_host = sa_name; +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + c->remote_ip = "127.0.0.1"; + c->remote_addr = server->addrs->host_addr; +#else + c->client_ip = "127.0.0.1"; + c->client_addr = server->addrs->host_addr; +#endif + c->input_filters = NULL; + c->output_filters = NULL; + c->bucket_alloc = apr_bucket_alloc_create(pc); + + return c; +} + +void modsecProcessConnection(conn_rec *c) { + hookfn_process_connection(c); +} + +request_rec *modsecNewRequest(conn_rec *connection, directory_config *config) { + request_rec *r; + apr_pool_t *pr = NULL; + + apr_pool_create(&pr, connection->pool); + + r = apr_pcalloc(pr, sizeof(request_rec)); + + r->connection = connection; + r->server = server; + r->pool = pr; + r->main = NULL; + r->next = NULL; + r->notes = apr_table_make(pr, 10); + r->per_dir_config = apr_palloc(pr, sizeof(void *)); + ((void **)r->per_dir_config)[0] = config; + r->prev = NULL; + r->subprocess_env = apr_table_make(pr, 10); + apr_table_setn(r->subprocess_env, "UNIQUE_ID", "unique_id"); + r->user = NULL; + + r->headers_in = apr_table_make(pr, 10); + r->headers_out = apr_table_make(pr, 10); + r->err_headers_out = apr_table_make(pr, 10); + //apr_table_setn(r->headers_in, "Host", "www.google.com"); + //apr_table_setn(r->headers_in, "", ""); + + r->the_request = "GET /../../index.html HTTP/1.1"; + r->method = "GET"; + r->method_number = M_GET; + r->protocol = "HTTP/1.1"; + r->uri = "http://www.google.com/../../index.html"; + r->args = ""; + r->filename = "/../../index.html"; + r->handler = "IIS"; + + r->parsed_uri.scheme = "http"; + r->parsed_uri.path = "/../../index.html"; + r->parsed_uri.hostname = "www.google.com"; + r->parsed_uri.is_initialized = 1; + r->parsed_uri.port = 1234; + r->parsed_uri.port_str = "1234"; + r->parsed_uri.query = ""; + r->parsed_uri.dns_looked_up = 0; + r->parsed_uri.dns_resolved = 0; + r->parsed_uri.password = NULL; + r->parsed_uri.user = NULL; + r->parsed_uri.fragment = ""; + + r->input_filters = NULL; + r->output_filters = NULL; + + return r; +} + +static modsec_rec *retrieve_msr(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; + } + + /* If this is a subrequest then look in the main request. */ + if (r->main != NULL) { + msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + } + + /* If the request was redirected then look in the previous requests. */ + rx = r->prev; + while(rx != NULL) { + msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR); + if (msr != NULL) { + msr->r = r; + return msr; + } + rx = rx->prev; + } + + return NULL; +} + +int modsecProcessRequest(request_rec *r) { + int status = DECLINED; + modsec_rec *msr = NULL; + + ap_filter_t *f = ap_add_input_filter("HTTP_IN", NULL, r, r->connection); + + status = hookfn_post_read_request(r); + status = hookfn_fixups(r); + + ap_remove_input_filter(f); + + hookfn_insert_filter(r); + + /* Find the transaction context first. */ + msr = retrieve_msr(r); + + if (msr == NULL) + return status; + + if(msr->stream_input_data != NULL && modsecWriteBody != NULL) + { + // target is responsible for copying the data into correctly managed buffer + // + modsecWriteBody(r, msr->stream_input_data, msr->stream_input_length); + + free(msr->stream_input_data); + + msr->stream_input_data = NULL; + } + + // leftover code possibly for future use + // + //if(r->input_filters != NULL && r->input_filters->frec->filter_init_func != NULL) + //r->input_filters->frec->filter_init_func(r->input_filters); + //if(r->input_filters != NULL && r->input_filters->frec->filter_func.in_func != NULL) + //r->input_filters->frec->filter_func.in_func(r->input_filters, NULL, 0, 0, 0); + + return status; +} + +void modsecSetConfigForIISRequestBody(request_rec *r) +{ + modsec_rec *msr = retrieve_msr(r); + + if(msr == NULL || msr->txcfg == NULL) + return; + + if(msr->txcfg->reqbody_access) + msr->txcfg->stream_inbody_inspection = 1; +} + +int modsecIsResponseBodyAccessEnabled(request_rec *r) +{ + modsec_rec *msr = retrieve_msr(r); + + if(msr == NULL || msr->txcfg == NULL) + return 0; + + return msr->txcfg->resbody_access; +} + +int modsecProcessResponse(request_rec *r) { + int status = DECLINED; + + if(r->output_filters != NULL) { + modsec_rec *msr = (modsec_rec *)r->output_filters->ctx; + char buf[8192]; + char *tmp = NULL; + apr_bucket *e = NULL; + unsigned int readcnt = 0; + int is_eos = 0; + ap_filter_t *f = NULL; + apr_bucket_brigade *bb = NULL; + + if (msr == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, + "ModSecurity: Internal Error: msr is null in output filter."); + ap_remove_output_filter(r->output_filters); + return send_error_bucket(msr, r->output_filters, HTTP_INTERNAL_SERVER_ERROR); + } + + bb = apr_brigade_create(msr->mp, r->connection->bucket_alloc); + + if (bb == NULL) { + msr_log(msr, 1, "Process response: Failed to create brigade."); + return -1; + } + + msr->r = r; + + if(modsecReadResponse == NULL) + return AP_NOBODY_WROTE; + + f = ap_add_output_filter("HTTP_OUT", msr, r, r->connection); + + while(!is_eos) { + modsecReadResponse(r, buf, 8192, &readcnt, &is_eos); + + if(readcnt > 0) { + tmp = (char *)apr_palloc(r->pool, readcnt); + memcpy(tmp, buf, readcnt); + + e = apr_bucket_pool_create(tmp, readcnt, r->pool, r->connection->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, e); + } + + if(is_eos) { + e = apr_bucket_eos_create(r->connection->bucket_alloc); + + APR_BRIGADE_INSERT_TAIL(bb, e); + } + } + + status = ap_pass_brigade(r->output_filters, bb); + + ap_remove_output_filter(f); + } + + return status; +} + +int modsecFinishRequest(request_rec *r) { + // run output filter + //if(r->output_filters != NULL && r->output_filters->frec->filter_init_func != NULL) + //r->output_filters->frec->filter_init_func(r->output_filters); + + hookfn_log_transaction(r); + + // make sure you cleanup before calling apr_terminate() + // otherwise double-free might occur, because of the request body pool cleanup function + // + apr_pool_destroy(r->connection->pool); + + return DECLINED; +} + +void modsecSetLogHook(void *obj, void (*hook)(void *obj, int level, char *str)) { + modsecLogObj = obj; + modsecLogHook = hook; +} + +void modsecSetReadBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { + modsecReadBody = func; +} + +void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos)) { + modsecReadResponse = func; +} + +void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { + modsecWriteBody = func; +} + +void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)) { + modsecWriteResponse = func; +} + +void modsecSetDropAction(int (*func)(request_rec *r)) { + modsecDropAction = func; +} diff --git a/standalone/api.h b/standalone/api.h index 825f722cc4..7ba998d562 100644 --- a/standalone/api.h +++ b/standalone/api.h @@ -51,7 +51,7 @@ void modsecTerminate(); void modsecStartConfig(); directory_config *modsecGetDefaultConfig(); -const char *modsecProcessConfig(directory_config *config, const char *dir); +const char *modsecProcessConfig(directory_config *config, const char *file, const char *dir); void modsecFinalizeConfig(); void modsecInitProcess(); @@ -71,8 +71,11 @@ void modsecSetReadResponse(apr_status_t (*func)(request_rec *r, char *buf, unsig void modsecSetWriteBody(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)); void modsecSetWriteResponse(apr_status_t (*func)(request_rec *r, char *buf, unsigned int length)); void modsecSetDropAction(int (*func)(request_rec *r)); + int modsecIsResponseBodyAccessEnabled(request_rec *r); +void modsecSetConfigForIISRequestBody(request_rec *r); + #ifdef __cplusplus } #endif diff --git a/standalone/config.c b/standalone/config.c index 95c363d269..b1a57dadc9 100644 --- a/standalone/config.c +++ b/standalone/config.c @@ -1,1134 +1,1188 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) -* -* You may not use this file except in compliance with -* the License.  You may obtain a copy of the License at -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include -#include - -#include "http_core.h" -#include "http_request.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "http_main.h" -#include "http_connection.h" - -#include "apr_optional.h" -#include "mod_log_config.h" - -#include "msc_logging.h" -#include "msc_util.h" - -#include "ap_mpm.h" -#include "scoreboard.h" - -#include "apr_version.h" - -#include "apr_lib.h" -#include "ap_config.h" -#include "http_config.h" -#include "apr_fnmatch.h" - -AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) -{ -#ifdef DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, - "Done with config file %s", cfp->name); -#endif - return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); -} - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 -static apr_status_t cfg_close(void *param) -{ - apr_file_t *cfp = (apr_file_t *) param; - return (apr_file_close(cfp)); -} - -static int cfg_getch(void *param) -{ - char ch; - apr_file_t *cfp = (apr_file_t *) param; - if (apr_file_getc(&ch, cfp) == APR_SUCCESS) - return ch; - return (int)EOF; -} - -static void *cfg_getstr(void *buf, size_t bufsiz, void *param) -{ - apr_file_t *cfp = (apr_file_t *) param; - apr_status_t rv; - rv = apr_file_gets(buf, bufsiz, cfp); - if (rv == APR_SUCCESS) { - return buf; - } - return NULL; -} -#else -/* we can't use apr_file_* directly because of linking issues on Windows */ -static apr_status_t cfg_close(void *param) -{ - return apr_file_close(param); -} - -static apr_status_t cfg_getch(char *ch, void *param) -{ - return apr_file_getc(ch, param); -} - -static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param) -{ - return apr_file_gets(buf, bufsiz, param); -} -#endif - -/* Read one line from open ap_configfile_t, strip LF, increase line number */ -/* If custom handler does not define a getstr() function, read char by char */ - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 -AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp) -{ - /* If a "get string" function is defined, use it */ - if (cfp->getstr != NULL) { - char *src, *dst; - char *cp; - char *cbuf = buf; - size_t cbufsize = bufsize; - - while (1) { - ++cfp->line_number; - if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) - return 1; - - /* - * check for line continuation, - * i.e. match [^\\]\\[\r]\n only - */ - cp = cbuf; - while (cp < cbuf+cbufsize && *cp != '\0') - cp++; - if (cp > cbuf && cp[-1] == LF) { - cp--; - if (cp > cbuf && cp[-1] == CR) - cp--; - if (cp > cbuf && cp[-1] == '\\') { - cp--; - if (!(cp > cbuf && cp[-1] == '\\')) { - /* - * line continuation requested - - * then remove backslash and continue - */ - cbufsize -= (cp-cbuf); - cbuf = cp; - continue; - } - else { - /* - * no real continuation because escaped - - * then just remove escape character - */ - for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) - cp[0] = cp[1]; - } - } - } - break; - } - - /* - * Leading and trailing white space is eliminated completely - */ - src = buf; - while (apr_isspace(*src)) - ++src; - /* blast trailing whitespace */ - dst = &src[strlen(src)]; - while (--dst >= src && apr_isspace(*dst)) - *dst = '\0'; - /* Zap leading whitespace by shifting */ - if (src != buf) - for (dst = buf; (*dst++ = *src++) != '\0'; ) - ; - -#ifdef DEBUG_CFG_LINES - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf); -#endif - return 0; - } else { - /* No "get string" function defined; read character by character */ - register int c; - register size_t i = 0; - - buf[0] = '\0'; - /* skip leading whitespace */ - do { - c = cfp->getch(cfp->param); - } while (c == '\t' || c == ' '); - - if (c == EOF) - return 1; - - if(bufsize < 2) { - /* too small, assume caller is crazy */ - return 1; - } - - while (1) { - if ((c == '\t') || (c == ' ')) { - buf[i++] = ' '; - while ((c == '\t') || (c == ' ')) - c = cfp->getch(cfp->param); - } - if (c == CR) { - /* silently ignore CR (_assume_ that a LF follows) */ - c = cfp->getch(cfp->param); - } - if (c == LF) { - /* increase line number and return on LF */ - ++cfp->line_number; - } - if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { - /* - * check for line continuation - */ - if (i > 0 && buf[i-1] == '\\') { - i--; - if (!(i > 0 && buf[i-1] == '\\')) { - /* line is continued */ - c = cfp->getch(cfp->param); - continue; - } - /* else nothing needs be done because - * then the backslash is escaped and - * we just strip to a single one - */ - } - /* blast trailing whitespace */ - while (i > 0 && apr_isspace(buf[i - 1])) - --i; - buf[i] = '\0'; -#ifdef DEBUG_CFG_LINES - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, - "Read config: %s", buf); -#endif - return 0; - } - buf[i] = c; - ++i; - c = cfp->getch(cfp->param); - } - } -} -#else -static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize, - ap_configfile_t *cfp) -{ - apr_status_t rc; - /* If a "get string" function is defined, use it */ - if (cfp->getstr != NULL) { - char *cp; - char *cbuf = buf; - apr_size_t cbufsize = bufsize; - - while (1) { - ++cfp->line_number; - rc = cfp->getstr(cbuf, cbufsize, cfp->param); - if (rc == APR_EOF) { - if (cbuf != buf) { - *cbuf = '\0'; - break; - } - else { - return APR_EOF; - } - } - if (rc != APR_SUCCESS) { - return rc; - } - - /* - * check for line continuation, - * i.e. match [^\\]\\[\r]\n only - */ - cp = cbuf; - cp += strlen(cp); - if (cp > cbuf && cp[-1] == LF) { - cp--; - if (cp > cbuf && cp[-1] == CR) - cp--; - if (cp > cbuf && cp[-1] == '\\') { - cp--; - /* - * line continuation requested - - * then remove backslash and continue - */ - cbufsize -= (cp-cbuf); - cbuf = cp; - continue; - } - } - else if (cp - buf >= bufsize - 1) { - return APR_ENOSPC; - } - break; - } - } else { - /* No "get string" function defined; read character by character */ - apr_size_t i = 0; - - if (bufsize < 2) { - /* too small, assume caller is crazy */ - return APR_EINVAL; - } - buf[0] = '\0'; - - while (1) { - char c; - rc = cfp->getch(&c, cfp->param); - if (rc == APR_EOF) { - if (i > 0) - break; - else - return APR_EOF; - } - if (rc != APR_SUCCESS) - return rc; - if (c == LF) { - ++cfp->line_number; - /* check for line continuation */ - if (i > 0 && buf[i-1] == '\\') { - i--; - continue; - } - else { - break; - } - } - else if (i >= bufsize - 2) { - return APR_ENOSPC; - } - buf[i] = c; - ++i; - } - buf[i] = '\0'; - } - return APR_SUCCESS; -} - -static int cfg_trim_line(char *buf) -{ - char *start, *end; - /* - * Leading and trailing white space is eliminated completely - */ - start = buf; - while (apr_isspace(*start)) - ++start; - /* blast trailing whitespace */ - end = &start[strlen(start)]; - while (--end >= start && apr_isspace(*end)) - *end = '\0'; - /* Zap leading whitespace by shifting */ - if (start != buf) - memmove(buf, start, end - start + 2); -#ifdef DEBUG_CFG_LINES - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf); -#endif - return end - start + 1; -} - -AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize, - ap_configfile_t *cfp) -{ - apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp); - if (rc == APR_SUCCESS) - cfg_trim_line(buf); - return rc; -} -#endif - -static char *substring_conf(apr_pool_t *p, const char *start, int len, - char quote) -{ - char *result = apr_palloc(p, len + 2); - char *resp = result; - int i; - - for (i = 0; i < len; ++i) { - if (start[i] == '\\' && (start[i + 1] == '\\' - || (quote && start[i + 1] == quote))) - *resp++ = start[++i]; - else - *resp++ = start[i]; - } - - *resp++ = '\0'; -#if RESOLVE_ENV_PER_TOKEN - return (char *)ap_resolve_env(p,result); -#else - return result; -#endif -} - -AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) -{ - const char *str = *line, *strend; - char *res; - char quote; - - while (*str && apr_isspace(*str)) - ++str; - - if (!*str) { - *line = str; - return ""; - } - - if ((quote = *str) == '"' || quote == '\'') { - strend = str + 1; - while (*strend && *strend != quote) { - if (*strend == '\\' && strend[1] && - (strend[1] == quote || strend[1] == '\\')) { - strend += 2; - } - else { - ++strend; - } - } - res = substring_conf(p, str + 1, strend - str - 1, quote); - - if (*strend == quote) - ++strend; - } - else { - strend = str; - while (*strend && !apr_isspace(*strend)) - ++strend; - - res = substring_conf(p, str, strend - str, 0); - } - - while (*strend && apr_isspace(*strend)) - ++strend; - *line = strend; - return res; -} - -/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */ -AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, - apr_pool_t *p, const char *name) -{ - ap_configfile_t *new_cfg; - apr_file_t *file = NULL; - apr_finfo_t finfo; - apr_status_t status; -#ifdef DEBUG - char buf[120]; -#endif - - if (name == NULL) { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "Internal error: pcfg_openfile() called with NULL filename"); - return APR_EBADF; - } - - status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, - APR_OS_DEFAULT, p); -#ifdef DEBUG - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, - "Opening config file %s (%s)", - name, (status != APR_SUCCESS) ? - apr_strerror(status, buf, sizeof(buf)) : "successful"); -#endif - if (status != APR_SUCCESS) - return status; - - status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); - if (status != APR_SUCCESS) - return status; - - if (finfo.filetype != APR_REG && -#if defined(WIN32) || defined(OS2) || defined(NETWARE) - strcasecmp(apr_filepath_name_get(name), "nul") != 0) { -#else - strcmp(name, "/dev/null") != 0) { -#endif /* WIN32 || OS2 */ - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, - "Access to file %s denied by server: not a regular file", - name); - apr_file_close(file); - return APR_EBADF; - } - -#ifdef WIN32 - /* Some twisted character [no pun intended] at MS decided that a - * zero width joiner as the lead wide character would be ideal for - * describing Unicode text files. This was further convoluted to - * another MSism that the same character mapped into utf-8, EF BB BF - * would signify utf-8 text files. - * - * Since MS configuration files are all protecting utf-8 encoded - * Unicode path, file and resource names, we already have the correct - * WinNT encoding. But at least eat the stupid three bytes up front. - */ - { - unsigned char buf[4]; - apr_size_t len = 3; - status = apr_file_read(file, buf, &len); - if ((status != APR_SUCCESS) || (len < 3) - || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) { - apr_off_t zero = 0; - apr_file_seek(file, APR_SET, &zero); - } - } -#endif - - new_cfg = apr_palloc(p, sizeof(*new_cfg)); - new_cfg->param = file; - new_cfg->name = apr_pstrdup(p, name); -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 - new_cfg->getch = (int (*)(void *)) cfg_getch; - new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; - new_cfg->close = (int (*)(void *)) cfg_close; -#else - new_cfg->getch = cfg_getch; - new_cfg->getstr = cfg_getstr; - new_cfg->close = cfg_close; -#endif - new_cfg->line_number = 0; - *ret_cfg = new_cfg; - return APR_SUCCESS; -} - -AP_CORE_DECLARE(const command_rec *) ap_find_command(const char *name, - const command_rec *cmds) -{ - while (cmds->name) { - if (!strcasecmp(name, cmds->name)) - return cmds; - - ++cmds; - } - - return NULL; -} - -#define AP_MAX_ARGC 64 - -static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, - void *mconfig, const char *args) -{ - char *w, *w2, *w3; - const char *errmsg = NULL; - - if ((parms->override & cmd->req_override) == 0) - return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL); - - parms->info = cmd->cmd_data; - parms->cmd = cmd; - - switch (cmd->args_how) { - case RAW_ARGS: -#ifdef RESOLVE_ENV_PER_TOKEN - args = ap_resolve_env(parms->pool,args); -#endif - return cmd->AP_RAW_ARGS(parms, mconfig, args); - - case TAKE_ARGV: - { - char *argv[AP_MAX_ARGC]; - int argc = 0; - - do { - w = ap_getword_conf(parms->pool, &args); - if (*w == '\0' && *args == '\0') { - break; - } - argv[argc] = w; - argc++; - } while (argc < AP_MAX_ARGC && *args != '\0'); - - return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv); - } - - case NO_ARGS: - if (*args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes no arguments", - NULL); - - return cmd->AP_NO_ARGS(parms, mconfig); - - case TAKE1: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes one argument", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE1(parms, mconfig, w); - - case TAKE2: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *w2 == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes two arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE2(parms, mconfig, w, w2); - - case TAKE12: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL); - - case TAKE3: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - w3 = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, " takes three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case TAKE23: - w = ap_getword_conf(parms->pool, &args); - w2 = ap_getword_conf(parms->pool, &args); - w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - - if (*w == '\0' || *w2 == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, - " takes two or three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case TAKE123: - w = ap_getword_conf(parms->pool, &args); - w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - - if (*w == '\0' || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, - " takes one, two or three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case TAKE13: - w = ap_getword_conf(parms->pool, &args); - w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; - - if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0) - return apr_pstrcat(parms->pool, cmd->name, - " takes one or three arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); - - case ITERATE: - while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') { - - errmsg = cmd->AP_TAKE1(parms, mconfig, w); - - if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) - return errmsg; - } - - return errmsg; - - case ITERATE2: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args == 0) - return apr_pstrcat(parms->pool, cmd->name, - " requires at least two arguments", - cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); - - while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') { - - errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2); - - if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) - return errmsg; - } - - return errmsg; - - case FLAG: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off"))) - return apr_pstrcat(parms->pool, cmd->name, " must be On or Off", - NULL); - - return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0); - - default: - return apr_pstrcat(parms->pool, cmd->name, - " is improperly configured internally (server bug)", - NULL); - } -} - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 -static cmd_parms default_parms = -{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; -#endif - -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3 -static cmd_parms default_parms = -{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; -#endif - -typedef struct { - const char *fname; -} fnames; - -AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) -{ - apr_finfo_t finfo; - - if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) - return 0; /* in error condition, just return no */ - - return (finfo.filetype == APR_DIR); -} - -AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, - const char *src2) -{ - apr_size_t len1, len2; - char *path; - - len1 = strlen(src1); - len2 = strlen(src2); - /* allocate +3 for '/' delimiter, trailing NULL and overallocate - * one extra byte to allow the caller to add a trailing '/' - */ - path = (char *)apr_palloc(a, len1 + len2 + 3); - if (len1 == 0) { - *path = '/'; - memcpy(path + 1, src2, len2 + 1); - } - else { - char *next; - memcpy(path, src1, len1); - next = path + len1; - if (next[-1] != '/') { - *next++ = '/'; - } - memcpy(next, src2, len2 + 1); - } - return path; -} - -static int fname_alphasort(const void *fn1, const void *fn2) -{ - const fnames *f1 = fn1; - const fnames *f2 = fn2; - - return strcmp(f1->fname,f2->fname); -} - -AP_DECLARE(const char *) process_resource_config(const char *fname, - apr_array_header_t *ari, - apr_pool_t *ptemp) -{ - *(char **)apr_array_push(ari) = (char *)fname; - - return NULL; -} - -static const char *process_resource_config_nofnmatch(const char *fname, - apr_array_header_t *ari, - apr_pool_t *p, - apr_pool_t *ptemp, - unsigned depth, - int optional) -{ - const char *error; - apr_status_t rv; - - if (ap_is_directory(ptemp, fname)) { - apr_dir_t *dirp; - apr_finfo_t dirent; - int current; - apr_array_header_t *candidates = NULL; - fnames *fnew; - char *path = apr_pstrdup(ptemp, fname); - - if (++depth > 100) { - return apr_psprintf(p, "Directory %s exceeds the maximum include " - "directory nesting level of %u. You have " - "probably a recursion somewhere.", path, - 100); - } - - /* - * first course of business is to grok all the directory - * entries here and store 'em away. Recall we need full pathnames - * for this. - */ - rv = apr_dir_open(&dirp, path, ptemp); - if (rv != APR_SUCCESS) { - char errmsg[120]; - return apr_psprintf(p, "Could not open config directory %s: %s", - path, apr_strerror(rv, errmsg, sizeof errmsg)); - } - - candidates = apr_array_make(ptemp, 1, sizeof(fnames)); - while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { - /* strip out '.' and '..' */ - if (strcmp(dirent.name, ".") - && strcmp(dirent.name, "..")) { - fnew = (fnames *) apr_array_push(candidates); - fnew->fname = ap_make_full_path(ptemp, path, dirent.name); - } - } - - apr_dir_close(dirp); - if (candidates->nelts != 0) { - qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); - - /* - * Now recurse these... we handle errors and subdirectories - * via the recursion, which is nice - */ - for (current = 0; current < candidates->nelts; ++current) { - fnew = &((fnames *) candidates->elts)[current]; - error = process_resource_config_nofnmatch(fnew->fname, - ari, p, ptemp, - depth, optional); - if (error) { - return error; - } - } - } - - return NULL; - } - - return process_resource_config(fname, ari, ptemp); -} - -static const char *process_resource_config_fnmatch(const char *path, - const char *fname, - apr_array_header_t *ari, - apr_pool_t *p, - apr_pool_t *ptemp, - unsigned depth, - int optional) -{ - const char *rest; - apr_status_t rv; - apr_dir_t *dirp; - apr_finfo_t dirent; - apr_array_header_t *candidates = NULL; - fnames *fnew; - int current; - - /* find the first part of the filename */ - rest = ap_strchr_c(fname, '/'); - - if(rest == NULL) - rest = ap_strchr_c(fname, '\\'); - - if (rest) { - fname = apr_pstrndup(ptemp, fname, rest - fname); - rest++; - } - - /* optimisation - if the filename isn't a wildcard, process it directly */ - if (!apr_fnmatch_test(fname)) { - path = ap_make_full_path(ptemp, path, fname); - if (!rest) { - return process_resource_config_nofnmatch(path, - ari, p, - ptemp, 0, optional); - } - else { - return process_resource_config_fnmatch(path, rest, - ari, p, - ptemp, 0, optional); - } - } - - /* - * first course of business is to grok all the directory - * entries here and store 'em away. Recall we need full pathnames - * for this. - */ - rv = apr_dir_open(&dirp, path, ptemp); - if (rv != APR_SUCCESS) { - char errmsg[120]; - return apr_psprintf(p, "Could not open config directory %s: %s", - path, apr_strerror(rv, errmsg, sizeof errmsg)); - } - - candidates = apr_array_make(ptemp, 1, sizeof(fnames)); - while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) { - /* strip out '.' and '..' */ - if (strcmp(dirent.name, ".") - && strcmp(dirent.name, "..") - && (apr_fnmatch(fname, dirent.name, - APR_FNM_PERIOD) == APR_SUCCESS)) { - const char *full_path = ap_make_full_path(ptemp, path, dirent.name); - /* If matching internal to path, and we happen to match something - * other than a directory, skip it - */ - if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) { - continue; - } - fnew = (fnames *) apr_array_push(candidates); - fnew->fname = full_path; - } - } - - apr_dir_close(dirp); - if (candidates->nelts != 0) { - const char *error; - - qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); - - /* - * Now recurse these... we handle errors and subdirectories - * via the recursion, which is nice - */ - for (current = 0; current < candidates->nelts; ++current) { - fnew = &((fnames *) candidates->elts)[current]; - if (!rest) { - error = process_resource_config_nofnmatch(fnew->fname, - ari, p, - ptemp, 0, optional); - } - else { - error = process_resource_config_fnmatch(fnew->fname, rest, - ari, p, - ptemp, 0, optional); - } - if (error) { - return error; - } - } - } - else { - - if (!optional) { - return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing " - "(use IncludeOptional if required)", fname, path); - } - } - - return NULL; -} - -AP_DECLARE(const char *) process_fnmatch_configs(apr_array_header_t *ari, - const char *fname, - apr_pool_t *p, - apr_pool_t *ptemp, - int optional) -{ - if (!apr_fnmatch_test(fname)) { - return process_resource_config(fname, ari, p); - } - else { - apr_status_t status; - const char *rootpath, *filepath = fname; - - /* locate the start of the directories proper */ - status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); - - /* we allow APR_SUCCESS and APR_EINCOMPLETE */ - if (APR_ERELATIVE == status) { - return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL); - } - else if (APR_EBADPATH == status) { - return apr_pstrcat(p, "Include has a bad path, ", fname, NULL); - } - - /* walk the filepath */ - return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp, - 0, optional); - } -} - -const char *populate_include_files(apr_pool_t *p, apr_pool_t *ptemp, apr_array_header_t *ari, const char *fname, int optional) -{ - return process_fnmatch_configs(ari, fname, p, ptemp, optional); -} - -const char *process_command_config(server_rec *s, - void *mconfig, - apr_pool_t *p, - apr_pool_t *ptemp, - const char *filename) -{ - const char *errmsg; - char *l = apr_palloc (ptemp, MAX_STRING_LEN); - const char *args = l; - char *cmd_name, *w; - const command_rec *cmd; - apr_array_header_t *arr = apr_array_make(p, 1, sizeof(cmd_parms)); - apr_array_header_t *ari = apr_array_make(p, 1, sizeof(char *)); - cmd_parms *parms; - apr_status_t status; - ap_directive_t *newdir; - int optional; - char *err = NULL; - - errmsg = populate_include_files(p, ptemp, ari, filename, 0); - - if(errmsg != NULL) - goto Exit; - - while(ari->nelts != 0 || arr->nelts != 0) - { - if(ari->nelts > 0) - { - char *fn = *(char **)apr_array_pop(ari); - - parms = (cmd_parms *)apr_array_push(arr); - *parms = default_parms; - parms->pool = p; - parms->temp_pool = ptemp; - parms->server = s; - parms->override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); - parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI; - - status = ap_pcfg_openfile(&parms->config_file, p, fn); - - if(status != APR_SUCCESS) - { - apr_array_pop(arr); - errmsg = apr_pstrcat(p, "Cannot open config file: ", fn, NULL); - goto Exit; - } - } - - if (arr->nelts > 1024) { - errmsg = "Exceeded the maximum include directory nesting level. You have " - "probably a recursion somewhere."; - goto Exit; - } - - parms = (cmd_parms *)apr_array_pop(arr); - - if(parms == NULL) - break; - - while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { - if (*l == '#' || *l == '\0') - continue; - - args = l; - - cmd_name = ap_getword_conf(p, &args); - - if (*cmd_name == '\0') - continue; - - if (!strcasecmp(cmd_name, "IncludeOptional")) - { - optional = 1; - goto ProcessInclude; - } - - if (!strcasecmp(cmd_name, "Include")) - { - optional = 0; -ProcessInclude: - w = ap_getword_conf(parms->pool, &args); - - if (*w == '\0' || *args != 0) - { - ap_cfg_closefile(parms->config_file); - errmsg = apr_pstrcat(parms->pool, "Include takes one argument", NULL); - goto Exit; - } - - errmsg = populate_include_files(p, ptemp, ari, w, optional); - - *(cmd_parms *)apr_array_push(arr) = *parms; - - if(errmsg != NULL) - goto Exit; - - // we don't want to close the current file yet - // - parms = NULL; - break; - } - - cmd = ap_find_command(cmd_name, security2_module.cmds); - - if(cmd == NULL) - { - // unknown command, should error - // - ap_cfg_closefile(parms->config_file); - errmsg = apr_pstrcat(p, "Unknown command in config: ", cmd_name, NULL); - goto Exit; - } - - newdir = apr_pcalloc(p, sizeof(ap_directive_t)); - newdir->filename = parms->config_file->name; - newdir->line_num = parms->config_file->line_number; - newdir->directive = cmd_name; - newdir->args = apr_pstrdup(p, args); - - parms->directive = newdir; - - errmsg = invoke_cmd(cmd, parms, mconfig, args); - - if(errmsg != NULL) - break; - } - - if(parms != NULL) - ap_cfg_closefile(parms->config_file); - - if(errmsg != NULL) - break; - } - - if (errmsg) { - err = (char *)apr_palloc(p, 1024); - - if(parms != NULL) - apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, - parms->config_file->line_number, errmsg); - else - apr_snprintf(err, 1024, "Syntax error in config file: %s", errmsg); - } - - errmsg = err; - -Exit: - while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) - { - ap_cfg_closefile(parms->config_file); - } - - return errmsg; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include +#include + +#include "http_core.h" +#include "http_request.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "http_main.h" +#include "http_connection.h" + +#include "apr_optional.h" +#include "mod_log_config.h" + +#include "msc_logging.h" +#include "msc_util.h" + +#include "ap_mpm.h" +#include "scoreboard.h" + +#include "apr_version.h" + +#include "apr_lib.h" +#include "ap_config.h" +#include "http_config.h" +#include "apr_fnmatch.h" + +AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) +{ +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "Done with config file %s", cfp->name); +#endif + return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); +} + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 +static apr_status_t cfg_close(void *param) +{ + apr_file_t *cfp = (apr_file_t *) param; + return (apr_file_close(cfp)); +} + +static int cfg_getch(void *param) +{ + char ch; + apr_file_t *cfp = (apr_file_t *) param; + if (apr_file_getc(&ch, cfp) == APR_SUCCESS) + return ch; + return (int)EOF; +} + +static void *cfg_getstr(void *buf, size_t bufsiz, void *param) +{ + apr_file_t *cfp = (apr_file_t *) param; + apr_status_t rv; + rv = apr_file_gets(buf, bufsiz, cfp); + if (rv == APR_SUCCESS) { + return buf; + } + return NULL; +} +#else +/* we can't use apr_file_* directly because of linking issues on Windows */ +static apr_status_t cfg_close(void *param) +{ + return apr_file_close(param); +} + +static apr_status_t cfg_getch(char *ch, void *param) +{ + return apr_file_getc(ch, param); +} + +static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param) +{ + return apr_file_gets(buf, bufsiz, param); +} +#endif + +/* Read one line from open ap_configfile_t, strip LF, increase line number */ +/* If custom handler does not define a getstr() function, read char by char */ + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 +AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp) +{ + /* If a "get string" function is defined, use it */ + if (cfp->getstr != NULL) { + char *src, *dst; + char *cp; + char *cbuf = buf; + size_t cbufsize = bufsize; + + while (1) { + ++cfp->line_number; + if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) + return 1; + + /* + * check for line continuation, + * i.e. match [^\\]\\[\r]\n only + */ + cp = cbuf; + while (cp < cbuf+cbufsize && *cp != '\0') + cp++; + if (cp > cbuf && cp[-1] == LF) { + cp--; + if (cp > cbuf && cp[-1] == CR) + cp--; + if (cp > cbuf && cp[-1] == '\\') { + cp--; + if (!(cp > cbuf && cp[-1] == '\\')) { + /* + * line continuation requested - + * then remove backslash and continue + */ + cbufsize -= (cp-cbuf); + cbuf = cp; + continue; + } + else { + /* + * no real continuation because escaped - + * then just remove escape character + */ + for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) + cp[0] = cp[1]; + } + } + } + break; + } + + /* + * Leading and trailing white space is eliminated completely + */ + src = buf; + while (apr_isspace(*src)) + ++src; + /* blast trailing whitespace */ + dst = &src[strlen(src)]; + while (--dst >= src && apr_isspace(*dst)) + *dst = '\0'; + /* Zap leading whitespace by shifting */ + if (src != buf) + for (dst = buf; (*dst++ = *src++) != '\0'; ) + ; + +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf); +#endif + return 0; + } else { + /* No "get string" function defined; read character by character */ + register int c; + register size_t i = 0; + + buf[0] = '\0'; + /* skip leading whitespace */ + do { + c = cfp->getch(cfp->param); + } while (c == '\t' || c == ' '); + + if (c == EOF) + return 1; + + if(bufsize < 2) { + /* too small, assume caller is crazy */ + return 1; + } + + while (1) { + if ((c == '\t') || (c == ' ')) { + buf[i++] = ' '; + while ((c == '\t') || (c == ' ')) + c = cfp->getch(cfp->param); + } + if (c == CR) { + /* silently ignore CR (_assume_ that a LF follows) */ + c = cfp->getch(cfp->param); + } + if (c == LF) { + /* increase line number and return on LF */ + ++cfp->line_number; + } + if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { + /* + * check for line continuation + */ + if (i > 0 && buf[i-1] == '\\') { + i--; + if (!(i > 0 && buf[i-1] == '\\')) { + /* line is continued */ + c = cfp->getch(cfp->param); + continue; + } + /* else nothing needs be done because + * then the backslash is escaped and + * we just strip to a single one + */ + } + /* blast trailing whitespace */ + while (i > 0 && apr_isspace(buf[i - 1])) + --i; + buf[i] = '\0'; +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "Read config: %s", buf); +#endif + return 0; + } + buf[i] = c; + ++i; + c = cfp->getch(cfp->param); + } + } +} +#else +static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize, + ap_configfile_t *cfp) +{ + apr_status_t rc; + /* If a "get string" function is defined, use it */ + if (cfp->getstr != NULL) { + char *cp; + char *cbuf = buf; + apr_size_t cbufsize = bufsize; + + while (1) { + ++cfp->line_number; + rc = cfp->getstr(cbuf, cbufsize, cfp->param); + if (rc == APR_EOF) { + if (cbuf != buf) { + *cbuf = '\0'; + break; + } + else { + return APR_EOF; + } + } + if (rc != APR_SUCCESS) { + return rc; + } + + /* + * check for line continuation, + * i.e. match [^\\]\\[\r]\n only + */ + cp = cbuf; + cp += strlen(cp); + if (cp > cbuf && cp[-1] == LF) { + cp--; + if (cp > cbuf && cp[-1] == CR) + cp--; + if (cp > cbuf && cp[-1] == '\\') { + cp--; + /* + * line continuation requested - + * then remove backslash and continue + */ + cbufsize -= (cp-cbuf); + cbuf = cp; + continue; + } + } + else if (cp - buf >= bufsize - 1) { + return APR_ENOSPC; + } + break; + } + } else { + /* No "get string" function defined; read character by character */ + apr_size_t i = 0; + + if (bufsize < 2) { + /* too small, assume caller is crazy */ + return APR_EINVAL; + } + buf[0] = '\0'; + + while (1) { + char c; + rc = cfp->getch(&c, cfp->param); + if (rc == APR_EOF) { + if (i > 0) + break; + else + return APR_EOF; + } + if (rc != APR_SUCCESS) + return rc; + if (c == LF) { + ++cfp->line_number; + /* check for line continuation */ + if (i > 0 && buf[i-1] == '\\') { + i--; + continue; + } + else { + break; + } + } + else if (i >= bufsize - 2) { + return APR_ENOSPC; + } + buf[i] = c; + ++i; + } + buf[i] = '\0'; + } + return APR_SUCCESS; +} + +static int cfg_trim_line(char *buf) +{ + char *start, *end; + /* + * Leading and trailing white space is eliminated completely + */ + start = buf; + while (apr_isspace(*start)) + ++start; + /* blast trailing whitespace */ + end = &start[strlen(start)]; + while (--end >= start && apr_isspace(*end)) + *end = '\0'; + /* Zap leading whitespace by shifting */ + if (start != buf) + memmove(buf, start, end - start + 2); +#ifdef DEBUG_CFG_LINES + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf); +#endif + return end - start + 1; +} + +AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize, + ap_configfile_t *cfp) +{ + apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp); + if (rc == APR_SUCCESS) + cfg_trim_line(buf); + return rc; +} +#endif + +static char *substring_conf(apr_pool_t *p, const char *start, int len, + char quote) +{ + char *result = apr_palloc(p, len + 2); + char *resp = result; + int i; + + for (i = 0; i < len; ++i) { + if (start[i] == '\\' && (start[i + 1] == '\\' + || (quote && start[i + 1] == quote))) + *resp++ = start[++i]; + else + *resp++ = start[i]; + } + + *resp++ = '\0'; +#if RESOLVE_ENV_PER_TOKEN + return (char *)ap_resolve_env(p,result); +#else + return result; +#endif +} + +AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) +{ + const char *str = *line, *strend; + char *res; + char quote; + + while (*str && apr_isspace(*str)) + ++str; + + if (!*str) { + *line = str; + return ""; + } + + if ((quote = *str) == '"' || quote == '\'') { + strend = str + 1; + while (*strend && *strend != quote) { + if (*strend == '\\' && strend[1] && + (strend[1] == quote || strend[1] == '\\')) { + strend += 2; + } + else { + ++strend; + } + } + res = substring_conf(p, str + 1, strend - str - 1, quote); + + if (*strend == quote) + ++strend; + } + else { + strend = str; + while (*strend && !apr_isspace(*strend)) + ++strend; + + res = substring_conf(p, str, strend - str, 0); + } + + while (*strend && apr_isspace(*strend)) + ++strend; + *line = strend; + return res; +} + +/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */ +AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, + apr_pool_t *p, const char *name) +{ + ap_configfile_t *new_cfg; + apr_file_t *file = NULL; + apr_finfo_t finfo; + apr_status_t status; +#ifdef DEBUG + char buf[120]; +#endif + + if (name == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Internal error: pcfg_openfile() called with NULL filename"); + return APR_EBADF; + } + + status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, + APR_OS_DEFAULT, p); +#ifdef DEBUG + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "Opening config file %s (%s)", + name, (status != APR_SUCCESS) ? + apr_strerror(status, buf, sizeof(buf)) : "successful"); +#endif + if (status != APR_SUCCESS) + return status; + + status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); + if (status != APR_SUCCESS) + return status; + + if (finfo.filetype != APR_REG && +#if defined(WIN32) || defined(OS2) || defined(NETWARE) + strcasecmp(apr_filepath_name_get(name), "nul") != 0) { +#else + strcmp(name, "/dev/null") != 0) { +#endif /* WIN32 || OS2 */ + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "Access to file %s denied by server: not a regular file", + name); + apr_file_close(file); + return APR_EBADF; + } + +#ifdef WIN32 + /* Some twisted character [no pun intended] at MS decided that a + * zero width joiner as the lead wide character would be ideal for + * describing Unicode text files. This was further convoluted to + * another MSism that the same character mapped into utf-8, EF BB BF + * would signify utf-8 text files. + * + * Since MS configuration files are all protecting utf-8 encoded + * Unicode path, file and resource names, we already have the correct + * WinNT encoding. But at least eat the stupid three bytes up front. + */ + { + unsigned char buf[4]; + apr_size_t len = 3; + status = apr_file_read(file, buf, &len); + if ((status != APR_SUCCESS) || (len < 3) + || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) { + apr_off_t zero = 0; + apr_file_seek(file, APR_SET, &zero); + } + } +#endif + + new_cfg = apr_palloc(p, sizeof(*new_cfg)); + new_cfg->param = file; + new_cfg->name = apr_pstrdup(p, name); +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 + new_cfg->getch = (int (*)(void *)) cfg_getch; + new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; + new_cfg->close = (int (*)(void *)) cfg_close; +#else + new_cfg->getch = cfg_getch; + new_cfg->getstr = cfg_getstr; + new_cfg->close = cfg_close; +#endif + new_cfg->line_number = 0; + *ret_cfg = new_cfg; + return APR_SUCCESS; +} + +AP_CORE_DECLARE(const command_rec *) ap_find_command(const char *name, + const command_rec *cmds) +{ + while (cmds->name) { + if (!strcasecmp(name, cmds->name)) + return cmds; + + ++cmds; + } + + return NULL; +} + +#define AP_MAX_ARGC 64 + +static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms, + void *mconfig, const char *args) +{ + char *w, *w2, *w3; + const char *errmsg = NULL; + + if ((parms->override & cmd->req_override) == 0) + return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL); + + parms->info = cmd->cmd_data; + parms->cmd = cmd; + + switch (cmd->args_how) { + case RAW_ARGS: +#ifdef RESOLVE_ENV_PER_TOKEN + args = ap_resolve_env(parms->pool,args); +#endif + return cmd->AP_RAW_ARGS(parms, mconfig, args); + + case TAKE_ARGV: + { + char *argv[AP_MAX_ARGC]; + int argc = 0; + + do { + w = ap_getword_conf(parms->pool, &args); + if (*w == '\0' && *args == '\0') { + break; + } + argv[argc] = w; + argc++; + } while (argc < AP_MAX_ARGC && *args != '\0'); + + return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv); + } + + case NO_ARGS: + if (*args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes no arguments", + NULL); + + return cmd->AP_NO_ARGS(parms, mconfig); + + case TAKE1: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes one argument", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE1(parms, mconfig, w); + + case TAKE2: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE2(parms, mconfig, w, w2); + + case TAKE12: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL); + + case TAKE3: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, " takes three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case TAKE23: + w = ap_getword_conf(parms->pool, &args); + w2 = ap_getword_conf(parms->pool, &args); + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *w2 == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, + " takes two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case TAKE123: + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, + " takes one, two or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case TAKE13: + w = ap_getword_conf(parms->pool, &args); + w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL; + + if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0) + return apr_pstrcat(parms->pool, cmd->name, + " takes one or three arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + return cmd->AP_TAKE3(parms, mconfig, w, w2, w3); + + case ITERATE: + while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') { + + errmsg = cmd->AP_TAKE1(parms, mconfig, w); + + if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) + return errmsg; + } + + return errmsg; + + case ITERATE2: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args == 0) + return apr_pstrcat(parms->pool, cmd->name, + " requires at least two arguments", + cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL); + + while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') { + + errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2); + + if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0) + return errmsg; + } + + return errmsg; + + case FLAG: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off"))) + return apr_pstrcat(parms->pool, cmd->name, " must be On or Off", + NULL); + + return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0); + + default: + return apr_pstrcat(parms->pool, cmd->name, + " is improperly configured internally (server bug)", + NULL); + } +} + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3 +static cmd_parms default_parms = +{NULL, 0, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +#endif + +#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3 +static cmd_parms default_parms = +{NULL, 0, 0, NULL, -1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +#endif + +typedef struct { + const char *fname; +} fnames; + +AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) +{ + apr_finfo_t finfo; + + if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) + return 0; /* in error condition, just return no */ + + return (finfo.filetype == APR_DIR); +} + +AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, + const char *src2) +{ + apr_size_t len1, len2; + char *path; + + len1 = strlen(src1); + len2 = strlen(src2); + /* allocate +3 for '/' delimiter, trailing NULL and overallocate + * one extra byte to allow the caller to add a trailing '/' + */ + path = (char *)apr_palloc(a, len1 + len2 + 3); + if (len1 == 0) { + *path = '/'; + memcpy(path + 1, src2, len2 + 1); + } + else { + char *next; + memcpy(path, src1, len1); + next = path + len1; + if (next[-1] != '/') { + *next++ = '/'; + } + memcpy(next, src2, len2 + 1); + } + return path; +} + +static int fname_alphasort(const void *fn1, const void *fn2) +{ + const fnames *f1 = fn1; + const fnames *f2 = fn2; + + return strcmp(f1->fname,f2->fname); +} + +int fnmatch_test(const char *pattern) +{ + int nesting; + + nesting = 0; + while (*pattern) { + switch (*pattern) { + case '?': + case '*': + return 1; + +/* case '\\': + if (*++pattern == '\0') { + return 0; + } + break;*/ // this breaks on Windows + + case '[': /* '[' is only a glob if it has a matching ']' */ + ++nesting; + break; + + case ']': + if (nesting) { + return 1; + } + break; + } + ++pattern; } + return 0; +} + +AP_DECLARE(const char *) process_resource_config(const char *fname, + apr_array_header_t *ari, + apr_pool_t *ptemp) +{ + *(char **)apr_array_push(ari) = (char *)fname; + + return NULL; +} + +static const char *process_resource_config_nofnmatch(const char *fname, + apr_array_header_t *ari, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth, + int optional) +{ + const char *error; + apr_status_t rv; + + if (ap_is_directory(ptemp, fname)) { + apr_dir_t *dirp; + apr_finfo_t dirent; + int current; + apr_array_header_t *candidates = NULL; + fnames *fnew; + char *path = apr_pstrdup(ptemp, fname); + + if (++depth > 100) { + return apr_psprintf(p, "Directory %s exceeds the maximum include " + "directory nesting level of %u. You have " + "probably a recursion somewhere.", path, + 100); + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..")) { + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = ap_make_full_path(ptemp, path, dirent.name); + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + error = process_resource_config_nofnmatch(fnew->fname, + ari, p, ptemp, + depth, optional); + if (error) { + return error; + } + } + } + + return NULL; + } + + return process_resource_config(fname, ari, ptemp); +} + +static const char *process_resource_config_fnmatch(const char *path, + const char *fname, + apr_array_header_t *ari, + apr_pool_t *p, + apr_pool_t *ptemp, + unsigned depth, + int optional) +{ + const char *rest; + apr_status_t rv; + apr_dir_t *dirp; + apr_finfo_t dirent; + apr_array_header_t *candidates = NULL; + fnames *fnew; + int current; + + /* find the first part of the filename */ + rest = ap_strchr_c(fname, '/'); + + if(rest == NULL) + rest = ap_strchr_c(fname, '\\'); + + if (rest) { + fname = apr_pstrndup(ptemp, fname, rest - fname); + rest++; + } + + /* optimisation - if the filename isn't a wildcard, process it directly */ + if (!fnmatch_test(fname)) { + path = ap_make_full_path(ptemp, path, fname); + if (!rest) { + return process_resource_config_nofnmatch(path, + ari, p, + ptemp, 0, optional); + } + else { + return process_resource_config_fnmatch(path, rest, + ari, p, + ptemp, 0, optional); + } + } + + /* + * first course of business is to grok all the directory + * entries here and store 'em away. Recall we need full pathnames + * for this. + */ + rv = apr_dir_open(&dirp, path, ptemp); + if (rv != APR_SUCCESS) { + char errmsg[120]; + return apr_psprintf(p, "Could not open config directory %s: %s", + path, apr_strerror(rv, errmsg, sizeof errmsg)); + } + + candidates = apr_array_make(ptemp, 1, sizeof(fnames)); + while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) { + /* strip out '.' and '..' */ + if (strcmp(dirent.name, ".") + && strcmp(dirent.name, "..") + && (apr_fnmatch(fname, dirent.name, + APR_FNM_PERIOD | APR_FNM_NOESCAPE | APR_FNM_PATHNAME) == APR_SUCCESS)) { + const char *full_path = ap_make_full_path(ptemp, path, dirent.name); + /* If matching internal to path, and we happen to match something + * other than a directory, skip it + */ + if (rest && (rv == APR_SUCCESS) && (dirent.filetype != APR_DIR)) { + continue; + } + fnew = (fnames *) apr_array_push(candidates); + fnew->fname = full_path; + } + } + + apr_dir_close(dirp); + if (candidates->nelts != 0) { + const char *error; + + qsort((void *) candidates->elts, candidates->nelts, + sizeof(fnames), fname_alphasort); + + /* + * Now recurse these... we handle errors and subdirectories + * via the recursion, which is nice + */ + for (current = 0; current < candidates->nelts; ++current) { + fnew = &((fnames *) candidates->elts)[current]; + if (!rest) { + error = process_resource_config_nofnmatch(fnew->fname, + ari, p, + ptemp, 0, optional); + } + else { + error = process_resource_config_fnmatch(fnew->fname, rest, + ari, p, + ptemp, 0, optional); + } + if (error) { + return error; + } + } + } + else { + + if (!optional) { + return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing " + "(use IncludeOptional if required)", fname, path); + } + } + + return NULL; +} + +AP_DECLARE(const char *) process_fnmatch_configs(apr_array_header_t *ari, + const char *fname, + apr_pool_t *p, + apr_pool_t *ptemp, + int optional) +{ + if (!fnmatch_test(fname)) { + return process_resource_config(fname, ari, p); + } + else { + apr_status_t status; + const char *rootpath, *filepath = fname; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + return apr_pstrcat(p, "Include must have an absolute path, ", fname, NULL); + } + else if (APR_EBADPATH == status) { + return apr_pstrcat(p, "Include has a bad path, ", fname, NULL); + } + + /* walk the filepath */ + return process_resource_config_fnmatch(rootpath, filepath, ari, p, ptemp, + 0, optional); + } +} + +const char *populate_include_files(apr_pool_t *p, apr_pool_t *ptemp, apr_array_header_t *ari, const char *fname, int optional) +{ + return process_fnmatch_configs(ari, fname, p, ptemp, optional); +} + +const char *process_command_config(server_rec *s, + void *mconfig, + apr_pool_t *p, + apr_pool_t *ptemp, + const char *filename) +{ + const char *errmsg; + char *l = apr_palloc (ptemp, MAX_STRING_LEN); + const char *args = l; + char *cmd_name, *w; + const command_rec *cmd; + apr_array_header_t *arr = apr_array_make(p, 1, sizeof(cmd_parms)); + apr_array_header_t *ari = apr_array_make(p, 1, sizeof(char *)); + cmd_parms *parms; + apr_status_t status; + ap_directive_t *newdir; + int optional; + char *err = NULL; + char *rootpath, *incpath; + int li; + + errmsg = populate_include_files(p, ptemp, ari, filename, 0); + + if(errmsg != NULL) + goto Exit; + + while(ari->nelts != 0 || arr->nelts != 0) + { + if(ari->nelts > 0) + { + char *fn = *(char **)apr_array_pop(ari); + + parms = (cmd_parms *)apr_array_push(arr); + *parms = default_parms; + parms->pool = p; + parms->temp_pool = ptemp; + parms->server = s; + parms->override = (RSRC_CONF | OR_ALL) & ~(OR_AUTHCFG | OR_LIMIT); + parms->override_opts = OPT_ALL | OPT_SYM_OWNER | OPT_MULTI; + + status = ap_pcfg_openfile(&parms->config_file, p, fn); + + if(status != APR_SUCCESS) + { + apr_array_pop(arr); + errmsg = apr_pstrcat(p, "Cannot open config file: ", fn, NULL); + goto Exit; + } + } + + if (arr->nelts > 1024) { + errmsg = "Exceeded the maximum include directory nesting level. You have " + "probably a recursion somewhere."; + goto Exit; + } + + parms = (cmd_parms *)apr_array_pop(arr); + + if(parms == NULL) + break; + + while (!(ap_cfg_getline(l, MAX_STRING_LEN, parms->config_file))) { + if (*l == '#' || *l == '\0') + continue; + + args = l; + + cmd_name = ap_getword_conf(p, &args); + + if (*cmd_name == '\0') + continue; + + if (!strcasecmp(cmd_name, "IncludeOptional")) + { + optional = 1; + goto ProcessInclude; + } + + if (!strcasecmp(cmd_name, "Include")) + { + optional = 0; +ProcessInclude: + w = ap_getword_conf(parms->pool, &args); + + if (*w == '\0' || *args != 0) + { + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(parms->pool, "Include takes one argument", NULL); + goto Exit; + } + + incpath = w; + + /* locate the start of the directories proper */ + status = apr_filepath_root(&rootpath, &incpath, APR_FILEPATH_TRUENAME | APR_FILEPATH_NATIVE, ptemp); + + /* we allow APR_SUCCESS and APR_EINCOMPLETE */ + if (APR_ERELATIVE == status) { + rootpath = apr_pstrdup(ptemp, parms->config_file->name); + li = strlen(rootpath) - 1; + + while(li >= 0 && rootpath[li] != '/' && rootpath[li] != '\\') + rootpath[li--] = 0; + + w = apr_pstrcat(p, rootpath, w, NULL); + } + else if (APR_EBADPATH == status) { + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(p, "Include file has a bad path, ", w, NULL); + goto Exit; + } + + errmsg = populate_include_files(p, ptemp, ari, w, optional); + + *(cmd_parms *)apr_array_push(arr) = *parms; + + if(errmsg != NULL) + goto Exit; + + // we don't want to close the current file yet + // + parms = NULL; + break; + } + + cmd = ap_find_command(cmd_name, security2_module.cmds); + + if(cmd == NULL) + { + // unknown command, should error + // + ap_cfg_closefile(parms->config_file); + errmsg = apr_pstrcat(p, "Unknown command in config: ", cmd_name, NULL); + goto Exit; + } + + newdir = apr_pcalloc(p, sizeof(ap_directive_t)); + newdir->filename = parms->config_file->name; + newdir->line_num = parms->config_file->line_number; + newdir->directive = cmd_name; + newdir->args = apr_pstrdup(p, args); + + parms->directive = newdir; + + errmsg = invoke_cmd(cmd, parms, mconfig, args); + + if(errmsg != NULL) + break; + } + + if(parms != NULL) + ap_cfg_closefile(parms->config_file); + + if(errmsg != NULL) + break; + } + + if (errmsg) { + err = (char *)apr_palloc(p, 1024); + + if(parms != NULL) + apr_snprintf(err, 1024, "Syntax error in config file %s, line %d: %s", parms->config_file->name, + parms->config_file->line_number, errmsg); + else + apr_snprintf(err, 1024, "Syntax error in config file: %s", errmsg); + } + + errmsg = err; + +Exit: + while((parms = (cmd_parms *)apr_array_pop(arr)) != NULL) + { + ap_cfg_closefile(parms->config_file); + } + + return errmsg; +} diff --git a/standalone/main.cpp b/standalone/main.cpp index 7e0a1163d5..73265b1ab2 100644 --- a/standalone/main.cpp +++ b/standalone/main.cpp @@ -239,7 +239,7 @@ void main(int argc, char *argv[]) config = modsecGetDefaultConfig(); - const char * err = modsecProcessConfig(config, config_file); + const char * err = modsecProcessConfig(config, config_file, "c:\\inetpub\\wwwroot"); if(err != NULL) { diff --git a/standalone/standalone.vcxproj.user b/standalone/standalone.vcxproj.user index 40e70632b2..0be15d4d97 100644 --- a/standalone/standalone.vcxproj.user +++ b/standalone/standalone.vcxproj.user @@ -1,7 +1,7 @@  - -c d:\xss.conf d:\test.dat + -c owasp_crs\modsecurity_iis.conf d:\test.dat WindowsLocalDebugger $(TargetPath) false