From 14156d831b8cf5acc476ee818a9851ca14235c43 Mon Sep 17 00:00:00 2001 From: brenosilva Date: Fri, 1 Jun 2012 20:26:51 +0000 Subject: [PATCH] Add ipmatchFromfile --- apache2/Makefile.am | 2 +- apache2/Makefile.win | 2 +- apache2/modules.mk | 4 +- apache2/msc_tree.c | 885 +++++++++++++++++++++++++++++++++++++++++ apache2/msc_tree.h | 75 ++++ apache2/msc_util.c | 57 +++ apache2/msc_util.h | 3 + apache2/re_operators.c | 190 +++++++++ tests/Makefile.am | 1 + 9 files changed, 1215 insertions(+), 4 deletions(-) create mode 100644 apache2/msc_tree.c create mode 100644 apache2/msc_tree.h diff --git a/apache2/Makefile.am b/apache2/Makefile.am index b6d43325..1d233162 100644 --- a/apache2/Makefile.am +++ b/apache2/Makefile.am @@ -11,7 +11,7 @@ mod_security2_la_SOURCES = mod_security2.c \ re_variables.c msc_logging.c msc_xml.c \ msc_multipart.c modsecurity.c msc_parsers.c \ msc_util.c msc_pcre.c persist_dbm.c msc_reqbody.c \ - msc_geo.c msc_gsb.c msc_crypt.c msc_unicode.c acmp.c msc_lua.c msc_release.c + msc_geo.c msc_gsb.c msc_crypt.c msc_tree.c msc_unicode.c acmp.c msc_lua.c msc_release.c mod_security2_la_CFLAGS = @APXS_CFLAGS@ @APR_CFLAGS@ @APU_CFLAGS@ \ @PCRE_CFLAGS@ @LIBXML2_CFLAGS@ @LUA_CFLAGS@ @MODSEC_EXTRA_CFLAGS@ @CURL_CFLAGS@ diff --git a/apache2/Makefile.win b/apache2/Makefile.win index 6ef993eb..f617bf83 100644 --- a/apache2/Makefile.win +++ b/apache2/Makefile.win @@ -45,7 +45,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 msc_geo.obj msc_gsb.obj msc_crypt.obj msc_unicode.obj acmp.obj msc_lua.obj \ + msc_reqbody.obj msc_geo.obj msc_gsb.obj msc_crypt.obj msc_tree.obj msc_unicode.obj acmp.obj msc_lua.obj \ msc_release.obj all: $(DLL) diff --git a/apache2/modules.mk b/apache2/modules.mk index afdb1e1d..3a8dd00a 100644 --- a/apache2/modules.mk +++ b/apache2/modules.mk @@ -1,11 +1,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 msc_gsb msc_crypt msc_unicode acmp msc_lua + persist_dbm msc_reqbody pdf_protect msc_geo msc_gsb msc_crypt msc_tree msc_unicode acmp msc_lua 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_gsb.h msc_crypt.h msc_unicode.h acmp.h utf8tables.h msc_lua.h + msc_geo.h msc_gsb.h msc_crypt.h msc_tree.h msc_unicode.h acmp.h utf8tables.h msc_lua.h ${MOD_SECURITY2:=.slo}: ${H} ${MOD_SECURITY2:=.lo}: ${H} diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c new file mode 100644 index 00000000..0451fab2 --- /dev/null +++ b/apache2/msc_tree.c @@ -0,0 +1,885 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License.  You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + */ + +#include +#include +#include +#include +#include +#include "apr_lib.h" +#include "msc_util.h" +#include "msc_tree.h" + +CPTTree *CPTCreateRadixTree(apr_pool_t *pool) { + CPTTree *tree = NULL; + + tree = apr_palloc(pool, sizeof(CPTTree)); + + if(tree == NULL) + return NULL; + + memset(tree, 0, sizeof(CPTTree)); + tree->pool = pool; + + return tree; +} + +void ConvertIPNetmask(uint8_t *buffer, uint8_t netmask, uint16_t ip_bitmask) { + int aux = 0, bytes = 0; + int mask = 0, mask_bit = 0; + + bytes = ip_bitmask/8; + + while(aux < bytes) { + mask_bit = ((1+aux) * 8); + + if (mask_bit > netmask) { + mask = 0; + if ((mask_bit - netmask) < 8) mask = SHIFT_LEFT_MASK(mask_bit - netmask); + } else { + mask = -1; + } + + buffer[aux] &= mask; + aux++; + } + + return; +} + +TreeNode *CPTCreateNode(apr_pool_t *pool) { + TreeNode *node = NULL; + + node = apr_palloc(pool, sizeof(TreeNode)); + + if(node == NULL) + return NULL; + + memset(node, 0, sizeof(TreeNode)); + return node; +} + +CPTData *CPTCreateCPTData(uint8_t netmask, apr_pool_t *pool) { + + CPTData *prefix_data = apr_palloc(pool, sizeof(CPTData)); + + if (prefix_data == NULL) { + return NULL; + } + + memset(prefix_data, 0, sizeof(CPTData)); + + prefix_data->netmask = netmask; + + return prefix_data; +} + +TreePrefix *InsertDataPrefix(TreePrefix *prefix, uint8_t *ipdata, uint16_t ip_bitmask, + uint8_t netmask, apr_pool_t *pool) { + + if(prefix == NULL) + return NULL; + + memcpy(prefix->buffer, ipdata, ip_bitmask/8); + prefix->bitlen = ip_bitmask; + + prefix->prefix_data = CPTCreateCPTData(netmask, pool); + + if(prefix->prefix_data == NULL) + return NULL; + + return prefix; +} + +TreePrefix *CPTCreatePrefix(uint8_t *ipdata, uint16_t ip_bitmask, + uint8_t netmask, apr_pool_t *pool) { + + TreePrefix *prefix = NULL; + int bytes = ip_bitmask/8; + + if ((ip_bitmask % 8 != 0) || (ipdata == NULL)) { + return NULL; + } + + prefix = apr_palloc(pool, sizeof(TreePrefix)); + if (prefix == NULL) + return NULL; + + memset(prefix, 0, sizeof(TreePrefix)); + + prefix->buffer = apr_palloc(pool, bytes); + + if(prefix->buffer == NULL) + return NULL; + + memset(prefix->buffer, 0, bytes); + + return InsertDataPrefix(prefix, ipdata, ip_bitmask, netmask, pool); +} + +void CPTAppendToCPTDataList(CPTData *new, CPTData **list) { + CPTData *temp = NULL, *prev = NULL; + + if (new == NULL) { + return; + } + + if (list == NULL) { + return; + } + + prev = *list; + temp = *list; + + while (temp != NULL) { + if (new->netmask > temp->netmask) + break; + prev = temp; + temp = temp->next; + } + + if (temp == *list) { + new->next = *list; + *list = new; + } else { + new->next = prev->next; + prev->next = new; + } + + return; +} + +int TreePrefixContainNetmask(TreePrefix *prefix, uint8_t netmask) { + CPTData *prefix_data = NULL; + + if (prefix == NULL) { + return 0; + } + + prefix_data = prefix->prefix_data; + + for(prefix_data != NULL; ; prefix_data = prefix_data->next) { + if (prefix_data->netmask == netmask) + return 1; + } + + return 0; +} + +int CheckBitmask(uint8_t netmask, uint16_t ip_bitmask) { + + switch(netmask) { + + case 0xff: + return 1; + case 0x20: + if(ip_bitmask == 0x20) + return 1; + break; + case 0x80: + if(ip_bitmask == 0x80) + return 1; + break; + } + + return 0; +} + +TreeNode *CPTCreateHead(TreePrefix *prefix, TreeNode *node, CPTTree *tree, uint8_t netmask, uint16_t ip_bitmask) { + + if(tree == NULL) + return NULL; + + if(prefix == NULL) + return NULL; + + if (node != NULL) { + + node->prefix = prefix; + node->bit = prefix->bitlen; + tree->head = node; + + if(CheckBitmask(netmask, ip_bitmask)) + return node; + + node->count++; + node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(uint8_t))); + + if(node->netmasks) + node->netmasks[0] = netmask; + + return node; + + } else { + return NULL; + } + + return NULL; +} + +TreeNode *SetParentNode(TreeNode *node, TreeNode *new_node, CPTTree *tree) { + + if (node->parent == NULL) + tree->head = new_node; + else if (node->parent->right == node) + node->parent->right = new_node; + else + node->parent->left = new_node; + + return new_node; +} + +int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node, + CPTTree *tree, uint8_t netmask, uint8_t bitlen) { + int i; + + if (netmask != NETMASK_256-1 && netmask != NETMASK_128) { + if ((netmask != NETMASK_32 || (netmask == NETMASK_32 && bitlen != NETMASK_32))) { + + node = new_node; + parent = new_node->parent; + + while (parent != NULL && netmask < (parent->bit + 1)) { + node = parent; + parent = parent->parent; + } + + node->count++; + node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(uint8_t))); + + if(node->netmasks == NULL) + return 0; + if ((node->count-1) == 0) { + node->netmasks[0] = netmask; + return 1; + } + + node->netmasks[node->count - 1] = netmask; + + i = node->count - 2; + while (i >= 0) { + if (netmask < node->netmasks[i]) { + node->netmasks[i + 1] = netmask; + break; + } + + node->netmasks[i + 1] = node->netmasks[i]; + node->netmasks[i] = netmask; + i--; + } + } + } + + return 0; +} + +TreeNode *CPTAddElement(uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree, uint8_t netmask) { + uint8_t *buffer = NULL; + uint8_t bitlen = 0; + int bit_validation = 0, test_bit = 0; + int i = 0, j = 0, temp = 0; + uint16_t x, y; + TreeNode *node = NULL, *new_node = NULL; + TreeNode *parent = NULL, *i_node = NULL; + TreeNode *bottom_node = NULL; + TreePrefix *prefix = NULL; + + if (tree == NULL) { + return NULL; + } + + ConvertIPNetmask(ipdata, netmask, ip_bitmask); + + prefix = CPTCreatePrefix(ipdata, ip_bitmask, netmask, tree->pool); + + if (prefix == NULL) { + return NULL; + } + + if (tree->head == NULL) { + node = CPTCreateNode(tree->pool); + return CPTCreateHead(prefix, node, tree, netmask, ip_bitmask); + } + + node = tree->head; + buffer = prefix->buffer; + bitlen = prefix->bitlen; + + while (node->bit < bitlen || node->prefix == NULL) { + + if (bitlen < node->bit) { + if (node->right == NULL) + break; + else + node = node->right; + } else { + x = SHIFT_RIGHT_MASK(node->bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (node->bit % 8)); + + if (TREE_CHECK(buffer[x],y)) { + if (node->right == NULL) + break; + node = node->right; + } else { + if (node->left == NULL) + break; + else + node = node->left; + } + } + } + + bottom_node = node; + + if(node->bit < bitlen) + bit_validation = node->bit; + else + bit_validation = bitlen; + + for (i = 0; (i * NETMASK_8) < bit_validation; i++) { + int net = 0, div = 0; + int cnt = 0; + + if ((temp = (buffer[i] ^ bottom_node->prefix->buffer[i])) == 0) { + test_bit = (i + 1) * NETMASK_8; + continue; + } + + temp += temp; + + for(cnt = 0, net = NETMASK_256, div = 2; net >= NETMASK_2; net = NETMASK_256/div, + div += div, cnt++) { + if(temp >= net) { + test_bit = (i * NETMASK_8) + cnt; + break; + } + } + break; + } + + if (bit_validation < test_bit) + test_bit = bit_validation; + + parent = node->parent; + + while (parent && test_bit <= parent->bit) { + node = parent; + parent = node->parent; + } + + if (test_bit == bitlen && node->bit == bitlen) { + if (node->prefix != NULL) { + int found = 0; + CPTData *prefix_data = NULL; + + prefix_data = node->prefix->prefix_data; + + for(prefix_data != NULL; ; prefix_data = prefix_data->next) { + if (prefix_data->netmask == netmask) + ++found; + } + + if (found != 0) { + + CPTData *prefix_data = CPTCreateCPTData(netmask, tree->pool); + CPTAppendToCPTDataList(prefix_data, &prefix->prefix_data); + + if(CheckBitmask(netmask, ip_bitmask)) + return node; + + parent = node->parent; + while (parent != NULL && netmask < (parent->bit + 1)) { + node = parent; + parent = parent->parent; + } + + node->count++; + new_node = node; + node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(uint8_t))); + + if ((node->count -1) == 0) { + node->netmasks[0] = netmask; + return new_node; + } + + node->netmasks[node->count - 1] = netmask; + + i = node->count - 2; + while (i >= 0) { + if (netmask < node->netmasks[i]) { + node->netmasks[i + 1] = netmask; + break; + } + + node->netmasks[i + 1] = node->netmasks[i]; + node->netmasks[i] = netmask; + i--; + } + } + } else { + node->prefix = CPTCreatePrefix(prefix->buffer, prefix->bitlen, + NETMASK_256-1, tree->pool); + } + return node; + } + + new_node = CPTCreateNode(tree->pool); + + if(new_node == NULL) + return NULL; + + new_node->prefix = prefix; + new_node->bit = prefix->bitlen; + + if (test_bit == bitlen) { + + x = SHIFT_RIGHT_MASK(test_bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (test_bit % 8)); + + if (TREE_CHECK(bottom_node->prefix->buffer[x],y)) { + new_node->right = node; + } else { + new_node->left = node; + } + + new_node->parent = node->parent; + node->parent = SetParentNode(node, new_node, tree); + + } else { + i_node = CPTCreateNode(tree->pool); + + if(i_node == NULL) + return NULL; + + //i_node->prefix = NULL; + i_node->bit = test_bit; + i_node->parent = node->parent; + + if (node->netmasks != NULL) { + i = 0; + while(i < node->count) { + if (node->netmasks[i] < test_bit + 1) + break; + i++; + } + + i_node->netmasks = apr_palloc(tree->pool, (node->count - i) * sizeof(uint8_t)); + + if(i_node->netmasks == NULL) { + return NULL; + } + + j = 0; + while (j < (node->count - i)) { + i_node->netmasks[j] = node->netmasks[i + j]; + j++; + } + + i_node->count = (node->count - i); + node->count = i; + + if (node->count == 0) { + node->netmasks = NULL; + } + } + + x = SHIFT_RIGHT_MASK(test_bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (test_bit % 8)); + + if (TREE_CHECK(buffer[x],y)) { + i_node->left = node; + i_node->right = new_node; + } else { + i_node->left = new_node; + i_node->right = node; + } + + new_node->parent = i_node; + node->parent = SetParentNode(node, i_node, tree); + } + + if (InsertNetmask(node, parent, new_node, tree, netmask, bitlen)) + return new_node; + + return new_node; +} + +int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, uint16_t netmask) { + + for(prefix_data != NULL; ; prefix_data = prefix_data->next) { + if (prefix_data->netmask == netmask) { + return 1; + } + } + + return 0; +} + +int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, uint16_t netmask, int flag) { + CPTData *prefix_data = NULL; + int ret = 0; + + if (prefix == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "TreePrefixNetmask: prefix is NULL."); + } + return 0; + } + + prefix_data = prefix->prefix_data; + + if (flag == 1) { + + if(prefix_data == NULL) return 0; + + if (prefix_data->netmask != netmask) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "TreePrefixNetmask: Cannot find a prefix with correct netmask."); + } + return 0; + } else { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "TreePrefixNetmask: Found a prefix with correct netmask."); + } + return 1; + } + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "TreePrefixNetmask: Check if a prefix has a the correct netmask"); + } + + ret = TreeCheckData(prefix, prefix_data, netmask); + + return ret; +} + +TreeNode *CPTRetriveNode(modsec_rec *msr, uint8_t *buffer, uint16_t ip_bitmask, TreeNode *node) { + uint16_t x, y; + + if(node == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTRetriveNode: Node tree is NULL."); + } + return NULL; + } + + if(buffer == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTRetriveNode: Empty ip address. Nothing to search for."); + } + return NULL; + } + + while (node->bit < ip_bitmask) { + + x = SHIFT_RIGHT_MASK(node->bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (node->bit % 8)); + + if (TREE_CHECK(buffer[x], y)) { + node = node->right; + if (node == NULL) return NULL; + } else { + node = node->left; + if (node == NULL) return NULL; + } + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTRetriveNode: Found the node for provided ip address."); + } + + + return node; +} + +TreeNode *CPTRetriveParentNode(TreeNode *node) { + + while (node != NULL && node->netmasks == NULL) + node = node->parent; + + return node; +} + +TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, uint8_t *ipdata, uint8_t ip_bitmask, TreeNode *node) { + TreeNode *netmask_node = NULL; + int mask = 0, bytes = 0; + int i = 0, j = 0; + int mask_bits = 0; + + node = CPTRetriveParentNode(node); + + if (node == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElementIPNetblock: Node tree is NULL."); + } + return NULL; + } + + netmask_node = node; + + while(j < netmask_node->count) { + bytes = ip_bitmask / 8; + + while( i < bytes ) { + + mask = -1; + mask_bits = ((i + 1) * 8); + + if (mask_bits > netmask_node->netmasks[j]) { + if ((mask_bits - netmask_node->netmasks[j]) < 8) + mask = SHIFT_LEFT_MASK(mask_bits - netmask_node->netmasks[j]); + else + mask = 0; + } + + ipdata[i] &= mask; + i++; + } + + node = CPTRetriveNode(msr, ipdata, ip_bitmask, node); + + if (node && node->bit != ip_bitmask) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but netmask is different."); + } + return NULL; + } + + if (node && node->prefix == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but prefix is NULL."); + } + return NULL; + } + + if (memcmp(node->prefix->buffer, ipdata, bytes) == 0) { + mask = SHIFT_LEFT_MASK(8 - ip_bitmask % 8); + + if ((ip_bitmask % 8) == 0) { + if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address"); + } + return node; + } + } + + if ((node->prefix->buffer[bytes] & mask) == (ipdata[bytes] & mask)) { + if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address"); + } + return node; + } + } + } + + j++; + } + + return CPTFindElementIPNetblock(msr, ipdata, ip_bitmask, netmask_node->parent); +} + +TreeNode *CPTFindElement(modsec_rec *msr, uint8_t *ipdata, uint16_t ip_bitmask, CPTTree *tree) { + TreeNode *node = NULL; + int mask = 0, bytes = 0; + uint8_t temp_data[NETMASK_256-1]; + + if (tree == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Tree is NULL. Cannot proceed searching the ip."); + } + return node; + } + + if (tree->head == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Tree head is NULL. Cannot proceed searching the ip."); + } + return node; + } + + node = tree->head; + + if (ip_bitmask > (NETMASK_256-1)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Netmask cannot be greater than 255"); + } + return NULL; + } + + bytes = ip_bitmask/8; + + memset(temp_data, 0, NETMASK_256-1); + memcpy(temp_data, ipdata, bytes); + + node = CPTRetriveNode(msr, temp_data, ip_bitmask, node); + + if (node && (node->bit != ip_bitmask)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Found a tree node but netmask is different."); + } + return NULL; + } + + if(node == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Node tree is NULL."); + } + return node; + } + + if(node->prefix == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Found a tree node but prefix is NULL."); + } + return node; + } + + if (memcmp(node->prefix->buffer, temp_data, bytes) == 0) { + mask = SHIFT_LEFT_MASK(8 - ip_bitmask % 8); + + if ((ip_bitmask % 8) == 0) { + if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Node found for provided ip address"); + } + return node; + } + } + + if ((node->prefix->buffer[bytes] & mask) == (temp_data[bytes] & mask)) { + if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElement: Node found for provided ip address"); + } + return node; + } + } + } + + return CPTFindElementIPNetblock(msr, temp_data, ip_bitmask, node); +} + +TreeNode *CPTIpMatch(modsec_rec *msr, uint8_t *ipdata, CPTTree *tree, int type) { + + if(tree == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTIpMatch: Tree is NULL. Cannot proceed searching the ip."); + } + return NULL; + } + + if(ipdata == NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTIpMatch: Empty ip address. Nothing to search for."); + } + return NULL; + } + + switch(type) { + case IPV4_TREE: + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type); + } + return CPTFindElement(msr, ipdata, NETMASK_32, tree); + case IPV6_TREE: + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type); + } + return CPTFindElement(msr, ipdata, NETMASK_128, tree); + default: + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTIpMatch: Unknown ip type 0x%x", type); + } + return NULL; + } +} + +TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { + uint32_t ip, ret; + uint8_t netmask_v4 = NETMASK_32, netmask_v6 = NETMASK_128; + char ip_strv4[NETMASK_32], ip_strv6[NETMASK_128]; + struct in_addr addr4; + struct in6_addr addr6; + char *ptr = NULL; + + if(tree == NULL) + return NULL; + + switch(type) { + + case IPV4_TREE: + bzero(&addr4, sizeof(addr4)); + memset(ip_strv4, 0x0, NETMASK_32); + + strncpy(ip_strv4, buffer, sizeof(ip_strv4) - 2); + *(ip_strv4 + (sizeof(ip_strv4) - 1)) = '\0'; + + ptr = strdup(ip_strv4); + netmask_v4 = is_netmask_v4(ptr); + + if(ptr != NULL) { + free(ptr); + ptr = NULL; + } + + if(netmask_v4 == 0) + return NULL; + else if(netmask_v4 != NETMASK_32) { + ip_strv4[strlen(ip_strv4)-3] = '\0'; + } + + ret = inet_pton(AF_INET, ip_strv4, &addr4); + if (ret <= 0) { + return NULL; + } + + ip = addr4.s_addr; + + tree->count++; + + return CPTAddElement((uint8_t *)&ip, NETMASK_32, tree, netmask_v4); + + case IPV6_TREE: + bzero(&addr6, sizeof(addr6)); + memset(ip_strv6, 0x0, NETMASK_128); + + strncpy(ip_strv6, buffer, sizeof(ip_strv6) - 2); + *(ip_strv6 + sizeof(ip_strv6) - 1) = '\0'; + + ptr = strdup(ip_strv6); + netmask_v6 = is_netmask_v6(ptr); + + if(ptr != NULL) { + free(ptr); + ptr = NULL; + } + + if(netmask_v6 == 0) + return NULL; + else if (netmask_v6 != NETMASK_64) { + ip_strv4[strlen(ip_strv6)-3] = '\0'; + } + + ret = inet_pton(AF_INET6, ip_strv6, &addr6); + if(ret <= 0) + return NULL; + + tree->count++; + + return CPTAddElement((uint8_t *)&addr6.s6_addr, NETMASK_128, tree, netmask_v6); + default: + return NULL; + } + + return NULL; +} diff --git a/apache2/msc_tree.h b/apache2/msc_tree.h new file mode 100644 index 00000000..04d96444 --- /dev/null +++ b/apache2/msc_tree.h @@ -0,0 +1,75 @@ +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* +* You may not use this file except in compliance with +* the License.  You may obtain a copy of the License at +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#ifndef __MSC_TREE_H__ +#define __MSC_TREE_H__ + +#include "modsecurity.h" + +#define IPV4_TREE 0x1 +#define IPV6_TREE 0x2 + +#define IPV4_LEN 0x20 +#define IPV6_LEN 0x80 + +#define TREE_CHECK(x, y) ((x) & (y)) +#define MASK_BITS(x) ((x + 1) * 8) +#define SHIFT_LEFT_MASK(x) (-1 << x) +#define SHIFT_RIGHT_MASK(x,y) (x >> y) + +#define NETMASK_256 0x100 +#define NETMASK_128 0x80 +#define NETMASK_64 0x40 +#define NETMASK_32 0x20 +#define NETMASK_16 0x10 +#define NETMASK_8 0x8 +#define NETMASK_4 0x4 +#define NETMASK_2 0x2 + +typedef struct CPTData_ { + uint8_t netmask; + struct CPTData_ *next; +} CPTData; + +typedef struct TreePrefix_ { + uint8_t *buffer; + uint16_t bitlen; + CPTData *prefix_data; +} TreePrefix; + +typedef struct TreeNode_ { + uint16_t bit; + int count; + uint8_t *netmasks; + TreePrefix *prefix; + struct TreeNode_ *left, *right; + struct TreeNode_ *parent; +} TreeNode; + +typedef struct CPTTree_ { + int count; + apr_pool_t *pool; + TreeNode *head; +} CPTTree; + +typedef struct TreeRoot_ { + CPTTree *ipv4_tree; + CPTTree *ipv6_tree; +} TreeRoot; + +DSOLOCAL CPTTree *CPTCreateRadixTree(apr_pool_t *pool); +DSOLOCAL TreeNode *CPTIpMatch(modsec_rec *, uint8_t *, CPTTree *, int); +DSOLOCAL TreeNode *TreeAddIP(const char *, CPTTree *, int); + +#endif /*__MSC_TREE_H__ */ diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 58ebd70d..900da363 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -74,6 +74,63 @@ static unsigned char *c2x(unsigned what, unsigned char *where); static unsigned char x2c(unsigned char *what); static unsigned char xsingle2c(unsigned char *what); +/** \brief Validate IPv4 Netmask + * + * \param ip_strv6 Pointer to ipv6 address + * + * \retval netmask_v4 On Success + */ +unsigned char is_netmask_v4(char *ip_strv4) { + unsigned char netmask_v4 = 32; + char *mask_str = NULL; + int cidr; + + if ((mask_str = strchr(ip_strv4, '/'))) { + *(mask_str++) = '\0'; + + if (strchr(mask_str, '.') != NULL) { + return 0; + } + + cidr = atoi(mask_str); + if ((cidr < 0) || (cidr > 32)) { + return 0; + } + + netmask_v4 = (unsigned char)cidr; + } + + return netmask_v4; +} + +/** \brief Validate IPv6 Netmask + * + * \param ip_strv6 Pointer to ipv6 address + * + * \retval netmask_v6 On Success + */ +unsigned char is_netmask_v6(char *ip_strv6) { + unsigned char netmask_v6 = 128; + char *mask_str = NULL; + int cidr; + + if ((mask_str = strchr(ip_strv6, '/'))) { + *(mask_str++) = '\0'; + + if (strchr(mask_str, '.') != NULL) { + return 0; + } + + cidr = atoi(mask_str); + if ((cidr < 0) || (cidr > 64)) { + return 0; + } + netmask_v6 = (unsigned char)cidr; + } + + return netmask_v6; +} + /** \brief Interpret |HEX| syntax * * \param op_parm Pointer to operator input diff --git a/apache2/msc_util.h b/apache2/msc_util.h index 7ca2eb28..f32cce22 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -116,4 +116,7 @@ char DSOLOCAL *construct_single_var(modsec_rec *msr, char *name); char DSOLOCAL *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp); +unsigned char DSOLOCAL is_netmask_v4(char *ip_strv4); + +unsigned char DSOLOCAL is_netmask_v6(char *ip_strv6); #endif diff --git a/apache2/re_operators.c b/apache2/re_operators.c index dc4e4fd1..aeff0fa1 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -20,6 +20,7 @@ #include "apr_strmatch.h" #include "acmp.h" #include "msc_util.h" +#include "msc_tree.h" #include "msc_crypt.h" #if !defined(WIN32) || !defined(WINNT) @@ -179,6 +180,182 @@ static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *v return 0; } +/** +* \brief Init function to ipmatchFromFile operator +* +* \param rule Pointer to the rule +* \param error_msg Pointer to error msg +* +* \retval 1 On Success +* \retval 0 On Fail +*/ +static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) { + char errstr[1024]; + char buf[HUGE_STRING_LEN + 1]; + const char *rootpath = NULL; + const char *filepath = NULL; + char *fn; + char *start; + char *end; + const char *ipfile_path; + int line = 0; + unsigned short int op_len; + apr_status_t rc; + apr_file_t *fd; + TreeRoot rtree; + TreeNode *tnode; + + if (error_msg == NULL) + return -1; + else + *error_msg = NULL; + + if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'ipmatchFromFile'."); + return 0; + } + + rtree.ipv4_tree = CPTCreateRadixTree(rule->ruleset->mp); + if (rtree.ipv4_tree == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "ipmatchFromFile: Tree tree initialization failed."); + return 0; + } + rtree.ipv6_tree = CPTCreateRadixTree(rule->ruleset->mp); + if (rtree.ipv6_tree == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "ipmatchFromFile: Tree tree initialization failed."); + return 0; + } + + fn = apr_pstrdup(rule->ruleset->mp, rule->op_param); + + ipfile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename))); + + while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++; + if (*fn == '\0') { + *error_msg = apr_psprintf(rule->ruleset->mp, "Empty file specification for operator ipmatchFromFile \"%s\"", fn); + return 0; + } + + filepath = fn; + if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) { + apr_filepath_merge(&fn, ipfile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp); + } + + rc = apr_file_open(&fd, fn, APR_READ | APR_BUFFERED | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp); + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not open ipmatch file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024)); + return 0; + } + + while((rc = apr_file_gets(buf, HUGE_STRING_LEN, fd)) != APR_EOF) { + line++; + if (rc != APR_SUCCESS) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024)); + return 0; + } + + op_len = strlen(buf); + + start = buf; + while ((apr_isspace(*start) != 0) && (*start != '\0')) start++; + for (end = start; end != NULL || *end != '\0' || *end != '\n'; end++) { + if (apr_isxdigit(*end) || *end == '.' || *end == '/' || *end == ':') { + continue; + } + else { + if (*end != '\n') { + *error_msg = apr_psprintf(rule->ruleset->mp, "Invalid char \"%c\" in line %d of file %s", *end, line, fn); + } + break; + } + } + *end = '\0'; + + if ((start == end) || (*start == '#')) continue; + + if (strchr(start, ':') == NULL) { + tnode = TreeAddIP(start, rtree.ipv4_tree, IPV4_TREE); + } + else { + tnode = TreeAddIP(start, rtree.ipv6_tree, IPV6_TREE); + } + if (tnode == NULL) { + *error_msg = apr_psprintf(rule->ruleset->mp, "Could not add entry \"%s\" in line %d of file %s to IP list", start, line, fn); + } + } + + if (fd != NULL) apr_file_close(fd); + rule->op_param_data = &rtree; + return 1; +} + +/** +* \brief Execution function to ipmatchFromFile operator +* +* \param msr Pointer internal modsec request structure +* \param rule Pointer to the rule +* \param var Pointer to variable structure +* \param error_msg Pointer to error msg +* +* \retval -1 On Failure +* \retval 1 On Match +* \retval 0 On No Match +*/ + +static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + TreeRoot *rtree = rule->op_param_data; + TreeNode *node; + apr_sockaddr_t *sa; + struct in_addr in; + struct in6_addr in6; + + if (error_msg == NULL) + return -1; + else + *error_msg = NULL; + + if(rtree == NULL) { + msr_log(msr, 1, "ipMatchFromFile Internal Error: tree value is null."); + return 0; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "IPmatchFromFile: Total tree entries: %d, ipv4 %d ipv6 %d", rtree->ipv4_tree->count+rtree->ipv6_tree->count, + rtree->ipv4_tree->count, rtree->ipv6_tree->count); + } + + if (strchr(var->value, ':') == NULL) { + if (inet_pton(AF_INET, var->value, &in) <= 0) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "IPmatchFromFile: bad IPv4 specification \"%s\".", var->value); + } + *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: bad IP specification \"%s\".", var->value); + return 0; + } + + if (CPTIpMatch(msr, (uint8_t *)&in.s_addr, rtree->ipv4_tree, IPV4_TREE) != NULL) { + *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name); + return 1; + } + } + else { + if (inet_pton(AF_INET6, var->value, &in6) <= 0) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "IPmatchFromFile: bad IPv6 specification \"%s\".", var->value); + } + *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: bad IP specification \"%s\".", var->value); + return 0; + } + + if (CPTIpMatch(msr, (uint8_t *)&in6.s6_addr, rtree->ipv6_tree, IPV6_TREE) != NULL) { + *error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name); + return 1; + } + } + + return 0; +} + /* rsub */ static char *param_remove_escape(msre_rule *rule, char *str, int len) { @@ -4191,6 +4368,19 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_ipmatch_execute ); + /* ipmatchFromFile */ + msre_engine_op_register(engine, + "ipmatchFromFile", + msre_op_ipmatchFromFile_param_init, + msre_op_ipmatchFromFile_execute + ); + /* ipmatchf */ + msre_engine_op_register(engine, + "ipmatchf", + msre_op_ipmatchFromFile_param_init, + msre_op_ipmatchFromFile_execute + ); + /* rsub */ #if !defined(MSC_TEST) msre_engine_op_register(engine, diff --git a/tests/Makefile.am b/tests/Makefile.am index 6668bd2d..2b2b6f46 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -16,6 +16,7 @@ msc_test_SOURCES = msc_test.c \ $(top_srcdir)/apache2/persist_dbm.c \ $(top_srcdir)/apache2/msc_reqbody.c \ $(top_srcdir)/apache2/msc_crypt.c \ + $(top_srcdir)/apache2/msc_tree.c \ $(top_srcdir)/apache2/msc_geo.c \ $(top_srcdir)/apache2/msc_gsb.c \ $(top_srcdir)/apache2/acmp.c \