diff --git a/CHANGES b/CHANGES index 0f0fc101..6a0038d9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,133 @@ +21 June 2007 - 2.5.0-dev2 +------------------------- + + * Reversioned from 2.2.0 base version to 2.5.0 because of the large changeset. + + * Added @within string comparison operator with support for macro expansion. + + * Removed experimental variable RESPONSE_CONTENT_ENCODING which was not + working as intended. + + * Update included core rules to latest version. + + * Do not trigger "pause" action for internal requests. + + * Added matching rule filename and line number to audit log. + + * Added new phrase matching operators, @pm and @pmFromFile. These use + an alternate set based matching engine (Aho-Corasick) to perform faster + phrase type matches such as black/white lists, spam keywords, etc. + + * Cache transformations per-request/phase so they are not repeated. + + * Fixed issue with requests that use internal requests. These had the + potential to be intercepted incorrectly when other Apache httpd modules + that used internal requests were used with mod_security. + + * Added Solaris to the list of platforms not supporting the hidden + visibility attribute. + + * Removed excessive debug log entries about "capture" action. + + * Fixed decoding full-width unicode in t:urlDecodeUni. + + * Lessen some overhead of debugging messages and calculations + + * Removed strnlen() calls for non-GNU platforms. + + +14 June 2007 - 2.1.2-rc1 +------------------------ + + * Update included core rules to latest version. + + * Do not trigger "pause" action for internal requests. + + * Fixed issue with requests that use internal requests. These had the + potential to be intercepted incorrectly when other Apache httpd modules + that used internal requests were used with mod_security. + + * Added Solaris to the list of platforms not supporting the hidden + visibility attribute. + + * Fixed decoding full-width unicode in t:urlDecodeUni. + + * Lessen some overhead of debugging messages and calculations. + + * Do not try to intercept a request after a failed rule. This fixes the + issue associated with an "Internal Error: Asked to intercept request + but was_intercepted is zero" error message. + + * Added SecAuditLog2 directive to allow redundent concurrent audit log + index files. This will allow sending audit data to two consoles, etc. + + * Small performance improvement in memory management for rule execution. + + +11 May 2007 - 2.2.0-dev1 +------------------------ + + * Added @within string comparison operator with support for macro expansion. + + * Removed experimental variable RESPONSE_CONTENT_ENCODING which was not + working as intended. + + * Update included core rules to latest version. + + * Do not trigger "pause" action for internal requests. + + * Added matching rule filename and line number to audit log. + + * Added new phrase matching operators, @pm and @pmFromFile. These use + an alternate set based matching engine (Aho-Corasick) to perform faster + phrase type matches such as black/white lists, spam keywords, etc. + + * Cache transformations per-request/phase so they are not repeated. + + * Fixed issue with requests that use internal requests. These had the + potential to be intercepted incorrectly when other Apache httpd modules + that used internal requests were used with mod_security. + + * Added Solaris to the list of platforms not supporting the hidden + visibility attribute. + + * Removed excessive debug log entries about "capture" action. + + * Fixed decoding full-width unicode in t:urlDecodeUni. + + * Lessen some overhead of debugging messages and calculations + TODO: more to come + + * Removed strnlen() calls for non-GNU platforms. + + +14 June 2007 - 2.1.2-rc1 +------------------------ + + * Update included core rules to latest version. + + * Do not trigger "pause" action for internal requests. + + * Fixed issue with requests that use internal requests. These had the + potential to be intercepted incorrectly when other Apache httpd modules + that used internal requests were used with mod_security. + + * Added Solaris to the list of platforms not supporting the hidden + visibility attribute. + + * Fixed decoding full-width unicode in t:urlDecodeUni. + + * Lessen some overhead of debugging messages and calculations. + + * Do not try to intercept a request after a failed rule. This fixes the + issue associated with an "Internal Error: Asked to intercept request + but was_intercepted is zero" error message. + + * Added SecAuditLog2 directive to allow redundent concurrent audit log + index files. This will allow sending audit data to two consoles, etc. + + * Small performance improvement in memory management for rule execution. + 11 May 2007 - 2.2.0-dev1 ------------------------- @@ -20,8 +150,8 @@ * Added experimental support for content injection. Directive SecContentInjection (On|Off) controls whether injection is taking place. - Actions "prepend" and "append" inject content when executed. Do note that - it is your responsibility to make sure the response is of the appropriate + Actions "prepend" and "append" inject content when executed. Do note that + it is your responsibility to make sure the response is of the appropriate content type (e.g. HTML, plain text, etc). * Added string comparison operators with support for macro expansion: @@ -32,7 +162,7 @@ * Removed support for %0 - %9 capture macros as they were incorrectly expanding url encoded values. Use %{TX.0} - %{TX.9} instead. - + * Added t:length to transform a value to its character length. * Added t:trimLeft, t:trimRight, t:trim to remove whitespace @@ -58,25 +188,21 @@ and/or counting operator in the debug log. -05 Apr 2007 - 2.1.1-rc2 ------------------------ +11 Apr 2007 - 2.1.1 +------------------- * Add the PCRE_DOLLAR_ENDONLY option when compiling regular expression for the @rx operator and variables. - + * Really set PCRE_DOTALL option when compiling the regular expression for the @rx operator as the docs state. - - -11 Mar 2007 - 2.1.1-rc1 ------------------------ - + * Fixed potential memory corruption when expanding macros. - * Fixed error when a collection var was fetched in the same second as creation - by setting the rate to zero. + * Fixed error when a collection was retrieved from storage in the same second + as creation by setting the rate to zero. - * Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms + * Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms. * Fixed the faulty REQUEST_FILENAME variable, which used to change the internal Apache structures by mistake. diff --git a/README.TXT b/README.TXT index 666c25a0..2c0ba1d8 100644 --- a/README.TXT +++ b/README.TXT @@ -1,6 +1,10 @@ +ModSecurity for Apache 2.x, http://www.modsecurity.org/ +Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) -ModSecurity for Apache (http://www.modsecurity.org) -Copyright (C) 2004-2006 Breach Security, Inc. (http://www.breach.com) +You should have received a copy of the licence along with this +program (stored in the file "LICENSE"). If the file is missing, +or if you have any other questions related to the licence, please +write to Breach Security, Inc. at support@breach.com. DOCUMENTATION diff --git a/apache2/Makefile b/apache2/Makefile index 049c9f23..9cdbbeca 100644 --- a/apache2/Makefile +++ b/apache2/Makefile @@ -29,6 +29,8 @@ APACHECTL = apachectl INCLUDES = -I /usr/include/libxml2 DEFS = -DWITH_LIBXML2 +#DEFS = -DWITH_LIBXML2 -DDEBUG_CONF +#DEFS = -DWITH_LIBXML2 -DCACHE_DEBUG #LIBS = -Lmy/lib/dir -lmylib CFLAGS = -O2 -g -Wuninitialized -Wall -Wmissing-prototypes -Wshadow -Wunused-variable -Wunused-value -Wchar-subscripts -Wsign-compare diff --git a/apache2/Makefile.win b/apache2/Makefile.win index 071741c2..96284cd7 100644 --- a/apache2/Makefile.win +++ b/apache2/Makefile.win @@ -24,7 +24,7 @@ OBJS = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \ re.obj re_operators.obj re_actions.obj re_tfns.obj re_variables.obj \ msc_logging.obj msc_xml.obj msc_multipart.obj modsecurity.obj msc_parsers.obj \ msc_util.obj msc_pcre.obj persist_dbm.obj msc_reqbody.obj pdf_protect.obj \ - msc_geo.obj + msc_geo.obj acmp.obj all: $(DLL) diff --git a/apache2/acmp.c b/apache2/acmp.c new file mode 100644 index 00000000..d5a6dba8 --- /dev/null +++ b/apache2/acmp.c @@ -0,0 +1,711 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) + * + * You should have received a copy of the licence along with this + * program (stored in the file "LICENSE"). If the file is missing, + * or if you have any other questions related to the licence, please + * write to Breach Security, Inc. at support@breach.com. + * + */ +#include "acmp.h" +#include "utf8tables.h" +#include +#include +#include + + +/* + ******************************************************************************* + ******************************************************************************* + * Data structures for acmp parser + */ + + /** + * One node in trie + */ +typedef struct acmp_node_t acmp_node_t; +typedef struct acmp_btree_node_t acmp_btree_node_t; +struct acmp_node_t { + acmp_utf8_char_t letter; + int is_last; + acmp_callback_t callback; + void *callback_data; + int depth; + + acmp_node_t *child; + acmp_node_t *sibling; + acmp_node_t *fail; + acmp_node_t *parent; + acmp_node_t *o_match; + + acmp_btree_node_t *btree; + + apr_size_t hit_count; + + char *text; + char *pattern; +}; + +struct acmp_btree_node_t { + acmp_utf8_char_t letter; + acmp_btree_node_t *left; + acmp_btree_node_t *right; + acmp_node_t *node; +}; + +/** + * Data related to parser, not to individual nodes + */ +struct ACMP { + int is_utf8; + int is_case_sensitive; + apr_pool_t *parent_pool; + apr_pool_t *pool; + + int dict_count; + apr_size_t longest_entry; + + acmp_node_t *root_node; + + const char *data_start; + const char *data_end; + const char *data_pos; + apr_size_t data_len; + + apr_size_t *bp_buffer; + apr_size_t bp_buff_len; + + acmp_node_t *active_node; + char u8_buff[6]; + apr_size_t u8buff_len; + apr_size_t hit_count; + int is_failtree_done; + int is_active; + apr_size_t byte_pos; + apr_size_t char_pos; +}; + +/* + ******************************************************************************* + ******************************************************************************* + * Functions for UTF-8 support + */ + +/** + * Returns length of utf-8 sequence based on its first byte + */ +static int utf8_seq_len(const char *first_byte) { + return utf8_seq_lengths[(unsigned int)(unsigned char)first_byte[0]]; +} + +/** + * Returns length of utf8-encoded text + */ +static size_t utf8_strlen(const char *str) { + int len = 0; + const char *c = str; + while (*c != 0) { + c += utf8_seq_len(c); + len++; + } + return len; +} + +/** + * Returns ucs code for given utf-8 sequence + */ +static acmp_utf8_char_t utf8_decodechar(const char *str) { + int len = utf8_seq_len(str); + acmp_utf8_char_t ch = 0; + switch (len) { + case 6: ch += (unsigned char)*str++; ch <<= 6; + case 5: ch += (unsigned char)*str++; ch <<= 6; + case 4: ch += (unsigned char)*str++; ch <<= 6; + case 3: ch += (unsigned char)*str++; ch <<= 6; + case 2: ch += (unsigned char)*str++; ch <<= 6; + case 1: ch += (unsigned char)*str++; + } + ch -= utf8_offsets[len - 1]; + return ch; +} + +/** + * Returns lowercase for given unicode character. Searches through + * utf8_lcase_map table, if it doesn't find the code assumes + * it doesn't have a lowercase variant and returns code itself. + */ +static long utf8_lcase(acmp_utf8_char_t ucs_code) { + long mid, left, right; + left = 1; + right = UTF8_LCASEMAP_LEN * 2 + 1; + + while (left <= right) { + mid = (left + right) >> 1; + mid -= (mid % 2); mid++; + if (ucs_code > utf8_lcase_map[mid]) + left = mid + 2; + else if (ucs_code < utf8_lcase_map[mid]) + right = mid - 2; + else if (ucs_code == utf8_lcase_map[mid]) + return utf8_lcase_map[mid - 1]; + } + return ucs_code; +} + +/* + ******************************************************************************* + ******************************************************************************* + * Code for local / static utility functions + */ + +/** + * Returns length of given string for parser's encoding + */ +static size_t acmp_strlen(ACMP *parser, const char *str) { + return (parser->is_utf8 == 0) ? strlen(str) : utf8_strlen(str); +} + +/** + * Turns string to array of ucs values, depending on parser's encoding + * str - string to convert, doesn't have to be NULL-terminated + * ucs_chars - where to write ucs values + * len - length of input string + */ +static void acmp_strtoucs(ACMP *parser, const char *str, acmp_utf8_char_t *ucs_chars, int len) { + int i; + const char *c = str; + + if (parser->is_utf8 == 0) { + for (i = 0; i < len; i++) { + *ucs_chars++ = *c++; + } + } else { + for (i = 0; i < len; i++) { + *ucs_chars++ = utf8_decodechar(c); + c += utf8_seq_len(c); + } + } +} + +/** + * Returns node with given letter, or null if not found + */ +static acmp_node_t *acmp_child_for_code(acmp_node_t *parent_node, acmp_utf8_char_t ucs_code) { + acmp_node_t *node = parent_node->child; + if (node == NULL) return NULL; + for (;;) { + if (node->letter == ucs_code) return node; + node = node->sibling; + if (node == NULL) return NULL; + } +} + +/** + * Adds node to parent node, if it is not already there + */ +static void acmp_add_node_to_parent(acmp_node_t *parent, acmp_node_t *child) { + child->parent = parent; + if (parent->child == NULL) { + parent->child = child; + return; + } + acmp_node_t *node = parent->child; + for (;;) { + if (node == child) return; + if (node->sibling == NULL) { + node->sibling = child; + return; + } + node = node->sibling; + } +} + +/** + * Copies values from one node to another, without child/sibling/fail pointers + * and without state variables. + */ +static void acmp_clone_node_no_state(acmp_node_t *from, acmp_node_t *to) { + memcpy(to, from, sizeof(acmp_node_t)); + to->child = NULL; + to->sibling = NULL; + to->fail = NULL; + to->hit_count = 0; +} + +/** + * Copies sibling nodes and child node for from given "from" node to "to" node. + * Both nodes must already exist. + */ +static void acmp_copy_nodes_recursive(acmp_node_t *from, acmp_node_t *to, apr_pool_t *pool) { + acmp_node_t *old_node = from->child, *new_node, *nn2; + if (old_node == NULL) return; + nn2 = apr_pcalloc(pool, sizeof(acmp_node_t)); + acmp_clone_node_no_state(old_node, nn2); + nn2->parent = to; + to->child = nn2; + acmp_copy_nodes_recursive(from->child, to->child, pool); + + for (;;) { + old_node = old_node->sibling; + if (old_node == NULL) break; + new_node = apr_pcalloc(pool, sizeof(acmp_node_t)); + acmp_clone_node_no_state(old_node, new_node); + new_node->parent = to; + nn2->sibling = new_node; + nn2 = new_node; + acmp_copy_nodes_recursive(old_node, new_node, pool); + } +} + +static inline acmp_node_t *acmp_btree_find(acmp_node_t *node, acmp_utf8_char_t letter) { + acmp_btree_node_t *bnode = node->btree; + for (;;) { + if (bnode == NULL) return NULL; + if (bnode->letter == letter) return bnode->node; + if (bnode->letter > letter) { + bnode = bnode->left; + } else { + bnode = bnode->right; + } + } +} + +/** + * + */ +static inline acmp_node_t *acmp_goto(acmp_node_t *node, acmp_utf8_char_t letter) { + //return acmp_child_for_code(node, letter); + return acmp_btree_find(node, letter); +} + +/** + * Connects each node with its first fail node that is end of a phrase. + */ +static void acmp_connect_other_matches(ACMP *parser, acmp_node_t *node) { + acmp_node_t *child, *om; + + for (child = node->child; child != NULL; child = child->sibling) { + if (child->fail == NULL) continue; + for (om = child->fail; om != parser->root_node; om = om->fail) { + if (om->is_last) { + child->o_match = om; + break; + } + } + } + + /* Go recursively through children of this node that have a child node */ + for(child = node->child; child != NULL; child = child->sibling) { + if (child->child != NULL) acmp_connect_other_matches(parser, child); + } +} + +/** + * Adds leaves to binary tree, working from sorted array of keyword tree nodes + */ +static void acmp_add_btree_leaves(acmp_btree_node_t *node, acmp_node_t *nodes[], + int pos, int lb, int rb, apr_pool_t *pool) { + + int left = 0, right = 0; + if ((pos - lb) > 1) { + left = lb + (pos - lb) / 2; + node->left = apr_pcalloc(pool, sizeof(acmp_btree_node_t)); + node->left->node = nodes[left]; + node->left->letter = nodes[left]->letter; + /* printf("%c ->left %c \n", node->node->letter, node->left->node->letter); */ + } + if ((rb - pos) > 1) { + right = pos + (rb - pos) / 2; + node->right = apr_pcalloc(pool, sizeof(acmp_btree_node_t)); + node->right->node = nodes[right]; + node->right->letter = nodes[right]->letter; + /* printf("%c ->right %c \n", node->node->letter, node->right->node->letter); */ + } + if (node->right != NULL) { + acmp_add_btree_leaves(node->right, nodes, right, pos, rb, pool); + } + if (node->left != NULL) { + acmp_add_btree_leaves(node->left, nodes, left, lb, pos, pool); + } +} + +/** + * Builds balanced binary tree from children nodes of given node. + */ +static void acmp_build_binary_tree(ACMP *parser, acmp_node_t *node) { + apr_size_t count, i, j; + acmp_node_t *child = node->child; + + for (count = 0; child != NULL; child = child->sibling) count++; + acmp_node_t *nodes[count]; + child = node->child; + for (i = 0; i < count; i++) { + nodes[i] = child; + child = child->sibling; + }; + /* We have array with all children of the node and number of those children + */ + for (i = 0; i < count - 1; i++) + for (j = i + 1; j < count; j++) { + if (nodes[i]->letter < nodes[j]->letter) continue; + acmp_node_t *tmp = nodes[i]; + nodes[i] = nodes[j]; + nodes[j] = tmp; + } + node->btree = apr_pcalloc(parser->pool, sizeof(acmp_btree_node_t)); + apr_size_t pos = count / 2; + node->btree->node = nodes[pos]; + node->btree->letter = nodes[pos]->letter; + acmp_add_btree_leaves(node->btree, nodes, pos, -1, count, parser->pool); + for (i = 0; i < count; i++) { + if (nodes[i]->child != NULL) acmp_build_binary_tree(parser, nodes[i]); + } +} + +/** + * Constructs fail paths on keyword trie + */ +static apr_status_t acmp_connect_fail_branches(ACMP *parser) { + /* Already connected ? */ + if (parser->is_failtree_done != 0) return APR_SUCCESS; + acmp_node_t *child, *node, *goto_node; + apr_array_header_t *arr, *arr2, *tmp; + + parser->root_node->text = ""; + arr = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *)); + arr2 = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *)); + + parser->root_node->fail = parser->root_node; + + /* All first-level children will fail back to root node */ + for (child = parser->root_node->child; child != NULL; child = child->sibling) { + child->fail = parser->root_node; + *(acmp_node_t **)apr_array_push(arr) = child; + /* printf("fail direction: *%s* => *%s*\n", child->text, child->fail->text); */ + } + + for (;;) { + while (apr_is_empty_array(arr) == 0) { + node = *(acmp_node_t **)apr_array_pop(arr); + node->fail = parser->root_node; + if (node->parent != parser->root_node) { + goto_node = acmp_child_for_code(node->parent->fail, node->letter); + node->fail = (goto_node != NULL) ? goto_node : parser->root_node; + } + /* printf("fail direction: *%s* => *%s*\n", node->text, node->fail->text); */ + child = node->child; + while (child != NULL) { + *(acmp_node_t **)apr_array_push(arr2) = child; + child = child->sibling; + } + } + if (apr_is_empty_array(arr2) != 0) break; + + tmp = arr; + arr = arr2; + arr2 = tmp; + } + acmp_connect_other_matches(parser, parser->root_node); + if (parser->root_node->child != NULL) acmp_build_binary_tree(parser, parser->root_node); + parser->is_failtree_done = 1; + return APR_SUCCESS; +} + +/** + * Clears hit count of each node, called from acmp_reset() + */ +static void acmp_clear_hit_count_recursive(acmp_node_t *node) { + for (; node != NULL; node = node->sibling) { + node->hit_count = 0; + if (node->child != NULL) acmp_clear_hit_count_recursive(node->child); + } +} + +/** + * Called when a match is found + */ +static void acmp_found(ACMP *parser, acmp_node_t *node) { + if (node->callback) { + node->callback(parser, node->callback_data, + parser->bp_buffer[(parser->char_pos - node->depth - 1) % parser->bp_buff_len], + parser->char_pos - node->depth - 1); + } + /* printf("found: %s at position %d\n", node->pattern, parser->char_pos - node->depth - 1); */ + node->hit_count++; + parser->hit_count++; +} + +/* + ******************************************************************************* + ******************************************************************************* + * Code for functions from header file + */ + + +/** + * flags - OR-ed values of ACMP_FLAG constants + * pool - apr_pool to use as parent pool, can be set to NULL + */ +ACMP *acmp_create(int flags, apr_pool_t *pool) { + apr_status_t rc; + apr_pool_t *p; + rc = apr_pool_create(&p, pool); + if (rc != APR_SUCCESS) return NULL; + + ACMP *parser = apr_pcalloc(p, sizeof(ACMP)); + parser->pool = p; + parser->parent_pool = pool; + parser->is_utf8 = (flags & ACMP_FLAG_UTF8) == 0 ? 0 : 1; + parser->is_case_sensitive = (flags & ACMP_FLAG_CASE_SENSITIVE) == 0 ? 0 : 1; + parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t)); + return parser; +} + +/** + * Destroys previously created parser + */ +void acmp_destroy(ACMP *parser) { + /* + * All data is kept in parser's pool (including parser struct itself), so + * destroying the pool will destroy everything + */ + apr_pool_destroy(parser->pool); +} + +/** + * Creates parser with same options and same patterns + * parser - ACMP parser to duplicate + * pool - parent pool to use, if left as NULL original parser's parent pool is used + */ +ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool) { + apr_status_t rc; + apr_pool_t *p; + + if (pool == NULL) pool = parser->parent_pool; + rc = apr_pool_create(&p, pool); + if (rc != APR_SUCCESS) return NULL; + + ACMP *new_parser = apr_pcalloc(p, sizeof(ACMP)); + new_parser->pool = p; + new_parser->parent_pool = pool; + new_parser->is_utf8 = parser->is_utf8; + new_parser->is_case_sensitive = parser->is_case_sensitive; + new_parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t)); + new_parser->dict_count = parser->dict_count; + new_parser->longest_entry = parser->longest_entry; + acmp_copy_nodes_recursive(parser->root_node, new_parser->root_node, new_parser->pool); + acmp_prepare(new_parser); + return new_parser; +} + +/** + * Creates fail tree and initializes buffer + */ +apr_status_t acmp_prepare(ACMP *parser) { + if (parser->bp_buff_len < parser->longest_entry) { + parser->bp_buff_len = parser->longest_entry * 2; + parser->bp_buffer = apr_pcalloc(parser->pool, sizeof(apr_size_t) * parser->bp_buff_len); + } + apr_status_t st = acmp_connect_fail_branches(parser); + parser->active_node = parser->root_node; + if (st != APR_SUCCESS) return st; + parser->is_active = 1; + return APR_SUCCESS; +} + +/** + * Adds pattern to parser + * parser - ACMP parser + * pattern - string with pattern to match + * callback - Optional, pointer to an acmp_callback_t function + * data - pointer to data that will be passed to callback function, only used if callback + * is supplied + * len - Length of pattern in characters, if zero string length is used. + */ +apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, + acmp_callback_t callback, void *data, apr_size_t len) +{ + if (parser->is_active != 0) return APR_EGENERAL; + size_t length = (len == 0) ? acmp_strlen(parser, pattern) : len; + size_t i, j; + acmp_utf8_char_t ucs_chars[length]; + + acmp_node_t *parent = parser->root_node, *child; + acmp_strtoucs(parser, pattern, ucs_chars, length); + + for (i = 0; i < length; i++) { + acmp_utf8_char_t letter = ucs_chars[i]; + if (parser->is_case_sensitive == 0) { + letter = utf8_lcase(letter); + } + child = acmp_child_for_code(parent, letter); + if (child == NULL) { + child = apr_pcalloc(parser->pool, sizeof(acmp_node_t)); + child->pattern = ""; + child->letter = letter; + child->depth = i; + child->text = apr_pcalloc(parser->pool, strlen(pattern) + 2); + for (j = 0; j <= i; j++) child->text[j] = pattern[j]; + } + if (i == length - 1) { + if (child->is_last == 0) { + parser->dict_count++; + child->is_last = 1; + child->pattern = apr_pcalloc(parser->pool, strlen(pattern) + 2); + strcpy(child->pattern, pattern); + } + child->callback = callback; + child->callback_data = data; + } + acmp_add_node_to_parent(parent, child); + parent = child; + } + if (length > parser->longest_entry) parser->longest_entry = length; + parser->is_failtree_done = 0; + + return APR_SUCCESS; +} + +/** + * Called to process incoming data stream + * data - ptr to incoming data + * len - size of data in bytes + */ +apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len) { + if (parser->is_failtree_done == 0) acmp_prepare(parser); + acmp_node_t *node = parser->active_node, *go_to; + apr_size_t seq_length; + const char *end = (data + len); + + while (data < end) { + parser->bp_buffer[parser->char_pos % parser->bp_buff_len] = parser->byte_pos; + acmp_utf8_char_t letter; + if (parser->is_utf8) { + if (parser->u8buff_len > 0) { + /* Resuming partial utf-8 sequence */ + seq_length = utf8_seq_len(parser->u8_buff); + for (;;) { + parser->u8_buff[parser->u8buff_len++] = *data++; + if (parser->u8buff_len == seq_length) { + parser->u8buff_len = 0; + letter = utf8_decodechar(parser->u8_buff); + parser->byte_pos += seq_length; + parser->char_pos++; + break; + } + } + } else { + /* not resuming partial sequence, reading from the stream */ + seq_length = utf8_seq_len(data); + if ((data + seq_length) > end) { + while (data < end) parser->u8_buff[parser->u8buff_len++] = *data++; + return APR_SUCCESS; + } else { + letter = utf8_decodechar(data); + data += seq_length; + parser->byte_pos += seq_length; + parser->char_pos++; + } + } + } else { + letter = *data++; + parser->byte_pos++; + parser->char_pos++; + } + if (parser->is_case_sensitive == 0) letter = utf8_lcase(letter); + + go_to = NULL; + while (go_to == NULL) { + acmp_node_t *n2 = acmp_goto(node, letter); + go_to = acmp_child_for_code(node, letter); + if (n2 != go_to) { + n2 = acmp_goto(node, letter); + }; + if (go_to != NULL) { + if (go_to->is_last) { + acmp_found(parser, go_to); + } + } + if (node == parser->root_node) break; + if (go_to == NULL) node = node->fail; + } + if (go_to != NULL) node = go_to; + + /* We need to collect other nodes that are last letters of phrase. These + * will be fail node of current node if it has is_last flag set, and + * fail node of that node, recursively down to root node. + */ + go_to = node; + if (go_to != parser->root_node) { + for (go_to = go_to->o_match; go_to != NULL; go_to = go_to->o_match) { + acmp_found(parser, go_to); + } + } + } + parser->active_node = node; + return parser->hit_count > 0 ? 1 : 0; +} + +/** + * Resets the state of parser so you can start using it with new set of data. + * + * No need to clear buffer since it will be re-initialized at first run of + * acmp_process + */ +void acmp_reset(ACMP *parser) { + parser->is_active = 0; + parser->byte_pos = 0; + parser->char_pos = 0; + parser->hit_count = 0; + parser->u8buff_len = 0; + acmp_clear_hit_count_recursive(parser->root_node); +} + +/** + * Creates an ACMPT struct that will use parser's tree, without duplicating its data + */ +ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool) { + apr_pool_t *p = (pool != NULL) ? pool : parser->pool; + ACMPT *dup = apr_pcalloc(p, sizeof(ACMPT)); + dup->parser = parser; + return dup; +} + +/** + * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree + */ +apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len) { + if (acmpt->parser->is_failtree_done == 0) { + acmp_prepare(acmpt->parser); + }; + ACMP *parser = acmpt->parser; + if (acmpt->ptr == NULL) acmpt->ptr = parser->root_node; + acmp_node_t *node = acmpt->ptr, *go_to; + const char *end = (data + len); + + while (data < end) { + acmp_utf8_char_t letter = (unsigned char)*data++; + go_to = NULL; + while (go_to == NULL) { + go_to = acmp_goto(node, letter); + if (go_to != NULL) { + if (go_to->is_last) { + *match = go_to->text; + return 1; + } + } + if (node == parser->root_node) break; + if (go_to == NULL) node = node->fail; + } + if (go_to != NULL) node = go_to; + + /* If node has o_match, then we found a pattern */ + if (node->o_match != NULL) { + *match = node->text; + return 1; + } + } + acmpt->ptr = node; + return 0; +} diff --git a/apache2/acmp.h b/apache2/acmp.h new file mode 100644 index 00000000..40865ac7 --- /dev/null +++ b/apache2/acmp.h @@ -0,0 +1,115 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) + * + * You should have received a copy of the licence along with this + * program (stored in the file "LICENSE"). If the file is missing, + * or if you have any other questions related to the licence, please + * write to Breach Security, Inc. at support@breach.com. + * + */ +#ifndef ACMP_H_ +#define ACMP_H_ + +#include +#include + +#define ACMP_FLAG_BYTE 0 +#define ACMP_FLAG_UTF8 0x100 +#define ACMP_FLAG_CASE_SENSITIVE 1 +#define ACMP_FLAG_CASE_INSENSITIVE 0 + +/** + * Opaque struct with parser data + */ +typedef struct ACMP ACMP; + +/** + * Used to separate state from the trie for acmp_process_quick function + */ +typedef struct { + ACMP *parser; + void *ptr; +} ACMPT; + +/** + * Callback function. Arguments are: + * ACMP * - acmp parser that initiated callback + * void * - custom data you supplied when adding callback + * apr_size_t - position in bytes where pattern was found + * apr_size_t - position in chars where pattern was found, for multibyte strings + */ +typedef void (*acmp_callback_t)(ACMP *, void *, apr_size_t, apr_size_t); + +/** + * flags - OR-ed values of ACMP_FLAG constants + * pool - apr_pool to use as parent pool, can be set to NULL + */ +ACMP *acmp_create(int flags, apr_pool_t *pool); + +/** + * Destroys previously created parser + */ +void acmp_destroy(ACMP *parser); + +/** + * Creates parser with same options and same patterns + * parser - ACMP parser to duplicate + * pool - parent pool to use, if left as NULL original parser's parent pool is used + */ +ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool); + +/** + * Adds pattern to parser. Cannot be done after starting the search. + * parser - ACMP parser + * pattern - string with pattern to match + * callback - Optional, pointer to an acmp_callback_t function + * data - pointer to data that will be passed to callback function, only used if callback + * is supplied + * len - Length of pattern in characters, if zero string length is used. + */ +apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern, + acmp_callback_t callback, void *data, apr_size_t len); + +/** + * Called to process incoming data stream. You must call acmp_done after sending + * last data packet + * + * data - ptr to incoming data + * len - size of data in bytes + */ +apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len); + +/** + * Returns number of matches on all patterns combined + */ +apr_size_t acmp_match_count_total(ACMP *parser); + +/** + * Returns number of matches for given pattern + */ +apr_size_t acmp_match_count(ACMP *parser, const char *pattern); + +/** + * Resets the state of parser so you can start using it with new set of data, + * or add new patterns. + */ +void acmp_reset(ACMP *parser); + +/** + * Creates an ACMPT struct that will use parser's tree, without duplicating its data + */ +ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool); + +/** + * Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree + */ +apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len); + +/** + * Prepares parser for searching + */ +apr_status_t acmp_prepare(ACMP *parser); + + +#endif /*ACMP_H_*/ diff --git a/apache2/apache2.h b/apache2/apache2.h index 6f8d68e6..1f3e9571 100644 --- a/apache2/apache2.h +++ b/apache2/apache2.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id$ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _APACHE2_H_ diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index e19da940..526934c3 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1,20 +1,18 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: apache2_config.c,v 1.8 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include #include "modsecurity.h" #include "msc_logging.h" - +#include "pdf_protect.h" #include "http_log.h" /* #define DEBUG_CONF 1 */ @@ -90,6 +88,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) { dcfg->pdfp_timeout = NOT_SET; dcfg->pdfp_token_name = NOT_SET_P; dcfg->pdfp_only_get = NOT_SET; + dcfg->pdfp_method = NOT_SET; /* Geo Lookups */ dcfg->geo = NOT_SET_P; @@ -384,6 +383,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { ? parent->pdfp_token_name : child->pdfp_token_name); merged->pdfp_only_get = (child->pdfp_only_get == NOT_SET ? parent->pdfp_only_get : child->pdfp_only_get); + merged->pdfp_method = (child->pdfp_method == NOT_SET + ? parent->pdfp_method : child->pdfp_method); /* Geo Lookup */ merged->geo = (child->geo == NOT_SET_P @@ -456,7 +457,8 @@ void init_directory_config(directory_config *dcfg) { if (dcfg->pdfp_secret == NOT_SET_P) dcfg->pdfp_secret = NULL; if (dcfg->pdfp_timeout == NOT_SET) dcfg->pdfp_timeout = 10; if (dcfg->pdfp_token_name == NOT_SET_P) dcfg->pdfp_token_name = "PDFPTOKEN"; - if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 0; + if (dcfg->pdfp_only_get == NOT_SET) dcfg->pdfp_only_get = 1; + if (dcfg->pdfp_method == NOT_SET) dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; /* Geo Lookup */ if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL; @@ -1195,6 +1197,25 @@ static const char *cmd_pdf_protect_intercept_get_only(cmd_parms *cmd, void *_dcf return NULL; } +static const char *cmd_pdf_protect_method(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + if (dcfg == NULL) return NULL; + + if (strcasecmp(p1, "TokenRedirection") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_TOKEN_REDIRECTION; + } else + if (strcasecmp(p1, "ForcedDownload") == 0) { + dcfg->pdfp_method = PDF_PROTECT_METHOD_FORCED_DOWNLOAD; + } else { + return (const char *)apr_psprintf(cmd->pool, + "ModSecurity: Unrecognised parameter value for SecPdfProtectMethod: %s", p1); + } + + return NULL; +} + /* -- Geo Lookup configuration -- */ static const char *cmd_geo_lookups_db(cmd_parms *cmd, void *_dcfg, @@ -1547,7 +1568,15 @@ const command_rec module_directives[] = { cmd_pdf_protect_intercept_get_only, NULL, RSRC_CONF, - "whether or not to intercept only GET requess." + "whether or not to intercept only GET and HEAD requess. Defaults to true." + ), + + AP_INIT_TAKE1 ( + "SecPdfProtectMethod", + cmd_pdf_protect_method, + NULL, + RSRC_CONF, + "protection method to use. Can be 'TokenRedirection' (default) or 'ForcedDownload'" ), AP_INIT_TAKE1 ( diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index c1656773..72d05141 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: apache2_io.c,v 1.6 2007/01/23 16:08:15 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 817ddc03..f60d1ce8 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: apache2_util.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" @@ -255,6 +253,7 @@ void msr_log(modsec_rec *msr, int level, const char *text, ...) { va_end(ap); } + /** * Converts an Apache error log message into one line of text. */ diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 34b25771..a179cda0 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: mod_security2.c,v 1.11 2006/12/15 15:06:04 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include @@ -55,14 +53,30 @@ int perform_interception(modsec_rec *msr) { msre_actionset *actionset = NULL; const char *message = NULL; const char *phase_text = ""; + const char *intreq_text = ""; + int is_initial_req = ap_is_initial_req(msr->r); int status = DECLINED; int log_level = 1; + /* Check for an initial request */ + + if (is_initial_req != 1) { + if (msr->r->main != NULL) { + intreq_text = "Sub-Request: "; + } + else if (msr->r->prev != NULL) { + intreq_text = "Internal Redirect: "; + } + else { + intreq_text = "Internal Request: "; + } + } + + /* Sanity checks first. */ if (msr->was_intercepted == 0) { msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero"); - msr->was_intercepted = 0; return DECLINED; } @@ -78,12 +92,13 @@ int perform_interception(modsec_rec *msr) { phase_text = apr_psprintf(msr->mp, " (phase %i)", msr->phase); /* By default we log at level 1 but we switch to 4 - * if a nolog action was used to hide the message. + * if a nolog action was used or this is not the initial request + * to hide the message. */ - log_level = (actionset->log != 1) ? 4 : 1; + log_level = ((actionset->log != 1) || (is_initial_req != 1)) ? 4 : 1; - /* Pause the request first (if configured to do so). */ - if (actionset->intercept_pause) { + /* Pause the request first (if configured and the initial request). */ + if (actionset->intercept_pause && (is_initial_req == 1)) { msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for " "%i msec.", actionset->intercept_pause); /* apr_sleep accepts microseconds */ @@ -95,14 +110,14 @@ int perform_interception(modsec_rec *msr) { case ACTION_DENY : if (actionset->intercept_status != 0) { status = actionset->intercept_status; - message = apr_psprintf(msr->mp, "Access denied with code %i%s.", status, - phase_text); + message = apr_psprintf(msr->mp, "%sAccess denied with code %i%s.", + intreq_text, status, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " - "(Internal Error: Invalid status code requested %i).", phase_text, - actionset->intercept_status); + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " + "(Internal Error: Invalid status code requested %i).", + intreq_text, phase_text, actionset->intercept_status); } break; @@ -111,23 +126,25 @@ int perform_interception(modsec_rec *msr) { if (ap_find_linked_module("mod_proxy.c") == NULL) { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Configuration Error: Proxy action to %s requested but mod_proxy not found).", - phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); + intreq_text, phase_text, + log_escape_nq(msr->mp, actionset->intercept_uri)); } else { msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri); msr->r->proxyreq = PROXYREQ_REVERSE; msr->r->handler = "proxy-server"; status = OK; - message = apr_psprintf(msr->mp, "Access denied using proxy to %s%s.", - phase_text, log_escape_nq(msr->mp, actionset->intercept_uri)); + message = apr_psprintf(msr->mp, "%sAccess denied using proxy to%s %s.", + intreq_text, phase_text, + log_escape_nq(msr->mp, actionset->intercept_uri)); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Configuration Error: Proxy action requested but it does not work in output phases).", - phase_text); + intreq_text, phase_text); } break; @@ -144,29 +161,30 @@ int perform_interception(modsec_rec *msr) { if (csd) { if (apr_socket_close(csd) == APR_SUCCESS) { status = HTTP_FORBIDDEN; - message = apr_psprintf(msr->mp, "Access denied with connection close%s.", - phase_text); + message = apr_psprintf(msr->mp, "%sAccess denied with connection close%s.", + intreq_text, phase_text); } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop requested but failed to close the " - " socket).", phase_text); + " socket).", + intreq_text, phase_text); } } else { log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop requested but socket not found.", - phase_text); + intreq_text, phase_text); } } #else log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Error: Connection drop not implemented on this platform).", - phase_text); + intreq_text, phase_text); #endif break; @@ -179,23 +197,25 @@ int perform_interception(modsec_rec *msr) { } else { status = HTTP_MOVED_TEMPORARILY; } - message = apr_psprintf(msr->mp, "Access denied with redirection to %s using " - "status %i%s.", log_escape_nq(msr->mp, actionset->intercept_uri), status, + message = apr_psprintf(msr->mp, "%sAccess denied with redirection to %s using " + "status %i%s.", + intreq_text, + log_escape_nq(msr->mp, actionset->intercept_uri), status, phase_text); break; case ACTION_ALLOW : status = DECLINED; - message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text); + message = apr_psprintf(msr->mp, "%sAccess allowed%s.", intreq_text, phase_text); msr->was_intercepted = 0; break; default : log_level = 1; status = HTTP_INTERNAL_SERVER_ERROR; - message = apr_psprintf(msr->mp, "Access denied with code 500%s " + message = apr_psprintf(msr->mp, "%sAccess denied with code 500%s " "(Internal Error: invalid interception action %i).", - phase_text, actionset->intercept_action); + intreq_text, phase_text, actionset->intercept_action); break; } @@ -333,6 +353,8 @@ static modsec_rec *create_tx_context(request_rec *r) { msr_log(msr, 4, "Transaction context created (dcfg %x).", msr->dcfg1); } + msr->msc_rule_mptmp = NULL; + return msr; } @@ -562,6 +584,10 @@ static int hook_request_late(request_rec *r) { /* Has this phase been completed already? */ if (msr->phase_request_body_complete) { + if (msr->was_intercepted) { + msr_log(msr, 4, "Phase REQUEST_BODY request already intercepted. Intercepting additional request."); + return perform_interception(msr); + } if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Phase REQUEST_BODY already complete, skipping."); } diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index e890bc1b..8eab51f8 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: modsecurity.c,v 1.7 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include @@ -297,6 +295,9 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { msr->collections_dirty = apr_table_make(msr->mp, 8); if (msr->collections_dirty == NULL) return -1; + msr->tcache = apr_hash_make(msr->mp); + if (msr->tcache == NULL) return -1; + return 1; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index c72327f8..faf6bb9d 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: modsecurity.h,v 1.27 2007/02/05 12:44:40 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MODSECURITY_H_ @@ -26,7 +24,7 @@ typedef struct msc_data_chunk msc_data_chunk; typedef struct msc_arg msc_arg; typedef struct msc_string msc_string; -#if !(defined(WIN32) || defined(NETWARE)) +#if !(defined(WIN32) || defined(NETWARE) || defined(SOLARIS2)) #define DSOLOCAL __attribute__((visibility("hidden"))) #else #define DSOLOCAL @@ -45,13 +43,14 @@ typedef struct msc_string msc_string; #include "ap_config.h" #include "apr_md5.h" #include "apr_strings.h" +#include "apr_hash.h" #include "httpd.h" #include "http_config.h" #include "http_log.h" #include "http_protocol.h" #define MODULE_NAME "ModSecurity" -#define MODULE_RELEASE "2.2.0-dev1" +#define MODULE_RELEASE "2.5.0-dev2" #define MODULE_NAME_FULL (MODULE_NAME " v" MODULE_RELEASE " (Apache 2.x)") #define PHASE_REQUEST_HEADERS 1 @@ -59,6 +58,8 @@ typedef struct msc_string msc_string; #define PHASE_RESPONSE_HEADERS 3 #define PHASE_RESPONSE_BODY 4 #define PHASE_LOGGING 5 +#define PHASE_FIRST PHASE_REQUEST_HEADERS +#define PHASE_LAST PHASE_LOGGING #define NOT_SET -1 #define NOT_SET_P (void *)-1 @@ -319,6 +320,9 @@ struct modsec_rec { apr_off_t content_prepend_len; const char *content_append; apr_off_t content_append_len; + + /* data cache */ + apr_hash_t *tcache; }; struct directory_config { @@ -406,6 +410,7 @@ struct directory_config { int pdfp_timeout; const char *pdfp_token_name; int pdfp_only_get; + int pdfp_method; /* Geo Lookup */ geo_db *geo; diff --git a/apache2/modules.mk b/apache2/modules.mk index 2f9cfbb4..bce0eb7d 100644 --- a/apache2/modules.mk +++ b/apache2/modules.mk @@ -2,11 +2,11 @@ MOD_SECURITY2 = mod_security2 apache2_config apache2_io apache2_util \ re re_operators re_actions re_tfns re_variables \ msc_logging msc_xml msc_multipart modsecurity msc_parsers msc_util msc_pcre \ - persist_dbm msc_reqbody pdf_protect msc_geo + persist_dbm msc_reqbody pdf_protect msc_geo acmp H = re.h modsecurity.h msc_logging.h msc_multipart.h msc_parsers.h \ msc_pcre.h msc_util.h msc_xml.h persist_dbm.h apache2.h pdf_protect.h \ - msc_geo.h + msc_geo.h acmp.h utf8tables.h ${MOD_SECURITY2:=.slo}: ${H} ${MOD_SECURITY2:=.lo}: ${H} diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 798d8d28..4f0df69c 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -1,11 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_geo.h" @@ -228,6 +228,23 @@ static int db_open(directory_config *dcfg, char **error_msg) return 1; } +static int field_length(const char *field, int maxlen) +{ + int i; + + if (field == NULL) { + return 0; + } + + for (i = 0; i < maxlen; i++) { + if (field[i] == '\0') { + break; + } + } + + return i; +} + /** * Initialise Geo data structure */ @@ -375,21 +392,21 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro remaining -= rec_offset; /* Region */ - field_len = strnlen((const char *)cbuf+rec_offset,remaining); + field_len = field_length((const char *)cbuf+rec_offset, remaining); msr_log(msr, 9, "GEO: region=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); georec->region = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); rec_offset += field_len + 1; remaining -= field_len + 1; /* City */ - field_len = strnlen((const char *)cbuf+rec_offset,remaining); + field_len = field_length((const char *)cbuf+rec_offset, remaining); msr_log(msr, 9, "GEO: city=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); georec->city = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); rec_offset += field_len + 1; remaining -= field_len + 1; /* Postal Code */ - field_len = strnlen((const char *)cbuf+rec_offset,remaining); + field_len = field_length((const char *)cbuf+rec_offset, remaining); msr_log(msr, 9, "GEO: postal_code=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4)); georec->postal_code = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining)); rec_offset += field_len + 1; diff --git a/apache2/msc_geo.h b/apache2/msc_geo.h index 418cd0b3..4c962ffa 100644 --- a/apache2/msc_geo.h +++ b/apache2/msc_geo.h @@ -1,11 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_GEO_H_ diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index abe5a2a6..9935ef57 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_logging.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_logging.h" diff --git a/apache2/msc_logging.h b/apache2/msc_logging.h index 26ddeb0a..6cfaa181 100644 --- a/apache2/msc_logging.h +++ b/apache2/msc_logging.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_logging.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_LOGGING_H_ diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index a540ce46..f953bd90 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_multipart.c,v 1.2 2006/10/16 04:41:51 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h index 81bf65d6..cfcc35dc 100644 --- a/apache2/msc_multipart.h +++ b/apache2/msc_multipart.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_multipart.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_MULTIPART_H_ diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index b3720554..69be93e4 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_parsers.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_parsers.h" diff --git a/apache2/msc_parsers.h b/apache2/msc_parsers.h index ff7c0b8d..d4370c02 100644 --- a/apache2/msc_parsers.h +++ b/apache2/msc_parsers.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_parsers.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_PARSERS_H_ diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c index 11fd9e72..c4f99117 100644 --- a/apache2/msc_pcre.c +++ b/apache2/msc_pcre.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_pcre.c,v 1.2 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_pcre.h" diff --git a/apache2/msc_pcre.h b/apache2/msc_pcre.h index d9af38b5..6067de43 100644 --- a/apache2/msc_pcre.h +++ b/apache2/msc_pcre.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_pcre.h,v 1.3 2006/12/28 10:39:13 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_PCRE_H_ diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index f0f40530..f1586019 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_reqbody.c,v 1.2 2006/12/04 21:54:10 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" diff --git a/apache2/msc_util.c b/apache2/msc_util.c index f9ebb611..e4ac75a0 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_util.c,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "msc_util.h" @@ -561,8 +559,18 @@ int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len) if ( (VALID_HEX(input[i + 2]))&&(VALID_HEX(input[i + 3])) &&(VALID_HEX(input[i + 4]))&&(VALID_HEX(input[i + 5])) ) { - /* We make use of the lower byte here, ignoring the higher byte. */ - *d++ = x2c(&input[i + 4]); + /* We first make use of the lower byte here, ignoring the higher byte. */ + *d = x2c(&input[i + 4]); + + /* Full width ASCII (ff01 - ff5e) needs 0x20 added */ + if ( (*d > 0x00) && (*d < 0x5f) + && ((input[i + 2] == 'f') || (input[i + 2] == 'F')) + && ((input[i + 3] == 'f') || (input[i + 3] == 'F'))) + { + *d += 0x20; + } + + d++; count++; i += 6; } else { diff --git a/apache2/msc_util.h b/apache2/msc_util.h index f1ee8169..9a5548ae 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_util.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _UTIL_H_ diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index a8f7025d..df1e62d5 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_xml.c,v 1.2 2006/12/04 20:04:09 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifdef WITH_LIBXML2 diff --git a/apache2/msc_xml.h b/apache2/msc_xml.h index cea27cef..f8da4adb 100644 --- a/apache2/msc_xml.h +++ b/apache2/msc_xml.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: msc_xml.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_XML_H_ diff --git a/apache2/pdf_protect.c b/apache2/pdf_protect.c index 33e3b29d..15c2caaf 100644 --- a/apache2/pdf_protect.c +++ b/apache2/pdf_protect.c @@ -1,17 +1,14 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id$ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "modsecurity.h" -// #include "apache2.h" #include "pdf_protect.h" #include @@ -225,17 +222,21 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, - "ModSecurity: Internal Error: msr is null in PDF output filter."); + "ModSecurity: Internal Error: Unable to retrieve context in PDF output filter."); + ap_remove_output_filter(f); + return send_error_bucket(f, HTTP_INTERNAL_SERVER_ERROR); } if (msr->txcfg->pdfp_enabled == 1) { + // TODO Should we look at err_headers_out too? const char *h_content_type = apr_table_get(f->r->headers_out, "Content-Type"); if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: r->content_type=%s, header C-T=%s", - f->r->content_type, h_content_type); + log_escape_nq(msr->mp, f->r->content_type), + log_escape_nq(msr->mp, h_content_type)); } /* Have we been asked to tweak the headers? */ @@ -255,6 +256,10 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { } /* Proceed to detect dynamically-generated PDF files. */ + + // TODO application/x-pdf, application/vnd.fdf, application/vnd.adobe.xfdf, + // application/vnd.adobe.xdp+xml, application/vnd.adobe.xfd+xml, application/vnd.pdf + // application/acrobat, text/pdf, text/x-pdf ??? if (((f->r->content_type != NULL)&&(strcasecmp(f->r->content_type, "application/pdf") == 0)) || ((h_content_type != NULL)&&(strcasecmp(h_content_type, "application/pdf") == 0))) { @@ -263,16 +268,35 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: Detected a dynamically-generated PDF in %s", - r->uri); + log_escape_nq(msr->mp, r->uri)); } + /* If we are configured with ForcedDownload protection method then we + * can do our thing here and finish early. + */ + if (msr->txcfg->pdfp_method == PDF_PROTECT_METHOD_FORCED_DOWNLOAD) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically " + "generated PDF file."); + } + + apr_table_set(f->r->headers_out, "Content-Disposition", DISPOSITION_VALUE); + f->r->content_type = ATTACHMENT_MIME_TYPE; + + ap_remove_output_filter(f); + + return ap_pass_brigade(f->next, bb_in); + } + + /* If we are here that means TokenRedirection is the desired protection method. */ + /* Is this a non-GET request? */ if ((f->r->method_number != M_GET)&& ((msr->txcfg->pdfp_only_get == 1)||(msr->txcfg->pdfp_only_get == -1)) ) { /* This is a non-GET request and we have been configured - * not to intercept it. We are not going to do that but - * we are going to tweak the headers to force download. + * not to intercept it. So we are going to tweak the headers + * to force download. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: Forcing download of a dynamically " @@ -283,6 +307,7 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { f->r->content_type = ATTACHMENT_MIME_TYPE; ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb_in); } @@ -298,10 +323,11 @@ apr_status_t pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { /* Redirect user to the new URI. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: PDF request without a token - " - "redirecting to %s.", new_uri); + "redirecting to %s.", log_escape_nq(msr->mp, new_uri)); } apr_table_set(r->headers_out, "Location", new_uri); + return send_error_bucket(f, REDIRECT_STATUS); } } else { /* Token found. */ @@ -353,6 +379,16 @@ int pdfp_check(modsec_rec *msr) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Not enabled here."); } + + return 0; + } + + if (msr->txcfg->pdfp_method != PDF_PROTECT_METHOD_TOKEN_REDIRECTION) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "PdfProtect: Configured with ForcedDownload as protection method, " + "skipping detection on the inbound."); + } + return 0; } @@ -365,17 +401,18 @@ int pdfp_check(modsec_rec *msr) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "PdfProtect: Unable to inspect URI because it is NULL."); } - /* TODO Should we return -1 instead? */ - return 0; + + return -1; /* Error. */ } if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: URI=%s, filename=%s, QUERY_STRING=%s.", - msr->r->uri, msr->r->filename, msr->r->args); + log_escape_nq(msr->mp, msr->r->uri), log_escape_nq(msr->mp, msr->r->filename), + log_escape_nq(msr->mp, msr->r->args)); } uri = apr_pstrdup(msr->mp, msr->r->uri); - if (uri == NULL) return -1; + if (uri == NULL) return -1; /* Error. */ ap_str_tolower(uri); /* Attempt to figure out if this is a request for a PDF file. We are @@ -389,17 +426,19 @@ int pdfp_check(modsec_rec *msr) { msr_log(msr, 4, "PdfProtect: No indication in the URI this is a " "request for a PDF file."); } + return 0; } - /* Ignore request methods other than GET if + /* Ignore request methods other than GET and HEAD if * configured to do so. */ if ((msr->r->method_number != M_GET)&&(cfg->pdfp_only_get != 0)) { if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "PdfProtect: Configured not to intercept non-GET requests " - "(method=%s/%i).", msr->r->method, msr->r->method_number); + msr_log(msr, 4, "PdfProtect: Not intercepting a GET/HEAD request " + "(method=%s/%i).", log_escape_nq(msr->mp, msr->r->method), msr->r->method_number); } + return 0; } @@ -421,7 +460,7 @@ int pdfp_check(modsec_rec *msr) { /* Redirect user to the new URI. */ if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "PdfProtect: PDF request without a token - redirecting to %s.", - new_uri); + log_escape_nq(msr->mp, new_uri)); } apr_table_set(msr->r->headers_out, "Location", new_uri); @@ -437,6 +476,7 @@ int pdfp_check(modsec_rec *msr) { msr_log(msr, 9, "PdfProtect: PDF request with a valid token - " "serving PDF file normally."); } + return 0; } else { /* Not valid. */ /* The token is not valid. We will tweak the response diff --git a/apache2/pdf_protect.h b/apache2/pdf_protect.h index 3502a6e1..8936eb55 100644 --- a/apache2/pdf_protect.h +++ b/apache2/pdf_protect.h @@ -1,19 +1,20 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2007 Thinking Stone (http://www.thinkingstone.com) - * - * $Id$ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _PDF_PROTECT_H_ #define _PDF_PROTECT_H_ +#define PDF_PROTECT_METHOD_TOKEN_REDIRECTION 1 +#define PDF_PROTECT_METHOD_FORCED_DOWNLOAD 2 + apr_status_t DSOLOCAL pdfp_output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in); int DSOLOCAL pdfp_check(modsec_rec *msr); diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index cb616c97..f5c31cef 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: persist_dbm.c,v 1.3 2006/12/21 19:57:41 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "persist_dbm.h" diff --git a/apache2/persist_dbm.h b/apache2/persist_dbm.h index fe006a43..6a5af98f 100644 --- a/apache2/persist_dbm.h +++ b/apache2/persist_dbm.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: persist_dbm.h,v 1.1.1.1 2006/10/14 09:30:43 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _PERSIST_DBM_H_ diff --git a/apache2/re.c b/apache2/re.c index cd630a70..20dfadb1 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re.c,v 1.15 2006/12/29 10:44:25 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include @@ -294,7 +292,7 @@ int msre_parse_generic(apr_pool_t *mp, const char *text, apr_table_t *vartable, /* we are at the beginning of the name */ name = p; - while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; // ENH replace with isvarnamechar() + while((*p != '\0')&&(*p != '|')&&(*p != ':')&&(*p != ',')&&(!isspace(*p))) p++; /* ENH replace with isvarnamechar() */ /* get the name */ name = apr_pstrmemdup(mp, name, p - name); @@ -421,6 +419,7 @@ msre_actionset *msre_actionset_create(msre_engine *engine, const char *text, actionset->msg = NOT_SET_P; actionset->phase = NOT_SET; actionset->severity = -1; + actionset->rule = NOT_SET_P; /* Flow */ actionset->is_chained = NOT_SET; @@ -495,6 +494,7 @@ msre_actionset *msre_actionset_merge(msre_engine *engine, msre_actionset *parent if (child->msg != NOT_SET_P) merged->msg = child->msg; if (child->severity != NOT_SET) merged->severity = child->severity; if (child->phase != NOT_SET) merged->phase = child->phase; + if (child->rule != NOT_SET_P) merged->rule = child->rule; /* Flow */ merged->is_chained = child->is_chained; @@ -550,6 +550,7 @@ static void msre_actionset_set_defaults(msre_actionset *actionset) { if (actionset->msg == NOT_SET_P) actionset->msg = NULL; if (actionset->phase == NOT_SET) actionset->phase = 2; if (actionset->severity == -1); /* leave at -1 */ + if (actionset->rule == NOT_SET_P) actionset->rule = NULL; /* Flow */ if (actionset->is_chained == NOT_SET) actionset->is_chained = 0; @@ -844,6 +845,8 @@ int msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase) { */ msre_actionset_set_defaults(rule->actionset); + rule->actionset->rule = rule; + *(const msre_rule **)apr_array_push(arr) = rule; return 1; @@ -972,14 +975,23 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { char *msg = ""; char *severity = ""; char *tags = ""; + char *fn = ""; int k; if (actionset == NULL) return ""; - if (actionset->id != NULL) id = apr_psprintf(msr->mp, " [id \"%s\"]", - log_escape(msr->mp, actionset->id)); - if (actionset->rev != NULL) rev = apr_psprintf(msr->mp, " [rev \"%s\"]", - log_escape(msr->mp, actionset->rev)); + if ((actionset->rule != NULL) && (actionset->rule->filename != NULL)) { + fn = apr_psprintf(msr->mp, " [file \"%s\"] [line \"%d\"]", + actionset->rule->filename, actionset->rule->line_num); + } + if (actionset->id != NULL) { + id = apr_psprintf(msr->mp, " [id \"%s\"]", + log_escape(msr->mp, actionset->id)); + } + if (actionset->rev != NULL) { + rev = apr_psprintf(msr->mp, " [rev \"%s\"]", + log_escape(msr->mp, actionset->rev)); + } if (actionset->msg != NULL) { /* Expand variables in the message string. */ msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); @@ -988,11 +1000,11 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { expand_macros(msr, var, NULL, msr->mp); msg = apr_psprintf(msr->mp, " [msg \"%s\"]", - log_escape_ex(msr->mp, var->value, var->value_len)); + log_escape_ex(msr->mp, var->value, var->value_len)); } if ((actionset->severity >= 0)&&(actionset->severity <= 7)) { severity = apr_psprintf(msr->mp, " [severity \"%s\"]", - msre_format_severity(actionset->severity)); + msre_format_severity(actionset->severity)); } /* Extract rule tags from the action list. */ @@ -1007,7 +1019,7 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { } } - return apr_pstrcat(msr->mp, id, rev, msg, severity, tags, NULL); + return apr_pstrcat(msr->mp, fn, id, rev, msg, severity, tags, NULL); } /** @@ -1175,7 +1187,7 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, msre_actionset *acting_actionset, apr_pool_t *mptmp) { - apr_time_t time_before_regex; + apr_time_t time_before_regex = 0; char *my_error_msg = NULL; const char *full_varname = NULL; int rc; @@ -1210,7 +1222,9 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, var->value_len)); } - time_before_regex = apr_time_now(); /* IMP1 time_before_regex? */ + if (msr->txcfg->debuglog_level >= 4) { + time_before_regex = apr_time_now(); /* IMP1 time_before_regex? */ + } rc = rule->op_metadata->execute(msr, rule, var, &my_error_msg); if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", @@ -1280,9 +1294,6 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { /* Use a fresh memory sub-pool for processing each rule */ if (msr->msc_rule_mptmp == NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Creating new rule processing memory pool"); - } if (apr_pool_create(&msr->msc_rule_mptmp, msr->mp) != APR_SUCCESS) { return -1; } @@ -1351,11 +1362,27 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { { const apr_array_header_t *tarr; const apr_table_entry_t *telts; + msre_cache_rec **carr = NULL; + msre_cache_rec *crec = NULL; + char *tfnsvar = NULL; + char *tfnskey = NULL; + int tfnscount = 0; + int usecache = 0; apr_table_t *normtab; int k; msre_action *action; msre_tfn_metadata *metadata; + /* Is this var cacheable? */ + if (var->metadata->is_cacheable == VAR_CACHE) { + usecache = 1; + tfnsvar = apr_psprintf(msr->mp, "%lx;%s", (unsigned long)var, var->name); + tfnskey = tfnsvar; + } + else { + msr_log(msr, 9, "CACHE: %s transformations are not cacheable", var->name); + } + normtab = apr_table_make(mptmp, 10); if (normtab == NULL) return -1; tarr = apr_table_elts(rule->actionset->actions); @@ -1367,6 +1394,8 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { if (strcmp(telts[k].key, "t") == 0) { if (strcmp(action->param, "none") == 0) { apr_table_clear(normtab); + tfnskey = tfnsvar; + tfnscount = 0; continue; } @@ -1374,12 +1403,55 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { apr_table_unset(normtab, action->param); } else { apr_table_addn(normtab, action->param, (void *)action); + tfnskey = apr_psprintf(msr->mp, "%s,%s", tfnskey, action->param); + tfnscount++; } } } /* Perform transformations. */ + /* Try to fetch the full multi-transformation from cache */ + if (usecache && tfnscount > 1 && !multi_match) { + crec = NULL; + msr_log(msr, 9, "CACHE: Fetching %s (multi)", tfnskey); + carr = (msre_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); + if (carr != NULL) { + crec = carr[msr->phase]; + } + + /* Cache Miss - Reset the key to perform transformations */ + if (crec == NULL) { + tfnskey = tfnsvar; + } + /* Cache Hit - Use cache value and execute immediatly */ + else { + crec->hits++; + if (crec->changed) { + var->value = apr_pmemdup(msr->mp, crec->val, crec->val_len); + var->value_len = crec->val_len; + } + + msr_log(msr, 9, "T (%i) %s: \"%s\" [cached hits=%d]", crec->changed, (tfnskey + strlen(tfnsvar) + 1), log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); + + rc = execute_operator(var, rule, msr, acting_actionset, mptmp); + + if (rc < 0) { + return -1; + } + if (rc == RULE_MATCH) { + /* Return straight away if the transaction + * was intercepted - no need to process the remaining + * targets. + */ + if (msr->was_intercepted) { + return RULE_MATCH; + } + } + continue; /* next target */ + } + } + tarr = apr_table_elts(normtab); /* Make a copy of the variable value so that @@ -1392,6 +1464,7 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { /* Execute transformations in a loop. */ + tfnskey = tfnsvar; changed = 1; telts = (const apr_table_entry_t*)tarr->elts; for (k = 0; k < tarr->nelts; k++) { @@ -1429,6 +1502,31 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { action = (msre_action *)telts[k].val; metadata = (msre_tfn_metadata *)action->param_data; + /* Try to use the cache */ + if (usecache) { + /* Generate the cache key */ + tfnskey = apr_psprintf(msr->mp, "%s,%s", tfnskey, action->param); + + /* Try to fetch this transformation from cache */ + msr_log(msr, 9, "CACHE: Fetching %s", tfnskey); + crec = NULL; + carr = (msre_cache_rec **)apr_hash_get(msr->tcache, tfnskey, APR_HASH_KEY_STRING); + if (carr != NULL) { + crec = carr[msr->phase]; + } + if (crec != NULL) { + crec->hits++; + + if ((changed = crec->changed) == 1) { + var->value = apr_pmemdup(msr->mp, crec->val, crec->val_len); + var->value_len = crec->val_len; + } + + msr_log(msr, 9, "T (%i) %s: \"%s\" [cached hits=%i]", crec->changed, metadata->name, log_escape_nq_ex(mptmp, var->value, var->value_len), crec->hits); + continue; + } + } + rc = metadata->execute(mptmp, (unsigned char *)var->value, var->value_len, &rval, &rval_length); if (rc < 0) { @@ -1440,6 +1538,26 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { var->value = rval; var->value_len = rval_length; + /* Cache the transformation */ + if (usecache) { + /* ENH1: Add flag to vars to tell which ones can change across phases store the rest in a global cache */ + if (carr == NULL) { + carr = (msre_cache_rec **)apr_pcalloc(msr->mp, (sizeof(msre_cache_rec *) * (PHASE_LAST + 1))); + if (carr == NULL) return -1; + memset(carr, 0, (sizeof(msre_cache_rec *) * (PHASE_LAST + 1))); + apr_hash_set(msr->tcache, tfnskey, APR_HASH_KEY_STRING, carr); + } + crec = carr[msr->phase] = (msre_cache_rec *)apr_pcalloc(msr->mp, sizeof(msre_cache_rec)); + if (crec == NULL) return -1; + + crec->hits = 0; + crec->changed = changed; + crec->key = tfnskey; + crec->val = changed ? apr_pmemdup(msr->mp, rval, rval_length) : NULL; + crec->val_len = changed ? rval_length : -1; + msr_log(msr, 9, "CACHE: Caching %s=\"%.*s\"", tfnskey, crec->val_len, crec->val); + } + if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "T (%i) %s: \"%s\"", rc, metadata->name, log_escape_nq_ex(mptmp, var->value, var->value_len)); @@ -1475,6 +1593,33 @@ apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { } + msr_log(msr, 9, "CACHE: size=%u", apr_hash_count(msr->tcache)); + #ifdef CACHE_DEBUG + if (msr->txcfg->debuglog_level >= 9) { + apr_hash_index_t *hi; + void *dummy; + msre_cache_rec **rec; + int hn = 0; + int ri; + for (hi = apr_hash_first(msr->mp, msr->tcache); hi; hi = apr_hash_next(hi)) { + hn++; + apr_hash_this(hi, NULL, NULL, &dummy); + rec = (msre_cache_rec **)dummy; + if (rec == NULL) continue; + + for (ri = PHASE_FIRST; ri <= PHASE_LAST; ri++) { + if (rec[ri] == NULL) continue; + if (rec[ri]->changed) { + msr_log(msr, 9, "CACHE: %5d) phase=%d hits=%d %s=\"%s\"", hn, msr->phase, rec[ri]->hits, rec[ri]->key, log_escape_nq_ex(mptmp, rec[ri]->val, rec[ri]->val_len)); + } + else { + msr_log(msr, 9, "CACHE: %5d) phase=%d hits=%d %s=", hn, msr->phase, rec[ri]->hits, rec[ri]->key); + } + } + } + } + #endif + return (match_count ? RULE_MATCH : RULE_NO_MATCH); } diff --git a/apache2/re.h b/apache2/re.h index 203a5089..09111279 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re.h,v 1.7 2006/12/29 10:31:38 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #ifndef _MSC_RE_H_ @@ -28,6 +26,7 @@ typedef struct msre_tfn_metadata msre_tfn_metadata; typedef struct msre_actionset msre_actionset; typedef struct msre_action_metadata msre_action_metadata; typedef struct msre_action msre_action; +typedef struct msre_cache_rec msre_cache_rec; #include "apr_general.h" #include "apr_tables.h" @@ -226,6 +225,7 @@ struct msre_actionset { const char *msg; int severity; int phase; + msre_rule *rule; /* Flow */ int is_chained; @@ -303,4 +303,15 @@ apr_status_t DSOLOCAL msre_parse_vars(msre_ruleset *ruleset, const char *text, char DSOLOCAL *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset); + +/* -- Data Cache -- */ + +struct msre_cache_rec { + int hits; + int changed; + const char *key; + const char *val; + apr_size_t val_len; +}; + #endif diff --git a/apache2/re_actions.c b/apache2/re_actions.c index ede2fc7d..fd0453a2 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_actions.c,v 1.9 2007/02/02 18:16:41 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "re.h" diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 50ab6518..ea723d81 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -1,19 +1,19 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_operators.c,v 1.7 2007/01/23 16:08:15 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "re.h" #include "msc_pcre.h" #include "msc_geo.h" +#include "apr_lib.h" #include "apr_strmatch.h" +#include "acmp.h" /** * @@ -109,15 +109,12 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* Are we supposed to capture subexpressions? */ capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - /* Warn when the regex captures but "capture" is not set */ - if (msr->txcfg->debuglog_level >= 3) { + /* Show when the regex captures but "capture" is not set */ + if (msr->txcfg->debuglog_level >= 6) { int capcount = 0; rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount); if ((capture == 0) && (capcount > 0)) { - msr_log(msr, 4, "Ignoring regex captures since \"capture\" action is not enabled."); - } - if ((capture == 1) && (capcount == 0)) { - msr_log(msr, 3, "Notice. The \"capture\" action is enabled, but the regex does not have explicit captures."); + msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled."); } } @@ -182,23 +179,247 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c return 0; } -/* contains */ +/* pm */ -static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = (const char *)rule->op_param; +static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'."); + return 0; /* ERROR */ + } + + ACMP *p = acmp_create(0, rule->ruleset->mp); + if (p == NULL) return 0; + + const char *phrase = apr_pstrdup(rule->ruleset->mp, rule->op_param); + const char *next = rule->op_param + strlen(rule->op_param); + + /* Loop through phrases */ + /* ENH: Need to allow quoted phrases w/space */ + for (;;) { + while((isspace(*phrase) != 0) && (*phrase != '\0')) phrase++; + if (*phrase == '\0') break; + next = phrase; + while((isspace(*next) == 0) && (*next != 0)) next++; + acmp_add_pattern(p, phrase, NULL, NULL, next - phrase); + phrase = next; + } + acmp_prepare(p); + rule->op_param_data = p; + return 1; +} + +/* pmFromFile */ + +static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { + char errstr[1024]; + char buf[HUGE_STRING_LEN + 1]; + char *fn; + char *next; + char *ptr; + const char *rulefile_path; + apr_status_t rc; + apr_file_t *fd; + + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'."); + return 0; /* ERROR */ + } + + ACMP *p = acmp_create(0, rule->ruleset->mp); + if (p == NULL) return 0; + + fn = apr_pstrdup(rule->ruleset->mp, rule->op_param); + next = fn + strlen(rule->op_param); + + /* Get the path of the rule filename to use as a base */ + rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); + + #ifdef DEBUG_CONF + fprintf(stderr, "Rulefile path: \"%s\"\n", rulefile_path); + #endif + + /* Loop through filenames */ + /* ENH: Need to allow quoted filenames w/space */ + for (;;) { + const char *rootpath = NULL; + const char *filepath = NULL; + int line = 0; + + /* Trim whitespace */ + while((isspace(*fn) != 0) && (*fn != '\0')) fn++; + if (*fn == '\0') break; + next = fn; + while((isspace(*next) == 0) && (*next != '\0')) next++; + while((isspace(*next) != 0) && (*next != '\0')) *next++ = '\0'; + + /* Add path of the rule filename for a relative phrase filename */ + filepath = fn; + if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) { + /* We are not an absolute path. It could mean an error, but + * let that pass through to the open call for a better error */ + apr_filepath_merge(&fn, rulefile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp); + } + + /* Open file and read */ + rc = apr_file_open(&fd, fn, APR_READ | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open phrase file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + return 0; + } + + #ifdef DEBUG_CONF + fprintf(stderr, "Loading phrase file: \"%s\"\n", fn); + #endif + + /* Read one pattern per line skipping empty/commented */ + for(;;) { + line++; + rc = apr_file_gets(buf, HUGE_STRING_LEN, fd); + if (rc == APR_EOF) break; + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); + return 0; + } + + /* Remove newline */ + ptr = buf; + while(*ptr != '\0') ptr++; + if ((ptr > buf) && (*(ptr - 1) == '\n')) *(ptr - 1) = '\0'; + + /* Ignore empty lines and comments */ + ptr = buf; + while((*ptr != '\0') && apr_isspace(*ptr)) ptr++; + if ((*ptr == '\0') || (*ptr == '#')) continue; + + #ifdef DEBUG_CONF + fprintf(stderr, "Adding phrase file pattern: \"%s\"\n", buf); + #endif + + acmp_add_pattern(p, buf, NULL, NULL, strlen(buf)); + } + fn = next; + } + if (fd != NULL) apr_file_close(fd); + acmp_prepare(p); + rule->op_param_data = p; + return 1; +} + +static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + const char *match = NULL; + apr_status_t rc = 0; + + /* Nothing to read */ + if ((var->value == NULL) || (var->value_len == 0)) return 0; + + ACMPT pt = {(ACMP *)rule->op_param_data, NULL}; + + rc = acmp_process_quick(&pt, &match, var->value, var->value_len); + if (rc) { + char *match_escaped = log_escape(msr->mp, match ? match : ""); + + /* This message will be logged. */ + if (strlen(match_escaped) > 252) { + *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.", + match_escaped, var->name); + } else { + *error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.", + match_escaped, var->name); + } + return 1; + } + return rc; +} + +/* within */ + +static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; const char *target; unsigned int match_length; - unsigned int target_length; + unsigned int target_length = 0; unsigned int i, i_max; + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + if (error_msg == NULL) return -1; *error_msg = NULL; - if (match == NULL) { + if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; } + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null we give up without a match */ + if (var->value == NULL) { + /* No match. */ + return 0; + } + + target = var->value; + target_length = var->value_len; + + /* These are impossible to match */ + if ((match_length == 0) || (target_length > match_length)) { + /* No match. */ + return 0; + } + + /* scan for first character, then compare from there until we + * have a match or there is no room left in the target + */ + msr_log(msr, 9, "match[%d]='%s' target[%d]='%s'", match_length, match, target_length, target); + i_max = match_length - target_length; + for (i = 0; i <= i_max; i++) { + if (match[i] == target[0]) { + if (strncmp(target, (match + i), target_length) == 0) { + /* match. */ + *error_msg = apr_psprintf(msr->mp, "String match %s=\"%s\" within \"%s\".", + var->name, + log_escape_ex(msr->mp, target, target_length), + log_escape_ex(msr->mp, match, match_length)); + return 1; + } + } + } + + /* No match. */ + return 0; +} + +/* contains */ + +static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length = 0; + unsigned int i, i_max; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -211,8 +432,6 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * target_length = var->value_len; } - match_length = strlen(match); - /* These are impossible to match */ if ((match_length == 0) || (match_length > target_length)) { /* No match. */ @@ -294,22 +513,31 @@ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var return 0; } -/* startsWith */ +/* beginsWith */ -static int msre_op_startsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = (const char *)rule->op_param; +static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + if (error_msg == NULL) return -1; *error_msg = NULL; - if (match == NULL) { + if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; } + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -322,8 +550,6 @@ static int msre_op_startsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var target_length = var->value_len; } - match_length = strlen(match); - /* These are impossible to match */ if ((match_length == 0) || (match_length > target_length)) { /* No match. */ @@ -345,19 +571,28 @@ static int msre_op_startsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var /* endsWith */ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - const char *match = (const char *)rule->op_param; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + if (error_msg == NULL) return -1; *error_msg = NULL; - if (match == NULL) { + if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; } + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + /* If the given target is null run against an empty * string. This is a behaviour consistent with previous * releases. @@ -370,8 +605,6 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * target_length = var->value_len; } - match_length = strlen(match); - /* These are impossible to match */ if ((match_length == 0) || (match_length > target_length)) { /* No match. */ @@ -1197,6 +1430,27 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_rx_execute ); + /* pm */ + msre_engine_op_register(engine, + "pm", + msre_op_pm_param_init, + msre_op_pm_execute + ); + + /* pmFromFile */ + msre_engine_op_register(engine, + "pmFromFile", + msre_op_pmFromFile_param_init, + msre_op_pm_execute + ); + + /* within */ + msre_engine_op_register(engine, + "within", + NULL, /* ENH init function to flag var substitution */ + msre_op_within_execute + ); + /* contains */ msre_engine_op_register(engine, "contains", @@ -1211,11 +1465,11 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_streq_execute ); - /* startsWith */ + /* beginsWith */ msre_engine_op_register(engine, - "startsWith", + "beginsWith", NULL, /* ENH init function to flag var substitution */ - msre_op_startsWith_execute + msre_op_beginsWith_execute ); /* endsWith */ diff --git a/apache2/re_tfns.c b/apache2/re_tfns.c index 967e38ee..97a4664b 100644 --- a/apache2/re_tfns.c +++ b/apache2/re_tfns.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_tfns.c,v 1.3 2006/12/04 12:00:24 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include diff --git a/apache2/re_variables.c b/apache2/re_variables.c index faefac89..6dcd2c87 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -1,13 +1,11 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2006 Thinking Stone (http://www.thinkingstone.com) - * - * $Id: re_variables.c,v 1.7 2007/01/23 16:08:15 ivanr Exp $ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) * * You should have received a copy of the licence along with this * program (stored in the file "LICENSE"). If the file is missing, * or if you have any other questions related to the licence, please - * write to Thinking Stone at contact@thinkingstone.com. + * write to Breach Security, Inc. at support@breach.com. * */ #include "http_core.h" @@ -1670,14 +1668,6 @@ static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rul return var_simple_generate(var, vartab, mptmp, value); } -/* RESPONSE_CONTENT_ENCODING */ - -static int var_response_content_encoding(modsec_rec *msr, msre_var *var, msre_rule *rule, - apr_table_t *vartab, apr_pool_t *mptmp) -{ - return var_simple_generate(var, vartab, mptmp, msr->r->content_encoding); -} - /* RESPONSE_CONTENT_TYPE */ static int var_response_content_type(modsec_rec *msr, msre_var *var, msre_rule *rule, @@ -2470,17 +2460,6 @@ void msre_engine_register_default_variables(msre_engine *engine) { PHASE_RESPONSE_HEADERS ); - /* RESPONSE_CONTENT_ENCODING */ - msre_engine_variable_register(engine, - "RESPONSE_CONTENT_ENCODING", - VAR_SIMPLE, - 0, 0, - NULL, - var_response_content_encoding, - VAR_CACHE, - PHASE_RESPONSE_HEADERS - ); - /* RESPONSE_CONTENT_TYPE */ msre_engine_variable_register(engine, "RESPONSE_CONTENT_TYPE", diff --git a/apache2/utf8tables.h b/apache2/utf8tables.h new file mode 100644 index 00000000..f12dc19b --- /dev/null +++ b/apache2/utf8tables.h @@ -0,0 +1,810 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2007 Breach Security, Inc. (http://www.breach.com/) + * + * You should have received a copy of the licence along with this + * program (stored in the file "LICENSE"). If the file is missing, + * or if you have any other questions related to the licence, please + * write to Breach Security, Inc. at support@breach.com. + * + */ +#ifndef UTF8TABLES_H_ +#define UTF8TABLES_H_ + +/** + * This include file is used by acmp.c only, it's not included anywhere else + */ + +typedef long acmp_utf8_char_t; + +static const char utf8_seq_lengths[256] = { + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,6,6, +}; + +static const acmp_utf8_char_t utf8_offsets[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +/** + * How many element pairs are there in utf8_lcase_map + */ +#define UTF8_LCASEMAP_LEN 759 + +/** + * Table mapping is from PHP's mbstring extension, maps uppercase + */ +static const acmp_utf8_char_t utf8_lcase_map[UTF8_LCASEMAP_LEN * 2] = { + 0x00000061, 0x00000041, + 0x00000062, 0x00000042, + 0x00000063, 0x00000043, + 0x00000064, 0x00000044, + 0x00000065, 0x00000045, + 0x00000066, 0x00000046, + 0x00000067, 0x00000047, + 0x00000068, 0x00000048, + 0x00000069, 0x00000049, + 0x0000006a, 0x0000004a, + 0x0000006b, 0x0000004b, + 0x0000006c, 0x0000004c, + 0x0000006d, 0x0000004d, + 0x0000006e, 0x0000004e, + 0x0000006f, 0x0000004f, + 0x00000070, 0x00000050, + 0x00000071, 0x00000051, + 0x00000072, 0x00000052, + 0x00000073, 0x00000053, + 0x00000074, 0x00000054, + 0x00000075, 0x00000055, + 0x00000076, 0x00000056, + 0x00000077, 0x00000057, + 0x00000078, 0x00000058, + 0x00000079, 0x00000059, + 0x0000007a, 0x0000005a, + 0x000000b5, 0x0000039c, + 0x000000e0, 0x000000c0, + 0x000000e1, 0x000000c1, + 0x000000e2, 0x000000c2, + 0x000000e3, 0x000000c3, + 0x000000e4, 0x000000c4, + 0x000000e5, 0x000000c5, + 0x000000e6, 0x000000c6, + 0x000000e7, 0x000000c7, + 0x000000e8, 0x000000c8, + 0x000000e9, 0x000000c9, + 0x000000ea, 0x000000ca, + 0x000000eb, 0x000000cb, + 0x000000ec, 0x000000cc, + 0x000000ed, 0x000000cd, + 0x000000ee, 0x000000ce, + 0x000000ef, 0x000000cf, + 0x000000f0, 0x000000d0, + 0x000000f1, 0x000000d1, + 0x000000f2, 0x000000d2, + 0x000000f3, 0x000000d3, + 0x000000f4, 0x000000d4, + 0x000000f5, 0x000000d5, + 0x000000f6, 0x000000d6, + 0x000000f8, 0x000000d8, + 0x000000f9, 0x000000d9, + 0x000000fa, 0x000000da, + 0x000000fb, 0x000000db, + 0x000000fc, 0x000000dc, + 0x000000fd, 0x000000dd, + 0x000000fe, 0x000000de, + 0x000000ff, 0x00000178, + 0x00000101, 0x00000100, + 0x00000103, 0x00000102, + 0x00000105, 0x00000104, + 0x00000107, 0x00000106, + 0x00000109, 0x00000108, + 0x0000010b, 0x0000010a, + 0x0000010d, 0x0000010c, + 0x0000010f, 0x0000010e, + 0x00000111, 0x00000110, + 0x00000113, 0x00000112, + 0x00000115, 0x00000114, + 0x00000117, 0x00000116, + 0x00000119, 0x00000118, + 0x0000011b, 0x0000011a, + 0x0000011d, 0x0000011c, + 0x0000011f, 0x0000011e, + 0x00000121, 0x00000120, + 0x00000123, 0x00000122, + 0x00000125, 0x00000124, + 0x00000127, 0x00000126, + 0x00000129, 0x00000128, + 0x0000012b, 0x0000012a, + 0x0000012d, 0x0000012c, + 0x0000012f, 0x0000012e, + 0x00000131, 0x00000049, + 0x00000133, 0x00000132, + 0x00000135, 0x00000134, + 0x00000137, 0x00000136, + 0x0000013a, 0x00000139, + 0x0000013c, 0x0000013b, + 0x0000013e, 0x0000013d, + 0x00000140, 0x0000013f, + 0x00000142, 0x00000141, + 0x00000144, 0x00000143, + 0x00000146, 0x00000145, + 0x00000148, 0x00000147, + 0x0000014b, 0x0000014a, + 0x0000014d, 0x0000014c, + 0x0000014f, 0x0000014e, + 0x00000151, 0x00000150, + 0x00000153, 0x00000152, + 0x00000155, 0x00000154, + 0x00000157, 0x00000156, + 0x00000159, 0x00000158, + 0x0000015b, 0x0000015a, + 0x0000015d, 0x0000015c, + 0x0000015f, 0x0000015e, + 0x00000161, 0x00000160, + 0x00000163, 0x00000162, + 0x00000165, 0x00000164, + 0x00000167, 0x00000166, + 0x00000169, 0x00000168, + 0x0000016b, 0x0000016a, + 0x0000016d, 0x0000016c, + 0x0000016f, 0x0000016e, + 0x00000171, 0x00000170, + 0x00000173, 0x00000172, + 0x00000175, 0x00000174, + 0x00000177, 0x00000176, + 0x0000017a, 0x00000179, + 0x0000017c, 0x0000017b, + 0x0000017e, 0x0000017d, + 0x0000017f, 0x00000053, + 0x00000183, 0x00000182, + 0x00000185, 0x00000184, + 0x00000188, 0x00000187, + 0x0000018c, 0x0000018b, + 0x00000192, 0x00000191, + 0x00000195, 0x000001f6, + 0x00000199, 0x00000198, + 0x0000019e, 0x00000220, + 0x000001a1, 0x000001a0, + 0x000001a3, 0x000001a2, + 0x000001a5, 0x000001a4, + 0x000001a8, 0x000001a7, + 0x000001ad, 0x000001ac, + 0x000001b0, 0x000001af, + 0x000001b4, 0x000001b3, + 0x000001b6, 0x000001b5, + 0x000001b9, 0x000001b8, + 0x000001bd, 0x000001bc, + 0x000001bf, 0x000001f7, + 0x000001c6, 0x000001c4, + 0x000001c9, 0x000001c7, + 0x000001cc, 0x000001ca, + 0x000001ce, 0x000001cd, + 0x000001d0, 0x000001cf, + 0x000001d2, 0x000001d1, + 0x000001d4, 0x000001d3, + 0x000001d6, 0x000001d5, + 0x000001d8, 0x000001d7, + 0x000001da, 0x000001d9, + 0x000001dc, 0x000001db, + 0x000001dd, 0x0000018e, + 0x000001df, 0x000001de, + 0x000001e1, 0x000001e0, + 0x000001e3, 0x000001e2, + 0x000001e5, 0x000001e4, + 0x000001e7, 0x000001e6, + 0x000001e9, 0x000001e8, + 0x000001eb, 0x000001ea, + 0x000001ed, 0x000001ec, + 0x000001ef, 0x000001ee, + 0x000001f3, 0x000001f1, + 0x000001f5, 0x000001f4, + 0x000001f9, 0x000001f8, + 0x000001fb, 0x000001fa, + 0x000001fd, 0x000001fc, + 0x000001ff, 0x000001fe, + 0x00000201, 0x00000200, + 0x00000203, 0x00000202, + 0x00000205, 0x00000204, + 0x00000207, 0x00000206, + 0x00000209, 0x00000208, + 0x0000020b, 0x0000020a, + 0x0000020d, 0x0000020c, + 0x0000020f, 0x0000020e, + 0x00000211, 0x00000210, + 0x00000213, 0x00000212, + 0x00000215, 0x00000214, + 0x00000217, 0x00000216, + 0x00000219, 0x00000218, + 0x0000021b, 0x0000021a, + 0x0000021d, 0x0000021c, + 0x0000021f, 0x0000021e, + 0x00000223, 0x00000222, + 0x00000225, 0x00000224, + 0x00000227, 0x00000226, + 0x00000229, 0x00000228, + 0x0000022b, 0x0000022a, + 0x0000022d, 0x0000022c, + 0x0000022f, 0x0000022e, + 0x00000231, 0x00000230, + 0x00000233, 0x00000232, + 0x00000253, 0x00000181, + 0x00000254, 0x00000186, + 0x00000256, 0x00000189, + 0x00000257, 0x0000018a, + 0x00000259, 0x0000018f, + 0x0000025b, 0x00000190, + 0x00000260, 0x00000193, + 0x00000263, 0x00000194, + 0x00000268, 0x00000197, + 0x00000269, 0x00000196, + 0x0000026f, 0x0000019c, + 0x00000272, 0x0000019d, + 0x00000275, 0x0000019f, + 0x00000280, 0x000001a6, + 0x00000283, 0x000001a9, + 0x00000288, 0x000001ae, + 0x0000028a, 0x000001b1, + 0x0000028b, 0x000001b2, + 0x00000292, 0x000001b7, + 0x00000345, 0x00000399, + 0x000003ac, 0x00000386, + 0x000003ad, 0x00000388, + 0x000003ae, 0x00000389, + 0x000003af, 0x0000038a, + 0x000003b1, 0x00000391, + 0x000003b2, 0x00000392, + 0x000003b3, 0x00000393, + 0x000003b4, 0x00000394, + 0x000003b5, 0x00000395, + 0x000003b6, 0x00000396, + 0x000003b7, 0x00000397, + 0x000003b8, 0x00000398, + 0x000003b9, 0x00000399, + 0x000003ba, 0x0000039a, + 0x000003bb, 0x0000039b, + 0x000003bc, 0x0000039c, + 0x000003bd, 0x0000039d, + 0x000003be, 0x0000039e, + 0x000003bf, 0x0000039f, + 0x000003c0, 0x000003a0, + 0x000003c1, 0x000003a1, + 0x000003c2, 0x000003a3, + 0x000003c3, 0x000003a3, + 0x000003c4, 0x000003a4, + 0x000003c5, 0x000003a5, + 0x000003c6, 0x000003a6, + 0x000003c7, 0x000003a7, + 0x000003c8, 0x000003a8, + 0x000003c9, 0x000003a9, + 0x000003ca, 0x000003aa, + 0x000003cb, 0x000003ab, + 0x000003cc, 0x0000038c, + 0x000003cd, 0x0000038e, + 0x000003ce, 0x0000038f, + 0x000003d0, 0x00000392, + 0x000003d1, 0x00000398, + 0x000003d5, 0x000003a6, + 0x000003d6, 0x000003a0, + 0x000003d9, 0x000003d8, + 0x000003db, 0x000003da, + 0x000003dd, 0x000003dc, + 0x000003df, 0x000003de, + 0x000003e1, 0x000003e0, + 0x000003e3, 0x000003e2, + 0x000003e5, 0x000003e4, + 0x000003e7, 0x000003e6, + 0x000003e9, 0x000003e8, + 0x000003eb, 0x000003ea, + 0x000003ed, 0x000003ec, + 0x000003ef, 0x000003ee, + 0x000003f0, 0x0000039a, + 0x000003f1, 0x000003a1, + 0x000003f2, 0x000003a3, + 0x000003f5, 0x00000395, + 0x00000430, 0x00000410, + 0x00000431, 0x00000411, + 0x00000432, 0x00000412, + 0x00000433, 0x00000413, + 0x00000434, 0x00000414, + 0x00000435, 0x00000415, + 0x00000436, 0x00000416, + 0x00000437, 0x00000417, + 0x00000438, 0x00000418, + 0x00000439, 0x00000419, + 0x0000043a, 0x0000041a, + 0x0000043b, 0x0000041b, + 0x0000043c, 0x0000041c, + 0x0000043d, 0x0000041d, + 0x0000043e, 0x0000041e, + 0x0000043f, 0x0000041f, + 0x00000440, 0x00000420, + 0x00000441, 0x00000421, + 0x00000442, 0x00000422, + 0x00000443, 0x00000423, + 0x00000444, 0x00000424, + 0x00000445, 0x00000425, + 0x00000446, 0x00000426, + 0x00000447, 0x00000427, + 0x00000448, 0x00000428, + 0x00000449, 0x00000429, + 0x0000044a, 0x0000042a, + 0x0000044b, 0x0000042b, + 0x0000044c, 0x0000042c, + 0x0000044d, 0x0000042d, + 0x0000044e, 0x0000042e, + 0x0000044f, 0x0000042f, + 0x00000450, 0x00000400, + 0x00000451, 0x00000401, + 0x00000452, 0x00000402, + 0x00000453, 0x00000403, + 0x00000454, 0x00000404, + 0x00000455, 0x00000405, + 0x00000456, 0x00000406, + 0x00000457, 0x00000407, + 0x00000458, 0x00000408, + 0x00000459, 0x00000409, + 0x0000045a, 0x0000040a, + 0x0000045b, 0x0000040b, + 0x0000045c, 0x0000040c, + 0x0000045d, 0x0000040d, + 0x0000045e, 0x0000040e, + 0x0000045f, 0x0000040f, + 0x00000461, 0x00000460, + 0x00000463, 0x00000462, + 0x00000465, 0x00000464, + 0x00000467, 0x00000466, + 0x00000469, 0x00000468, + 0x0000046b, 0x0000046a, + 0x0000046d, 0x0000046c, + 0x0000046f, 0x0000046e, + 0x00000471, 0x00000470, + 0x00000473, 0x00000472, + 0x00000475, 0x00000474, + 0x00000477, 0x00000476, + 0x00000479, 0x00000478, + 0x0000047b, 0x0000047a, + 0x0000047d, 0x0000047c, + 0x0000047f, 0x0000047e, + 0x00000481, 0x00000480, + 0x0000048b, 0x0000048a, + 0x0000048d, 0x0000048c, + 0x0000048f, 0x0000048e, + 0x00000491, 0x00000490, + 0x00000493, 0x00000492, + 0x00000495, 0x00000494, + 0x00000497, 0x00000496, + 0x00000499, 0x00000498, + 0x0000049b, 0x0000049a, + 0x0000049d, 0x0000049c, + 0x0000049f, 0x0000049e, + 0x000004a1, 0x000004a0, + 0x000004a3, 0x000004a2, + 0x000004a5, 0x000004a4, + 0x000004a7, 0x000004a6, + 0x000004a9, 0x000004a8, + 0x000004ab, 0x000004aa, + 0x000004ad, 0x000004ac, + 0x000004af, 0x000004ae, + 0x000004b1, 0x000004b0, + 0x000004b3, 0x000004b2, + 0x000004b5, 0x000004b4, + 0x000004b7, 0x000004b6, + 0x000004b9, 0x000004b8, + 0x000004bb, 0x000004ba, + 0x000004bd, 0x000004bc, + 0x000004bf, 0x000004be, + 0x000004c2, 0x000004c1, + 0x000004c4, 0x000004c3, + 0x000004c6, 0x000004c5, + 0x000004c8, 0x000004c7, + 0x000004ca, 0x000004c9, + 0x000004cc, 0x000004cb, + 0x000004ce, 0x000004cd, + 0x000004d1, 0x000004d0, + 0x000004d3, 0x000004d2, + 0x000004d5, 0x000004d4, + 0x000004d7, 0x000004d6, + 0x000004d9, 0x000004d8, + 0x000004db, 0x000004da, + 0x000004dd, 0x000004dc, + 0x000004df, 0x000004de, + 0x000004e1, 0x000004e0, + 0x000004e3, 0x000004e2, + 0x000004e5, 0x000004e4, + 0x000004e7, 0x000004e6, + 0x000004e9, 0x000004e8, + 0x000004eb, 0x000004ea, + 0x000004ed, 0x000004ec, + 0x000004ef, 0x000004ee, + 0x000004f1, 0x000004f0, + 0x000004f3, 0x000004f2, + 0x000004f5, 0x000004f4, + 0x000004f9, 0x000004f8, + 0x00000501, 0x00000500, + 0x00000503, 0x00000502, + 0x00000505, 0x00000504, + 0x00000507, 0x00000506, + 0x00000509, 0x00000508, + 0x0000050b, 0x0000050a, + 0x0000050d, 0x0000050c, + 0x0000050f, 0x0000050e, + 0x00000561, 0x00000531, + 0x00000562, 0x00000532, + 0x00000563, 0x00000533, + 0x00000564, 0x00000534, + 0x00000565, 0x00000535, + 0x00000566, 0x00000536, + 0x00000567, 0x00000537, + 0x00000568, 0x00000538, + 0x00000569, 0x00000539, + 0x0000056a, 0x0000053a, + 0x0000056b, 0x0000053b, + 0x0000056c, 0x0000053c, + 0x0000056d, 0x0000053d, + 0x0000056e, 0x0000053e, + 0x0000056f, 0x0000053f, + 0x00000570, 0x00000540, + 0x00000571, 0x00000541, + 0x00000572, 0x00000542, + 0x00000573, 0x00000543, + 0x00000574, 0x00000544, + 0x00000575, 0x00000545, + 0x00000576, 0x00000546, + 0x00000577, 0x00000547, + 0x00000578, 0x00000548, + 0x00000579, 0x00000549, + 0x0000057a, 0x0000054a, + 0x0000057b, 0x0000054b, + 0x0000057c, 0x0000054c, + 0x0000057d, 0x0000054d, + 0x0000057e, 0x0000054e, + 0x0000057f, 0x0000054f, + 0x00000580, 0x00000550, + 0x00000581, 0x00000551, + 0x00000582, 0x00000552, + 0x00000583, 0x00000553, + 0x00000584, 0x00000554, + 0x00000585, 0x00000555, + 0x00000586, 0x00000556, + 0x00001e01, 0x00001e00, + 0x00001e03, 0x00001e02, + 0x00001e05, 0x00001e04, + 0x00001e07, 0x00001e06, + 0x00001e09, 0x00001e08, + 0x00001e0b, 0x00001e0a, + 0x00001e0d, 0x00001e0c, + 0x00001e0f, 0x00001e0e, + 0x00001e11, 0x00001e10, + 0x00001e13, 0x00001e12, + 0x00001e15, 0x00001e14, + 0x00001e17, 0x00001e16, + 0x00001e19, 0x00001e18, + 0x00001e1b, 0x00001e1a, + 0x00001e1d, 0x00001e1c, + 0x00001e1f, 0x00001e1e, + 0x00001e21, 0x00001e20, + 0x00001e23, 0x00001e22, + 0x00001e25, 0x00001e24, + 0x00001e27, 0x00001e26, + 0x00001e29, 0x00001e28, + 0x00001e2b, 0x00001e2a, + 0x00001e2d, 0x00001e2c, + 0x00001e2f, 0x00001e2e, + 0x00001e31, 0x00001e30, + 0x00001e33, 0x00001e32, + 0x00001e35, 0x00001e34, + 0x00001e37, 0x00001e36, + 0x00001e39, 0x00001e38, + 0x00001e3b, 0x00001e3a, + 0x00001e3d, 0x00001e3c, + 0x00001e3f, 0x00001e3e, + 0x00001e41, 0x00001e40, + 0x00001e43, 0x00001e42, + 0x00001e45, 0x00001e44, + 0x00001e47, 0x00001e46, + 0x00001e49, 0x00001e48, + 0x00001e4b, 0x00001e4a, + 0x00001e4d, 0x00001e4c, + 0x00001e4f, 0x00001e4e, + 0x00001e51, 0x00001e50, + 0x00001e53, 0x00001e52, + 0x00001e55, 0x00001e54, + 0x00001e57, 0x00001e56, + 0x00001e59, 0x00001e58, + 0x00001e5b, 0x00001e5a, + 0x00001e5d, 0x00001e5c, + 0x00001e5f, 0x00001e5e, + 0x00001e61, 0x00001e60, + 0x00001e63, 0x00001e62, + 0x00001e65, 0x00001e64, + 0x00001e67, 0x00001e66, + 0x00001e69, 0x00001e68, + 0x00001e6b, 0x00001e6a, + 0x00001e6d, 0x00001e6c, + 0x00001e6f, 0x00001e6e, + 0x00001e71, 0x00001e70, + 0x00001e73, 0x00001e72, + 0x00001e75, 0x00001e74, + 0x00001e77, 0x00001e76, + 0x00001e79, 0x00001e78, + 0x00001e7b, 0x00001e7a, + 0x00001e7d, 0x00001e7c, + 0x00001e7f, 0x00001e7e, + 0x00001e81, 0x00001e80, + 0x00001e83, 0x00001e82, + 0x00001e85, 0x00001e84, + 0x00001e87, 0x00001e86, + 0x00001e89, 0x00001e88, + 0x00001e8b, 0x00001e8a, + 0x00001e8d, 0x00001e8c, + 0x00001e8f, 0x00001e8e, + 0x00001e91, 0x00001e90, + 0x00001e93, 0x00001e92, + 0x00001e95, 0x00001e94, + 0x00001e9b, 0x00001e60, + 0x00001ea1, 0x00001ea0, + 0x00001ea3, 0x00001ea2, + 0x00001ea5, 0x00001ea4, + 0x00001ea7, 0x00001ea6, + 0x00001ea9, 0x00001ea8, + 0x00001eab, 0x00001eaa, + 0x00001ead, 0x00001eac, + 0x00001eaf, 0x00001eae, + 0x00001eb1, 0x00001eb0, + 0x00001eb3, 0x00001eb2, + 0x00001eb5, 0x00001eb4, + 0x00001eb7, 0x00001eb6, + 0x00001eb9, 0x00001eb8, + 0x00001ebb, 0x00001eba, + 0x00001ebd, 0x00001ebc, + 0x00001ebf, 0x00001ebe, + 0x00001ec1, 0x00001ec0, + 0x00001ec3, 0x00001ec2, + 0x00001ec5, 0x00001ec4, + 0x00001ec7, 0x00001ec6, + 0x00001ec9, 0x00001ec8, + 0x00001ecb, 0x00001eca, + 0x00001ecd, 0x00001ecc, + 0x00001ecf, 0x00001ece, + 0x00001ed1, 0x00001ed0, + 0x00001ed3, 0x00001ed2, + 0x00001ed5, 0x00001ed4, + 0x00001ed7, 0x00001ed6, + 0x00001ed9, 0x00001ed8, + 0x00001edb, 0x00001eda, + 0x00001edd, 0x00001edc, + 0x00001edf, 0x00001ede, + 0x00001ee1, 0x00001ee0, + 0x00001ee3, 0x00001ee2, + 0x00001ee5, 0x00001ee4, + 0x00001ee7, 0x00001ee6, + 0x00001ee9, 0x00001ee8, + 0x00001eeb, 0x00001eea, + 0x00001eed, 0x00001eec, + 0x00001eef, 0x00001eee, + 0x00001ef1, 0x00001ef0, + 0x00001ef3, 0x00001ef2, + 0x00001ef5, 0x00001ef4, + 0x00001ef7, 0x00001ef6, + 0x00001ef9, 0x00001ef8, + 0x00001f00, 0x00001f08, + 0x00001f01, 0x00001f09, + 0x00001f02, 0x00001f0a, + 0x00001f03, 0x00001f0b, + 0x00001f04, 0x00001f0c, + 0x00001f05, 0x00001f0d, + 0x00001f06, 0x00001f0e, + 0x00001f07, 0x00001f0f, + 0x00001f10, 0x00001f18, + 0x00001f11, 0x00001f19, + 0x00001f12, 0x00001f1a, + 0x00001f13, 0x00001f1b, + 0x00001f14, 0x00001f1c, + 0x00001f15, 0x00001f1d, + 0x00001f20, 0x00001f28, + 0x00001f21, 0x00001f29, + 0x00001f22, 0x00001f2a, + 0x00001f23, 0x00001f2b, + 0x00001f24, 0x00001f2c, + 0x00001f25, 0x00001f2d, + 0x00001f26, 0x00001f2e, + 0x00001f27, 0x00001f2f, + 0x00001f30, 0x00001f38, + 0x00001f31, 0x00001f39, + 0x00001f32, 0x00001f3a, + 0x00001f33, 0x00001f3b, + 0x00001f34, 0x00001f3c, + 0x00001f35, 0x00001f3d, + 0x00001f36, 0x00001f3e, + 0x00001f37, 0x00001f3f, + 0x00001f40, 0x00001f48, + 0x00001f41, 0x00001f49, + 0x00001f42, 0x00001f4a, + 0x00001f43, 0x00001f4b, + 0x00001f44, 0x00001f4c, + 0x00001f45, 0x00001f4d, + 0x00001f51, 0x00001f59, + 0x00001f53, 0x00001f5b, + 0x00001f55, 0x00001f5d, + 0x00001f57, 0x00001f5f, + 0x00001f60, 0x00001f68, + 0x00001f61, 0x00001f69, + 0x00001f62, 0x00001f6a, + 0x00001f63, 0x00001f6b, + 0x00001f64, 0x00001f6c, + 0x00001f65, 0x00001f6d, + 0x00001f66, 0x00001f6e, + 0x00001f67, 0x00001f6f, + 0x00001f70, 0x00001fba, + 0x00001f71, 0x00001fbb, + 0x00001f72, 0x00001fc8, + 0x00001f73, 0x00001fc9, + 0x00001f74, 0x00001fca, + 0x00001f75, 0x00001fcb, + 0x00001f76, 0x00001fda, + 0x00001f77, 0x00001fdb, + 0x00001f78, 0x00001ff8, + 0x00001f79, 0x00001ff9, + 0x00001f7a, 0x00001fea, + 0x00001f7b, 0x00001feb, + 0x00001f7c, 0x00001ffa, + 0x00001f7d, 0x00001ffb, + 0x00001f80, 0x00001f88, + 0x00001f81, 0x00001f89, + 0x00001f82, 0x00001f8a, + 0x00001f83, 0x00001f8b, + 0x00001f84, 0x00001f8c, + 0x00001f85, 0x00001f8d, + 0x00001f86, 0x00001f8e, + 0x00001f87, 0x00001f8f, + 0x00001f90, 0x00001f98, + 0x00001f91, 0x00001f99, + 0x00001f92, 0x00001f9a, + 0x00001f93, 0x00001f9b, + 0x00001f94, 0x00001f9c, + 0x00001f95, 0x00001f9d, + 0x00001f96, 0x00001f9e, + 0x00001f97, 0x00001f9f, + 0x00001fa0, 0x00001fa8, + 0x00001fa1, 0x00001fa9, + 0x00001fa2, 0x00001faa, + 0x00001fa3, 0x00001fab, + 0x00001fa4, 0x00001fac, + 0x00001fa5, 0x00001fad, + 0x00001fa6, 0x00001fae, + 0x00001fa7, 0x00001faf, + 0x00001fb0, 0x00001fb8, + 0x00001fb1, 0x00001fb9, + 0x00001fb3, 0x00001fbc, + 0x00001fbe, 0x00000399, + 0x00001fc3, 0x00001fcc, + 0x00001fd0, 0x00001fd8, + 0x00001fd1, 0x00001fd9, + 0x00001fe0, 0x00001fe8, + 0x00001fe1, 0x00001fe9, + 0x00001fe5, 0x00001fec, + 0x00001ff3, 0x00001ffc, + 0x00002170, 0x00002160, + 0x00002171, 0x00002161, + 0x00002172, 0x00002162, + 0x00002173, 0x00002163, + 0x00002174, 0x00002164, + 0x00002175, 0x00002165, + 0x00002176, 0x00002166, + 0x00002177, 0x00002167, + 0x00002178, 0x00002168, + 0x00002179, 0x00002169, + 0x0000217a, 0x0000216a, + 0x0000217b, 0x0000216b, + 0x0000217c, 0x0000216c, + 0x0000217d, 0x0000216d, + 0x0000217e, 0x0000216e, + 0x0000217f, 0x0000216f, + 0x000024d0, 0x000024b6, + 0x000024d1, 0x000024b7, + 0x000024d2, 0x000024b8, + 0x000024d3, 0x000024b9, + 0x000024d4, 0x000024ba, + 0x000024d5, 0x000024bb, + 0x000024d6, 0x000024bc, + 0x000024d7, 0x000024bd, + 0x000024d8, 0x000024be, + 0x000024d9, 0x000024bf, + 0x000024da, 0x000024c0, + 0x000024db, 0x000024c1, + 0x000024dc, 0x000024c2, + 0x000024dd, 0x000024c3, + 0x000024de, 0x000024c4, + 0x000024df, 0x000024c5, + 0x000024e0, 0x000024c6, + 0x000024e1, 0x000024c7, + 0x000024e2, 0x000024c8, + 0x000024e3, 0x000024c9, + 0x000024e4, 0x000024ca, + 0x000024e5, 0x000024cb, + 0x000024e6, 0x000024cc, + 0x000024e7, 0x000024cd, + 0x000024e8, 0x000024ce, + 0x000024e9, 0x000024cf, + 0x0000ff41, 0x0000ff21, + 0x0000ff42, 0x0000ff22, + 0x0000ff43, 0x0000ff23, + 0x0000ff44, 0x0000ff24, + 0x0000ff45, 0x0000ff25, + 0x0000ff46, 0x0000ff26, + 0x0000ff47, 0x0000ff27, + 0x0000ff48, 0x0000ff28, + 0x0000ff49, 0x0000ff29, + 0x0000ff4a, 0x0000ff2a, + 0x0000ff4b, 0x0000ff2b, + 0x0000ff4c, 0x0000ff2c, + 0x0000ff4d, 0x0000ff2d, + 0x0000ff4e, 0x0000ff2e, + 0x0000ff4f, 0x0000ff2f, + 0x0000ff50, 0x0000ff30, + 0x0000ff51, 0x0000ff31, + 0x0000ff52, 0x0000ff32, + 0x0000ff53, 0x0000ff33, + 0x0000ff54, 0x0000ff34, + 0x0000ff55, 0x0000ff35, + 0x0000ff56, 0x0000ff36, + 0x0000ff57, 0x0000ff37, + 0x0000ff58, 0x0000ff38, + 0x0000ff59, 0x0000ff39, + 0x0000ff5a, 0x0000ff3a, + 0x00010428, 0x00010400, + 0x00010429, 0x00010401, + 0x0001042a, 0x00010402, + 0x0001042b, 0x00010403, + 0x0001042c, 0x00010404, + 0x0001042d, 0x00010405, + 0x0001042e, 0x00010406, + 0x0001042f, 0x00010407, + 0x00010430, 0x00010408, + 0x00010431, 0x00010409, + 0x00010432, 0x0001040a, + 0x00010433, 0x0001040b, + 0x00010434, 0x0001040c, + 0x00010435, 0x0001040d, + 0x00010436, 0x0001040e, + 0x00010437, 0x0001040f, + 0x00010438, 0x00010410, + 0x00010439, 0x00010411, + 0x0001043a, 0x00010412, + 0x0001043b, 0x00010413, + 0x0001043c, 0x00010414, + 0x0001043d, 0x00010415, + 0x0001043e, 0x00010416, + 0x0001043f, 0x00010417, + 0x00010440, 0x00010418, + 0x00010441, 0x00010419, + 0x00010442, 0x0001041a, + 0x00010443, 0x0001041b, + 0x00010444, 0x0001041c, + 0x00010445, 0x0001041d, + 0x00010446, 0x0001041e, + 0x00010447, 0x0001041f, + 0x00010448, 0x00010420, + 0x00010449, 0x00010421, + 0x0001044a, 0x00010422, + 0x0001044b, 0x00010423, + 0x0001044c, 0x00010424, + 0x0001044d, 0x00010425, +}; + +#endif /*UTF8TABLES_H_*/ diff --git a/doc/modsecurity2-apache-reference.xml b/doc/modsecurity2-apache-reference.xml index af71d599..f202f01d 100644 --- a/doc/modsecurity2-apache-reference.xml +++ b/doc/modsecurity2-apache-reference.xml @@ -3,7 +3,7 @@ ModSecurity Reference Manual - Version 2.2.0-dev1 / (May 11, 2007) + Version 2.5.0-dev2 / (June 21, 2007) 2004-2007 @@ -1087,7 +1087,7 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtect (Experimental) + <literal>SecPdfProtect</literal> (Experimental) Description: Enables the PDF XSS protection functionality. Once enabled access to PDF files is tracked. @@ -1101,7 +1101,25 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtectSecret (Experimental) + <literal>SecPdfProtectMethod</literal> (Experimental) + + Description: Configure desired + protection method to be used when requests for PDF files are detected. + Possible values are TokenRedirection and + ForcedDownload. The token redirection approach will + attempt to redirect with tokens where possible. This allows PDF files to + continue to be opened inline but only works for GET requests. Forced + download always causes PDF files to be delivered as opaque binaries and + attachments. The latter will always be used for non-GET requests. Forced + download is considered to be more secure but may cause usability + problems for users ("This PDF won't open anymore!"). + + Default: + TokenRedirection +
+ +
+ <literal>SecPdfProtectSecret</literal> (Experimental) Description: Defines the secret that will be used to construct one-time tokens. You should use a @@ -1113,7 +1131,7 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtectTimeout (Experimental) + <literal>SecPdfProtectTimeout</literal> (Experimental) Description: Defines the token timeout. After token expires it can no longer be used to allow access to @@ -1125,7 +1143,7 @@ SecAuditLogStorageDir logs/audit
- SecPdfProtectTokenName (Experimental) + <literal>SecPdfProtectTokenName</literal> (Experimental) Description: Defines the name of the token. The only reason you would want to change the name of the @@ -2422,21 +2440,23 @@ SecRule XML "@validateDTD /opt/apache-frontend/conf/xml.dtd"
- <literal>RESPONSE_CONTENT_LENGTH</literal> (Experimental) + <literal>RESPONSE_CONTENT_LENGTH</literal> Response body length in bytes. Can be available starting with phase 3 but it does not have to be (as the length of response body is - not always known in advance.) Does not exist of the content length is - not know. (We will consider making it empty if the size is not - known.) + 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. + is modified. For example, in embedded mode + mod_deflate can compress the response body between + phases 4 and 5.
- <literal>RESPONSE_CONTENT_TYPE</literal> (Experimental) + <literal>RESPONSE_CONTENT_TYPE</literal> Response content type. Only available starting with phase 3. @@ -2814,6 +2834,72 @@ SecRule REQBODY_PROCESSOR "!^XML$" skip:2 SecRule XML:/employees/employee/name/text() Fred SecRule XML:/xq:employees/employee/name/text() Fred \ xmlns:xq=http://www.example.com/employees + + The first XPath expression does not use namespaces. It would match + 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. + + To learn more about XPath we suggest the following + resources: + + + + XPath + Standard + + + + XPath + Tutorial + +
@@ -3035,8 +3121,10 @@ SecRule XML:/xq:employees/employee/name/text() In addition to decoding %xx like urlDecode, urlDecodeUni also decodes %uXXXX encoding (only the - lower byte will be used, the higher byte will be discarded). + moreinfo="none"> %uXXXX encoding. If the + code is in the range of FF01-FF5E (the full width ASCII codes), then the + higher byte is used to detect and adjust the lower byte. Otherwise, only + the lower byte will be used and the higher byte zeroed.
@@ -3544,6 +3632,17 @@ SecRule REQUEST_URI "^/cgi-bin/script\.pl" \ moreinfo="none"> SESSION, and USER. + + + Please note that ModSecurity does not implement atomic updates + of persistent variables at this time. Variables are read from storage + whenever initcol is encountered in the rules and + persisted at the end of request processing. On busy servers requests + often run in parallel, leading to situations where one request + overwrites the changes made by another request. We anticipate + implementing atomic updates of counter values in a future + version. +
@@ -4285,6 +4384,62 @@ SecRule ARGS:route "!@endsWith %{REQUEST_ADDR}" role="bold">@lt 15"
+
+ <literal>pm</literal> + + Description: Phrase Match + operator. This operator uses a set based matching engine (Aho-Corasick) + for faster matches of keyword lists. It will match any one of its + arguments anywhere in the target value. + + Example: + + SecRule REQUEST_HEADERS:User-Agent "@pm WebZIP WebCopier Webster WebStripper SiteSnagger ProWebWalker CheeseBot" "deny,status:403 + + The above would deny access with 403 if any of the words matched + within the User-Agent HTTP header value. +
+ +
+ <literal>pmFromFile</literal> + + Description: Phrase Match + operator. This operator uses a set based matching engine (Aho-Corasick) + for faster matches of keyword lists. This operator is the same as + @pm except that it takes a list of files as + arguments. It will match any one of the phrases listed in the file(s) + anywhere in the target value. + + Notes: + + + + The contents of the files should be one phrase per line. End + of line markers will be stripped from the phrases, however, + whitespace will not be trimmed from phrases in the file. Empty lines + and comment lines (beginning with a '#') are ignored. + + + + To allow easier inclusion of phrase files with rulesets, + relative paths may be used to the phrase files. In this case, the + path of the file containing the rule is prepended to the phrase file + path. + + + + Example: + + SecRule REQUEST_HEADERS:User-Agent "@pm /path/to/blacklist1 blacklist2" "deny,status:403 + + The above would deny access with 403 if any of the patterns in the + two files matched within the User-Agent HTTP header value. The + blacklist2 file would need to be placed in the same + path as the file containing the rule. +
+
<literal>rbl</literal> @@ -4494,5 +4649,24 @@ SecRule XML "@validateSchema /path/to/apache2/conf/xml.xsd
+ +
+ <literal>within</literal> + + Description: This operator is a + string comparison and returns true if the input value is found anywhere + within the parameter value. Note that this is similar to + @contains, except that the target and match values + are reversed. Macro expansion is performed so you may use variable names + such as %{TX.1}, etc. + + Example: + + SecRule REQUEST_METHOD "!@within get,post,head" t:lowercase,deny,status:403 + +SecAction "pass,setvar:'tx.allowed_methods=get,post,head'" +SecRule REQUEST_METHOD "!@within %{tx.allowed_methods}" t:lowercase,deny,status:403 +
diff --git a/rules/CHANGELOG b/rules/CHANGELOG index 1ab249f7..e78a4fa9 100644 --- a/rules/CHANGELOG +++ b/rules/CHANGELOG @@ -1,66 +1,122 @@ - - -version 1.3.2 build 4 2007/01/17 - -Fixed apache 2.4 dummy requests exclusion -Added persistent PDF UXSS detection rule - - -Vervion 1.3.2 build 3 2007/01/10 - -Fixed regular expresion in rule 960010 (file #30) to allow mulipart-data content - - -Version 1.3.2 - 2006/12/27 - -New events: -- 960037 Directory is restricted by policy -- 960038 HTTP header is restricted by policy - -Regular expressions fixes: -- Regular expressions with @ at end of beginning (for example "@import) -- Regular expressions with un-escaped "." -- Command Injections now always require certain characters both before and after the command. Important since many are common English words (finger, mail) -- The command injection wget is not searched in the UA header as it has different meaning there. -- LDAP Fixed to reduce FPs: - + More accurate regular expressions - + high bit characters not accpeted between signature tokens. -- Do not detect JSP compile error" in the response body, will trigger this rule, with severity 4 (Warning) +- 950015,950910,950911 - HTTP Response Splitting + Looking for HTTP Response Splitting patterns as described in Amit Klein's excellent article: + http://www.packetstormsecurity.org/papers/general/whitepaper_httpresponse.pdf +ModSecurity does not support compressed content at the moment. Thus, the following rules have been added: +- 960902 - Content-Encoding in request not supported + Any incoming compressed request will be denied +- 960903 - Content-Encoding in response not suppoted + An outgoing compressed response will be logged to alert, but ONLY ONCE. + +False Positives Fixes: +- Removed <.exe>,<.shtml> from restricted extensions +- Will not be looking for SQL Injection signatures , in the Via request header +- Excluded Referer header from SQL injection, XSS and command injection rules +- Excluded X-OS-Prefs header from command injection rule +- Will be looking for command injection signatures in + REQUEST_COOKIES|REQUEST_COOKIES_NAMES instead of REQUEST_HEADERS:Cookie. +- Allowing charset specification in the Content-Type + +Additional rules logic: +- Corrected match of OPTIONS method in event 960015 +- Changed location for event 960014 (proxy access) to REQUEST_URI_RAW +- Moved all rules apart from method inspection from phase 1 to phase 2 - + This will enable viewing content if such a rule triggers as well as setting + exceptions using Apache scope tags. +- Added match for double quote in addition to single quote for signature (SQL Injection) +- Added 1=1 signature (SQL Injection) + +-------------------------------- +version 1.3.2 build 4 2007/01/17 +-------------------------------- + +Fixed apache 2.4 dummy requests exclusion +Added persistent PDF UXSS detection rule + +-------------------------------- +Version 1.3.2 build 3 2007/01/10 +-------------------------------- + +Fixed regular expression in rule 960010 (file #30) to allow multipart form data +content + +-------------------------- +Version 1.3.2 - 2006/12/27 +-------------------------- + +New events: +- 960037 Directory is restricted by policy +- 960038 HTTP header is restricted by policy + +Regular expressions fixes: +- Regular expressions with @ at end of beginning (for example "@import) +- Regular expressions with un-escaped "." +- Command Injections now always require certain characters both before and after the command. Important since many are common English words (finger, mail) +- The command injection wget is not searched in the UA header as it has different meaning there. +- LDAP Fixed to reduce FPs: + + More accurate regular expressions + + high bit characters not accpeted between signature tokens. +- Do not detect - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/rules/README b/rules/README index fb6d6597..57d6bc59 100644 --- a/rules/README +++ b/rules/README @@ -1,179 +1,179 @@ - - -ModSecurity Core Rule Set -============================== - -(c) 2006 Breach Secuiry Inc. - -The ModSecurity Core Rule Set is provided to you under the terms and -conditions of GPL version 2 - -This directory contains the files for Core ModSecurity Rule Set -The rules are compatible with ModSecurity 2.1 (as of version 1.3.2) - - -Overview --------- - -Using ModSecurity requires rules. In order to enable users to take full -advantage of ModSecurity immediately, Breach Security Inc. is providing a free -Core rule set. Unlike intrusion detection and prevention systems which -rely on signature specific to known vulnerabilities, the Core Rule Set -provides generic protection from unknown vulnerabilities often found in web -application that are in most cases custom coded. - -Keep in mind that a predefined rule set is only part of the work required to -protect your web site. We strongly urge you to consult Ivan Ristic's book, -"Apache Security" in order to harden your Apache web server. You may also -consider writing custom rules for providing a positive security envelope to -your application or critical parts of it. Breach Security can provide you with -training and professional services to assist you in doing that. The Core -Rule Set is heavily commented to allow it to be used as a step-by-step -deployment guide for ModSecurity. - -For more information refer to the Core Rule Set page at -http://www.modsecurity.org/ - - -Core Rule Set Structure & Usage ------------------------------------- - -To activate the rules for your web server installation: - - 1) You may want to edit and customize modsecurity_crs_10_config.conf. - Additionally you may want to edit modsecurity_crs_30_http_policy.conf - which enforces an application specific HTTP protocol usage. - - 2) Add the following line to your httpd.conf (assuming - you've placed the rule files into conf/modsecurity/): - - Include conf/modsecurity/*.conf - - 3) Restart web server. - - 4) Make sure your web sites are still running fine. - - 5) Simulate an attack against the web server. Then check - the attack was correctly logged in the Apache error log, - ModSecurity debug log (if you enabled it) and ModSecurity - audit log (if you enabled it). - - 6) If you configured your audit log entries to be transported - to ModSecurity Console in real time, check the alert was - correctly recorded there too. - -About Regular Expressions -------------------------- - -One of the advantages of the Core Rule Set, being a set of text files is your -ability to modify it. However you will find that the regular expressions used -are very complex. - -Since regular expressions are much more efficient if assembled into a single -expression and optimized, a generation script takes a list of patterns that -are required for a rule and optimize them into a most efficient regular -expression. - -We plan to release the optimization script shortly to allow much easier editing -of regular expressions. - - -Core Rule Set Content --------------------------- - -In order to provide generic web applications protection, the Core Rule Set -uses the following techniques: - -1. HTTP protection - detecting violations of the HTTP protocol and a locally -defined usage policy. - -2. Common Web Attacks Protection - detecting common web application security -attack. - -3. Automation detection - Detecting bots, crawlers, scanners and other surface -malicious activity. - -4. Trojan Protection - Detecting access to Trojans horses. - -5. Errors Hiding – Disguising error messages sent by the server - -In addition the rule set also hints at the power of ModSecurity beyond -providing security by reporting access from the major search engines to your -site. - - -HTTP Protection - This first line of protection ensures that all abnormal HTTP -requests are detected. This line of defense eliminates a large number of -automated and non targeted attacks as well as protects the web server itself. -Common Web Attacks Protection Rules on the second level address the common web -application security attack methods. These are the issues that can appear in -any web application. Some of the issues addressed are: - -- SQL Injection -- Cross-Site Scripting (XSS) -- OS Command execution -- Remote code inclusion -- LDAP Injection -- SSI Injection -- Information leak -- Buffer overflows -- File disclosure - -Automation Detection - Automated clients are both a security risk and a -commercial risk. Automated crawlers collect information from your site, consume -bandwidth and might also search for vulnerabilities on the web site. Automation -detection is especially useful for generic detection of comments spam. - - -Trojan Protection - ModSecurity Core Rule Set detects access to back doors -installed on a web server. This feature is very important in a hosting -environment when some of this backdoors may be uploaded in a legitimate way and -used maliciously. In addition the Core Rule Set includes a hook for adding -an Anti-Virus program such as ClamAV for checking file uploads. - -Errors Hiding - If all fails, the Core Rule Set will detect errors sent by -the web server. Detecting and blocking errors prevents attackers from -collecting reconnaissance information about the web application and also server -as a last line of defense in case an attack was not detected eariler. - - -Few Word of Caution -------------------- - -As with every new technology, using the ModSecurity Core Rule Set requires some caution: - -- Every Rule Set can have false positive in new environments and any new -installation should initially use the log only Rule Set version or if no such -version is available, set ModSecurity to Detection only using the SecRuleEngine -DetectionOnly command. - -After running ModSecurity in a detection only mode for a while review the evens -generated and decide if any modification to the rule set should be made before -moving to protection mode. - -- Freely available wide spread signatures have their down side as attackers may -examine them and find ways to bypass them. Especially note that the automation -detection signatures are relatively easy to evade and should not be viewed as a -security mechanism but only as a "nuisance reduction" mechanism. - - -Road Map --------- - -This rule set is both young and old. Breach Security has a long experience with -rules and signatures for application security protection and the Core Rule -Set is based on this experience. On the other hand, this is a first cut of a -ModSecurity rule set so your feedback and remarks, either directly or through -the ModSecurity mailing list would be greatly appreciated. - -Going forward we plan to: - -- Utilize ModSecurity 2.0 support for events correlation to detect denial of -service attacks, brute force attacks and attack reconnaissance - -- Add a framework for validating SOAP requests. - -- Add signatures for key known vulnerabilities. - -Anything else you would want? - + + +ModSecurity Core Rule Set +============================== + +(c) 2006 Breach Secuiry Inc. + +The ModSecurity Core Rule Set is provided to you under the terms and +conditions of GPL version 2 + +This directory contains the files for Core ModSecurity Rule Set +The rules are compatible with ModSecurity 2.1 (as of version 1.3.2) + + +Overview +-------- + +Using ModSecurity requires rules. In order to enable users to take full +advantage of ModSecurity immediately, Breach Security Inc. is providing a free +Core rule set. Unlike intrusion detection and prevention systems which +rely on signature specific to known vulnerabilities, the Core Rule Set +provides generic protection from unknown vulnerabilities often found in web +application that are in most cases custom coded. + +Keep in mind that a predefined rule set is only part of the work required to +protect your web site. We strongly urge you to consult Ivan Ristic's book, +"Apache Security" in order to harden your Apache web server. You may also +consider writing custom rules for providing a positive security envelope to +your application or critical parts of it. Breach Security can provide you with +training and professional services to assist you in doing that. The Core +Rule Set is heavily commented to allow it to be used as a step-by-step +deployment guide for ModSecurity. + +For more information refer to the Core Rule Set page at +http://www.modsecurity.org/ + + +Core Rule Set Structure & Usage +------------------------------------ + +To activate the rules for your web server installation: + + 1) You may want to edit and customize modsecurity_crs_10_config.conf. + Additionally you may want to edit modsecurity_crs_30_http_policy.conf + which enforces an application specific HTTP protocol usage. + + 2) Add the following line to your httpd.conf (assuming + you've placed the rule files into conf/modsecurity/): + + Include conf/modsecurity/*.conf + + 3) Restart web server. + + 4) Make sure your web sites are still running fine. + + 5) Simulate an attack against the web server. Then check + the attack was correctly logged in the Apache error log, + ModSecurity debug log (if you enabled it) and ModSecurity + audit log (if you enabled it). + + 6) If you configured your audit log entries to be transported + to ModSecurity Console in real time, check the alert was + correctly recorded there too. + +About Regular Expressions +------------------------- + +One of the advantages of the Core Rule Set, being a set of text files is your +ability to modify it. However you will find that the regular expressions used +are very complex. + +Since regular expressions are much more efficient if assembled into a single +expression and optimized, a generation script takes a list of patterns that +are required for a rule and optimize them into a most efficient regular +expression. + +We plan to release the optimization script shortly to allow much easier editing +of regular expressions. + + +Core Rule Set Content +-------------------------- + +In order to provide generic web applications protection, the Core Rule Set +uses the following techniques: + +1. HTTP protection - detecting violations of the HTTP protocol and a locally +defined usage policy. + +2. Common Web Attacks Protection - detecting common web application security +attack. + +3. Automation detection - Detecting bots, crawlers, scanners and other surface +malicious activity. + +4. Trojan Protection - Detecting access to Trojans horses. + +5. Errors Hiding - Disguising error messages sent by the server + +In addition the rule set also hints at the power of ModSecurity beyond +providing security by reporting access from the major search engines to your +site. + + +HTTP Protection - This first line of protection ensures that all abnormal HTTP +requests are detected. This line of defense eliminates a large number of +automated and non targeted attacks as well as protects the web server itself. +Common Web Attacks Protection Rules on the second level address the common web +application security attack methods. These are the issues that can appear in +any web application. Some of the issues addressed are: + +- SQL Injection +- Cross-Site Scripting (XSS) +- OS Command execution +- Remote code inclusion +- LDAP Injection +- SSI Injection +- Information leak +- Buffer overflows +- File disclosure + +Automation Detection - Automated clients are both a security risk and a +commercial risk. Automated crawlers collect information from your site, consume +bandwidth and might also search for vulnerabilities on the web site. Automation +detection is especially useful for generic detection of comments spam. + + +Trojan Protection - ModSecurity Core Rule Set detects access to back doors +installed on a web server. This feature is very important in a hosting +environment when some of this backdoors may be uploaded in a legitimate way and +used maliciously. In addition the Core Rule Set includes a hook for adding +an Anti-Virus program such as ClamAV for checking file uploads. + +Errors Hiding - If all fails, the Core Rule Set will detect errors sent by +the web server. Detecting and blocking errors prevents attackers from +collecting reconnaissance information about the web application and also server +as a last line of defense in case an attack was not detected eariler. + + +Few Word of Caution +------------------- + +As with every new technology, using the ModSecurity Core Rule Set requires some caution: + +- Every Rule Set can have false positive in new environments and any new +installation should initially use the log only Rule Set version or if no such +version is available, set ModSecurity to Detection only using the SecRuleEngine +DetectionOnly command. + +After running ModSecurity in a detection only mode for a while review the evens +generated and decide if any modification to the rule set should be made before +moving to protection mode. + +- Freely available wide spread signatures have their down side as attackers may +examine them and find ways to bypass them. Especially note that the automation +detection signatures are relatively easy to evade and should not be viewed as a +security mechanism but only as a "nuisance reduction" mechanism. + + +Road Map +-------- + +This rule set is both young and old. Breach Security has a long experience with +rules and signatures for application security protection and the Core Rule +Set is based on this experience. On the other hand, this is a first cut of a +ModSecurity rule set so your feedback and remarks, either directly or through +the ModSecurity mailing list would be greatly appreciated. + +Going forward we plan to: + +- Utilize ModSecurity 2.0 support for events correlation to detect denial of +service attacks, brute force attacks and attack reconnaissance + +- Add a framework for validating SOAP requests. + +- Add signatures for key known vulnerabilities. + +Anything else you would want? + diff --git a/rules/blocking/modsecurity_crs_20_protocol_violations.conf b/rules/blocking/modsecurity_crs_20_protocol_violations.conf index 484085af..e2c03e76 100644 --- a/rules/blocking/modsecurity_crs_20_protocol_violations.conf +++ b/rules/blocking/modsecurity_crs_20_protocol_violations.conf @@ -1,74 +1,84 @@ -# --------------------------------------------------------------- -# Core ModSecurity Rule Set -# Copyright (C) 2006 Breach Security Inc. All rights reserved. -# -# The ModSecuirty Core Rule Set is distributed under GPL version 2 -# Please see the enclosed LICENCE file for full details. -# --------------------------------------------------------------- - - -# -# TODO in some cases a valid client (usually automated) generates requests that -# violates the HTTP protocol. Create exceptions for those clients, but try -# to limit the exception to a source IP or other additional properties of -# the request such as URL and not allow the violation generally. -# -# - -# Use status code 400 response status code by default as protocol violations -# are in essence bad requests. -SecDefaultAction "log,pass,phase:1,status:400" - -# Accept only digits in content length -# -SecRule REQUEST_HEADERS:Content-Length "!^\d+$" "deny,log,auditlog,status:400,msg:'Content-Length HTTP header is not numeric', severity:'2',id:'960016'" - -# Do not accept GET or HEAD requests with bodies -# HTTP standard allows GET requests to have a body but this -# feature is not used in real life. Attackers could try to force -# a request body on an unsuspecting web applications. -# -SecRule REQUEST_METHOD "^(GET|HEAD)$" "chain,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies', severity:'2',id:'960011'" -SecRule REQUEST_HEADERS:Content-Length "!^0?$" - -# Require Content-Length to be provided with every POST request. -# -SecRule REQUEST_METHOD "^POST$" "chain,deny,log,auditlog,status:400,msg:'POST request must have a Content-Length header',id:'960012',severity:'4'" -SecRule &REQUEST_HEADERS:Content-Length "@eq 0" - -# Don't accept transfer encodings we know we don't know how to handle -# -# NOTE ModSecurity does not support chunked transfer encodings at -# this time. You MUST reject all such requests. -# -SecRule HTTP_Transfer-Encoding "!^$" "deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',id:'960013',severity:'5'" - -# Check decodings -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "@validateUrlEncoding" \ - "chain, deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',id:'950107',severity:'4'" -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\%(?![0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" - -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',id:'950801',severity:'4'" - -# Proxy access attempt -# NOTE Apache blocks such access by default if not set as a proxy. The rule is -# included in case Apache proxy is misconfigured. -SecRule REQUEST_URI ^http:/ "deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',id:'960014'" - -# -# Restrict type of characters sent -# -# NOTE In order to be broad and support localized applications this rule -# only validates that NULL Is not used. -# -# The strict policy version also validates that protocol and application -# generated fields are limited to printable ASCII. -# -# TODO If your application use the range 32-126 for parameters. -# -SecRule REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer \ - "@validateByteRange 32-126" \ - "deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015',t:urlDecodeUni,phase:1" - -SecRule ARGS|ARGS_NAMES "@validateByteRange 1-255" \ - "deny,log,auditlog,status:400,msg:'Invalid character in request',id:'960901',severity:'4',t:urlDecodeUni,phase:2" +# --------------------------------------------------------------- +# Core ModSecurity Rule Set +# Copyright (C) 2006 Breach Security Inc. All rights reserved. +# +# The ModSecuirty Core Rule Set is distributed under GPL version 2 +# Please see the enclosed LICENCE file for full details. +# --------------------------------------------------------------- + + +# +# TODO in some cases a valid client (usually automated) generates requests that +# violates the HTTP protocol. Create exceptions for those clients, but try +# to limit the exception to a source IP or other additional properties of +# the request such as URL and not allow the violation generally. +# +# + +# Use status code 400 response status code by default as protocol violations +# are in essence bad requests. +SecDefaultAction "log,pass,phase:2,status:400" + + +# Validate request line +SecRule REQUEST_LINE "!^[a-z]{3,10}\s*(?:http\:\/\/[\w\-\.\/]*)??\/[\w\-\.\/]*(?:\?[\S]*)??\s*http\/[01]\.[901]$" \ + "t:none,t:lowercase,deny,log,auditlog,status:400,msg:'Invalid HTTP Request Line',,id:'960911',severity:'2'" + + +# Accept only digits in content length +# +SecRule REQUEST_HEADERS:Content-Length "!^\d+$" "deny,log,auditlog,status:400,msg:'Content-Length HTTP header is not numeric', severity:'2',,id:'960016'," + +# Do not accept GET or HEAD requests with bodies +# HTTP standard allows GET requests to have a body but this +# feature is not used in real life. Attackers could try to force +# a request body on an unsuspecting web applications. +# +SecRule REQUEST_METHOD "^(GET|HEAD)$" "chain,deny,log,auditlog,status:400,msg:'GET or HEAD requests with bodies', severity:'2',,id:'960011'," +SecRule REQUEST_HEADERS:Content-Length "!^0?$" + +# Require Content-Length to be provided with every POST request. +# +SecRule REQUEST_METHOD "^POST$" "chain,deny,log,auditlog,status:400,msg:'POST request must have a Content-Length header',,id:'960012',severity:'4'" +SecRule &REQUEST_HEADERS:Content-Length "@eq 0" + +# Don't accept transfer encodings we know we don't know how to handle +# +# NOTE ModSecurity does not support chunked transfer encodings at +# this time. You MUST reject all such requests. +# +SecRule REQUEST_HEADERS:Transfer-Encoding "!^$" "deny,log,auditlog,status:501,msg:'ModSecurity does not support transfer encodings',,id:'960013',severity:'3'" + +# Check decodings +SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUrlEncoding" \ + "chain, deny,log,auditlog,status:400,msg:'URL Encoding Abuse Attack Attempt',,id:'950107',severity:'4'" +SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\%(?!$|[0-9a-fA-F]{2}|u[0-9a-fA-F]{4})" + +SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "@validateUtf8Encoding" "deny,log,auditlog,status:400,msg:'UTF8 Encoding Abuse Attack Attempt',,id:'950801',severity:'4'" + +# Disallow use of full-width unicode +SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|XML:/*|!REQUEST_HEADERS:Referer "\%u[fF]{2}[0-9a-fA-F]{2}" \ + "t:none,deny,log,auditlog,status:400,msg:'Unicode Full/Half Width Abuse Attack Attempt',,id:'950116',severity:'4'" + +# Proxy access attempt +# NOTE Apache blocks such access by default if not set as a proxy. The rule is +# included in case Apache proxy is misconfigured. +SecRule REQUEST_URI_RAW ^http:/ "deny,log,auditlog,status:400,msg:'Proxy access attempt', severity:'2',,id:'960014'," + +# +# Restrict type of characters sent +# +# NOTE In order to be broad and support localized applications this rule +# only validates that NULL Is not used. +# +# The strict policy version also validates that protocol and application +# generated fields are limited to printable ASCII. +# +# TODO If your application use the range 32-126 for parameters. +# +SecRule REQUEST_FILENAME|REQUEST_HEADERS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer \ + "@validateByteRange 32-126" \ + "deny,log,auditlog,status:400,msg:'Invalid character in request',,id:'960018',severity:'4',t:urlDecodeUni,phase:1" + +SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS:Referer "@validateByteRange 1-255" \ + "deny,log,auditlog,status:400,msg:'Invalid character in request',,id:'960901',severity:'4',t:urlDecodeUni,phase:2" diff --git a/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf b/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf index 03084e36..448b9a8b 100644 --- a/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf +++ b/rules/blocking/modsecurity_crs_21_protocol_anomalies.conf @@ -1,50 +1,55 @@ -# --------------------------------------------------------------- -# Core ModSecurity Rule Set -# Copyright (C) 2006 Breach Security Inc. All rights reserved. -# -# The ModSecuirty Core Rule Set is distributed under GPL version 2 -# Please see the enclosed LICENCE file for full details. -# --------------------------------------------------------------- - - -# -# TODO in some cases a valid client (usually automated) generates requests that -# violates the HTTP protocol. Create exceptions for those clients, but try -# to limit the exception to a source IP or other additional properties of -# the request such as URL and not allow the violation generally. -# - -# Use status code 400 response status code by default as protocol violations -# are in essence bad requests. -SecDefaultAction "log,pass,phase:1,status:400" - -# Do not accept requests without common headers. -# -# Implies either an attacker or a legitimate automation client. -# -SecRule REQUEST_URI "^/$" "chain,skip:4" -SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" -SecRule REQUEST_HEADERS:User-Agent "^Apache.*\(internal dummy connection\)$" "t:none" - -SecRule &REQUEST_HEADERS:Host "@eq 0" \ - "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',severity:'4'" -SecRule REQUEST_HEADERS:Host "^$" \ - "deny,log,auditlog,status:400,msg:'Request Missing a Host Header',id:'960008',severity:'4'" - - -SecRule &REQUEST_HEADERS:Accept "@eq 0" \ - "chain,skip:1,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015'" -SecRule REQUEST_METHOD "!OPTIONS" -SecRule REQUEST_HEADERS:Accept "^$" \ - "chain,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',id:'960015'" -SecRule REQUEST_METHOD "!OPTIONS" - -SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ - "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',severity:'4'" -SecRule REQUEST_HEADERS:User-Agent "^$" \ - "deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',id:'960009',severity:'4'" - - -# Check that the host header is not an IP address -# -SecRule REQUEST_HEADERS:Host "^[\d\.]+$" "deny,log,auditlog,status:400,msg:'Host header is a numeric IP address', severity:'2',id:'960017'" +# --------------------------------------------------------------- +# Core ModSecurity Rule Set +# Copyright (C) 2006 Breach Security Inc. All rights reserved. +# +# The ModSecuirty Core Rule Set is distributed under GPL version 2 +# Please see the enclosed LICENCE file for full details. +# --------------------------------------------------------------- + + +# +# TODO in some cases a valid client (usually automated) generates requests that +# violates the HTTP protocol. Create exceptions for those clients, but try +# to limit the exception to a source IP or other additional properties of +# the request such as URL and not allow the violation generally. +# + +# Use status code 400 response status code by default as protocol violations +# are in essence bad requests. +SecDefaultAction "log,pass,phase:2,status:400" + +# Do not accept requests without common headers. +# +# Implies either an attacker or a legitimate automation client. +# +SecRule REQUEST_URI "^/$" "chain,skip:4" +SecRule REMOTE_ADDR "^127\.0\.0\.1$" "chain" +SecRule REQUEST_HEADERS:User-Agent "^Apache.*\(internal dummy connection\)$" "t:none" + +SecRule &REQUEST_HEADERS:Host "@eq 0" \ + "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a Host Header',,id:'960008',severity:'4'" +SecRule REQUEST_HEADERS:Host "^$" \ + "deny,log,auditlog,status:400,msg:'Request Missing a Host Header',,id:'960008',severity:'4'" + + +SecRule &REQUEST_HEADERS:Accept "@eq 0" \ + "chain,skip:1,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',,id:'960015'," +SecRule REQUEST_METHOD "!^OPTIONS$" "t:none" +SecRule REQUEST_HEADERS:Accept "^$" \ + "chain,deny,log,auditlog,status:400,msg:'Request Missing an Accept Header', severity:'2',,id:'960015'," +SecRule REQUEST_METHOD "!^OPTIONS$" "t:none" + +SecRule &REQUEST_HEADERS:User-Agent "@eq 0" \ + "skip:1,deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',,id:'960009',severity:'4'" +SecRule REQUEST_HEADERS:User-Agent "^$" \ + "deny,log,auditlog,status:400,msg:'Request Missing a User Agent Header',,id:'960009',severity:'4'" + + +SecRule &REQUEST_HEADERS:Content-Type "@eq 0" \ + "chain,deny,log,auditlog,status:400,msg:'Request Containing Content, but Missing Content-Type header',,id:'960904',severity:'4'" +SecRule REQUEST_HEADERS:Content-Length "!^0$" + + +# Check that the host header is not an IP address +# +SecRule REQUEST_HEADERS:Host "^[\d\.]+$" "deny,log,auditlog,status:400,msg:'Host header is a numeric IP address', severity:'2',,id:'960017'," diff --git a/rules/blocking/modsecurity_crs_40_generic_attacks.conf b/rules/blocking/modsecurity_crs_40_generic_attacks.conf index dc3a9e7e..2d0b013d 100644 --- a/rules/blocking/modsecurity_crs_40_generic_attacks.conf +++ b/rules/blocking/modsecurity_crs_40_generic_attacks.conf @@ -1,83 +1,94 @@ -# --------------------------------------------------------------- -# Core ModSecurity Rule Set -# Copyright (C) 2006 Breach Security Inc. All rights reserved. -# -# The ModSecuirty Core Rule Set is distributed under GPL version 2 -# Please see the enclosed LICENCE file for full details. -# --------------------------------------------------------------- - - -# -# TODO While some of the pattern groups such as command injection are usually -# safe of false positives, other pattern groups such as SQL injection and -# XSS may require setting exceptions and therefore are set to log only by -# default. -# -# Start ModSecurity in monitoring only mode and check whether your -# application requires exceptions for a specific URL, Pattern or source IP -# before moving to blocking mode. - -SecDefaultAction "log,pass,phase:2,status:500,t:urlDecodeUni,t:htmlEntityDecode,t:lowercase" - -# Session fixation -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "(?:\.cookie\b.*?;\W*?(?:expires|domain)\W*?=|\bhttp-equiv\W+set-cookie\b)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Session Fixation. Matched signature <%{TX.0}>',id:'950009',severity:'2'" - -# Blind SQL injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "(?:\b(?:(?:s(?:ys\.(?:user_(?:(?:t(?:ab(?:_column|le)|rigger)|object|view)s|c(?:onstraints|atalog))|all_tables|tab)|elect\b.{0,40}\b(?:substring|ascii|user))|m(?:sys(?:(?:queri|ac)e|relationship|column|object)s|ysql.user)|c(?:onstraint_type|harindex)|attnotnull)\b|(?:locate|instr)\W+\()|\@\@spid\b)" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',id:'950007',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\b(?:benchmark|encode)\b" \ -# "chain,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',id:'950903',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "[\\(\)\%#]\|--" -SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\b(?:(?:s(?:ys(?:(?:(?:process|tabl)e|filegroup|object)s|c(?:o(?:nstraint|lumn)s|at)|dba|ibm)|ubstr(?:ing)?)|user_(?:(?:(?:constrain|objec)t|tab(?:_column|le)|ind_column|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|(?:dba|mb)_users|xtype\W+\bchar|rownum)\b|t(?:able_name\b|extpos\W+\())" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Blind SQL Injection Attack. Matched signature <%{TX.0}>',id:'950904',severity:'2'" - -# SQL injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "(?:\b(?:(?:s(?:elect\b(?:.{1,100}?\b(?:(?:length|count|top)\b.{1,100}?\bfrom|from\b.{1,100}?\bwhere)|.*?\b(?:d(?:ump\b.*\bfrom|ata_type)|(?:to_(?:numbe|cha)|inst)r))|p_(?:(?:addextendedpro|sqlexe)c|(?:oacreat|prepar)e|execute(?:sql)?|makewebtask)|ql_(?:longvarchar|variant))|xp_(?:reg(?:re(?:movemultistring|ad)|delete(?:value|key)|enum(?:value|key)s|addmultistring|write)|e(?:xecresultset|numdsn)|(?:terminat|dirtre)e|availablemedia|loginconfig|cmdshell|filelist|makecab|ntsec)|u(?:nion\b.{1,100}?\bselect|tl_(?:file|http))|group\b.*\bby\b.{1,100}?\bhaving|load\b\W*?\bdata\b.*\binfile|(?:n?varcha|tbcreato)r|autonomous_transaction|open(?:rowset|query)|dbms_java)\b|i(?:n(?:to\b\W*?\b(?:dump|out)file|sert\b\W*?\binto|ner\b\W*?\bjoin)\b|(?:f(?:\b\W*?\(\W*?\bbenchmark|null\b)|snull\b)\W*?\()|(?:having|or|and)\b\s+?(?:\d{1,10}|'[^=]{1,10}')\s*?[=<>]+|(?:print\]\b\W*?\@|root)\@|c(?:ast\b\W*?\(|oalesce\b))|(?:;\W*?\b(?:shutdown|drop)|\@\@version)\b|'(?:s(?:qloledb|a)|msdasql|dbo)')" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',id:'950001',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\b(?:rel(?:(?:nam|typ)e|kind)|a(?:ttn(?:ame|um)|scii)|c(?:o(?:nver|un)t|ha?r)|s(?:hutdown|elect)|to_(?:numbe|cha)r|u(?:pdate|nion)|d(?:elete|rop)|group\b\W*\bby|having|insert|length|where)\b" \ -# "chain,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',id:'950905',severity:'2'" -#SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "[\\(\)\%#]\|--" -SecRule REQUEST_FILENAME|ARGS|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "\b(?:user_(?:(?:object|table|user)s|password|group)|a(?:tt(?:rel|typ)id|ll_objects)|object_(?:(?:nam|typ)e|id)|pg_(?:attribute|class)|column_(?:name|id)|substr(?:ing)?|table_name|mb_users|rownum)\b" \ - "capture,t:replaceComments,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'SQL Injection Attack. Matched signature <%{TX.0}>',id:'950906',severity:'2'" - -# XSS -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "(?:\b(?:on(?:(?:mo(?:use(?:o(?:ver|ut)|down|move|up)|ve)|key(?:press|down|up)|c(?:hange|lick)|s(?:elec|ubmi)t|(?:un)?load|dragdrop|resize|focus|blur)\b\W*?=|abort\b)|(?:l(?:owsrc\b\W*?\b(?:(?:java|vb)script|shell)|ivescript)|(?:href|url)\b\W*?\b(?:(?:java|vb)script|shell)|background-image|mocha):|type\b\W*?\b(?:text\b(?:\W*?\b(?:j(?:ava)?|ecma)script\b| [vbscript])|application\b\W*?\bx-(?:java|vb)script\b)|s(?:(?:tyle\b\W*=.*\bexpression\b\W*|ettimeout\b\W*?)\(|rc\b\W*?\b(?:(?:java|vb)script|shell|http):)|(?:c(?:opyparentfolder|reatetextrange)|get(?:special|parent)folder)\b|a(?:ctivexobject\b|lert\b\W*?\())|<(?:(?:body\b.*?\b(?:backgroun|onloa)d|input\b.*?\\btype\b\W*?\bimage)\b|!\[CDATA\[|script|meta)|(?:\.(?:(?:execscrip|addimpor)t|(?:fromcharcod|cooki)e|innerhtml)|\@import)\b)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Cross-site Scripting (XSS) Attack. Matched signature <%{TX.0}>',id:'950004',severity:'2'" - -# file injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "(?:\b(?:\.(?:ht(?:access|passwd|group)|www_?acl)|global\.asa|httpd\.conf|boot\.ini)\b|\/etc\/)" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Remote File Access Attempt. Matched signature <%{TX.0}>',id:'950005',severity:'2'" - -# Command access -SecRule REQUEST_FILENAME "\b(?:n(?:map|et|c)|w(?:guest|sh)|cmd(?:32)?|telnet|rcmd|ftp)\.exe\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Access. Matched signature <%{TX.0}>',id:'950002',severity:'2'" - -# Command injection -SecRule ARGS|ARGS_NAMES|REQUEST_HEADERS "(?:\b(?:(?:n(?:et(?:\b\W+?\blocalgroup|\.exe)|(?:map|c)\.exe)|t(?:racer(?:oute|t)|elnet\.exe|clsh8?|ftp)|(?:w(?:guest|sh)|rcmd|ftp)\.exe|echo\b\W*?\by+)\b|c(?:md(?:(?:32)?\.exe\b|\b\W*?\/c)|d(?:\b\W*?[\\\/]|\W*?\.\.)|hmod.{0,40}?\+.{0,3}x))|[\;\|\`]\W*?\b(?:(?:c(?:h(?:grp|mod|own|sh)|md|pp|c)|p(?:asswd|ython|erl|ing|s)|n(?:asm|map|c)|f(?:inger|tp)|(?:kil|mai)l|(?:xte)?rm|ls(?:of)?|telnet|uname|echo|id)\b|g(?:\+\+|cc\b))|\/(?:c(?:h(?:grp|mod|own|sh)|pp|c)|p(?:asswd|ython|erl|ing|s)|n(?:asm|map|c)|f(?:inger|tp)|(?:kil|mai)l|g(?:\+\+|cc)|(?:xte)?rm|ls(?:of)?|telnet|uname|echo|id)(?:[\'\"\|\;\`\-\s]|$))" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection. Matched signature <%{TX.0}>',id:'950006',severity:'2'" -SecRule "ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:User-Agent" \ - "\bwget\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'System Command Injection. Matched signature <%{TX.0}>',id:'950907',severity:'2'" - -# Coldfusion injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "\bcf(?:usion_(?:d(?:bconnections_flush|ecrypt)|set(?:tings_refresh|odbcini)|getodbc(?:dsn|ini)|verifymail|encrypt)|_(?:(?:iscoldfusiondatasourc|getdatasourceusernam)e|setdatasource(?:password|username))|newinternal(?:adminsecurit|registr)y|admin_registry_(?:delete|set)|internaldebug)\b" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'Injection of Undocumented ColdFusion Tags. Matched signature <%{TX.0}>',id:'950008',severity:'2'" - -# LDAP injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS|!REQUEST_HEADERS:Referer "(?:\((?:\W*?(?:objectc(?:ategory|lass)|homedirectory|[gu]idnumber|cn)\b\W*?=|[^\w\x80-\xFF]*?[\!\&\|][^\w\x80-\xFF]*?\()|\)[^\w\x80-\xFF]*?\([^\w\x80-\xFF]*?[\!\&\|])" \ - "capture,ctl:auditLogParts=+E,deny,log,auditlog,status:501,msg:'LDAP Injection Attack. Matched signature <%{TX.0}>',id:'950010',severity:'2'" - -# SSI injection -SecRule REQUEST_FILENAME|ARGS|ARGS_NAMES|REQUEST_HEADERS "