From f811ec65184640f84b7bdbd74a7bd6e296e0b803 Mon Sep 17 00:00:00 2001 From: Felipe Zimmerle Date: Thu, 30 Jul 2015 20:57:23 -0300 Subject: [PATCH] Adds support to @ipMatch operator --- src/Makefile.am | 2 + src/operators/ip_match.cc | 25 +- src/operators/ip_match.h | 12 +- src/utils/ip_tree.cc | 121 ++++ src/utils/ip_tree.h | 50 ++ src/utils/msc_tree.cc | 1101 +++++++++++++++++++++++++++++++++++++ src/utils/msc_tree.h | 104 ++++ test/unit/unit.cc | 1 - 8 files changed, 1402 insertions(+), 14 deletions(-) create mode 100644 src/utils/ip_tree.cc create mode 100644 src/utils/ip_tree.h create mode 100644 src/utils/msc_tree.cc create mode 100644 src/utils/msc_tree.h diff --git a/src/Makefile.am b/src/Makefile.am index a26fcaf9..7bb4e71c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -99,7 +99,9 @@ ACTIONS = \ UTILS = \ utils/geo_lookup.cc \ utils/https_client.cc \ + utils/ip_tree.cc \ utils/md5.cc \ + utils/msc_tree.cc \ utils/sha1.cc diff --git a/src/operators/ip_match.cc b/src/operators/ip_match.cc index e52aa0de..2342f73c 100644 --- a/src/operators/ip_match.cc +++ b/src/operators/ip_match.cc @@ -15,29 +15,32 @@ #include "operators/ip_match.h" +#include #include +#include "utils/msc_tree.h" #include "operators/operator.h" namespace ModSecurity { namespace operators { -bool IpMatch::evaluate(Assay *assay) { - /** - * @todo Implement the operator IpMatch. - * Reference: https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual#ipmatch - */ - return true; +bool IpMatch::init(const char **error) { + std::string e(""); + bool res = m_tree.addFromBuffer(param, &e); + + if (res == false) { + *error = e.c_str(); + } + + return res; } -IpMatch::IpMatch(std::string op, std::string param, - bool negation) - : Operator() { - this->op = op; - this->param = param; +bool IpMatch::evaluate(Assay *assay, const std::string &input) { + return m_tree.contains(input); } + } // namespace operators } // namespace ModSecurity diff --git a/src/operators/ip_match.h b/src/operators/ip_match.h index f957c91f..217d82da 100644 --- a/src/operators/ip_match.h +++ b/src/operators/ip_match.h @@ -19,6 +19,7 @@ #include #include "operators/operator.h" +#include "utils/ip_tree.h" #ifdef __cplusplus namespace ModSecurity { @@ -27,8 +28,15 @@ namespace operators { class IpMatch : public Operator { public: /** @ingroup ModSecurity_Operator */ - IpMatch(std::string o, std::string p, bool i); - bool evaluate(Assay *assay); + IpMatch(std::string op, std::string param, bool negation) + : Operator(op, param, negation) { } + + bool evaluate(Assay *assay, const std::string &input); + + bool init(const char **error); + + private: + Utils::IpTree m_tree; }; } // namespace operators diff --git a/src/utils/ip_tree.cc b/src/utils/ip_tree.cc new file mode 100644 index 00000000..bbdc50c8 --- /dev/null +++ b/src/utils/ip_tree.cc @@ -0,0 +1,121 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 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 "utils/ip_tree.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include "utils/geo_lookup.h" + +namespace ModSecurity { +namespace Utils { + +void IpTree::postOrderTraversal(TreeNode *node) { + if (node == NULL) { + return; + } + + postOrderTraversal(node->left); + postOrderTraversal(node->right); + + if (node->netmasks) { + delete node->netmasks; + node->netmasks = NULL; + } + if (node->prefix) { + if (node->prefix->buffer) { + delete node->prefix->buffer; + node->prefix->buffer = NULL; + } + if (node->prefix->prefix_data) { + delete node->prefix->prefix_data; + node->prefix->prefix_data = NULL; + } + delete node->prefix; + node->prefix = NULL; + } + delete node; + node = NULL; +} + +IpTree::~IpTree() { + if (m_tree != NULL) { + if (m_tree->ipv4_tree != NULL) { + // Tree_traversal: Post-order to delete all the items. + postOrderTraversal(m_tree->ipv4_tree->head); + delete m_tree->ipv4_tree; + m_tree->ipv4_tree = NULL; + } + if (m_tree->ipv6_tree != NULL) { + // Tree_traversal: Post-order to delete all the items. + postOrderTraversal(m_tree->ipv6_tree->head); + delete m_tree->ipv6_tree; + m_tree->ipv6_tree = NULL; + } + + delete m_tree; + m_tree = NULL; + } +} + + +bool IpTree::addFromBuffer(const std::string& buffer, std::string *error) { + char *error_msg = NULL; + std::stringstream ss; + std::string line; + ss << buffer; + int res = 0; + + for (std::string line; std::getline(ss, line); ) { + res = ip_tree_from_param(buffer.c_str(), &m_tree, &error_msg); + if (res != 0) { + if (error_msg != NULL) { + error->assign(error_msg); + } + return false; + } + } + + return true; +} + + +bool IpTree::contains(const std::string& ip) { + int res = 0; + char *error_msg = NULL; + + res = tree_contains_ip(m_tree, ip.c_str(), &error_msg); + + if (res < 0) { + return false; + } + + if (res > 0) { + return true; + } + + return false; +} + + +} // namespace Utils +} // namespace ModSecurity diff --git a/src/utils/ip_tree.h b/src/utils/ip_tree.h new file mode 100644 index 00000000..fc67dfdf --- /dev/null +++ b/src/utils/ip_tree.h @@ -0,0 +1,50 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 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 + +#ifndef SRC_UTILS_IP_TREE_H_ +#define SRC_UTILS_IP_TREE_H_ + +#include "modsecurity/assay.h" +#include "utils/msc_tree.h" + +namespace ModSecurity { +namespace Utils { + + +class IpTree { + public: + IpTree() + : m_tree(NULL) { } + ~IpTree(); + + bool contains(const std::string &ip); + bool addFromBuffer(const std::string& buffer, std::string *error); + void postOrderTraversal(TreeNode *node); + private: + TreeRoot *m_tree; +}; + + +} // namespace Utils +} // namespace ModSecurity + +#endif // SRC_UTILS_IP_TREE_H_ diff --git a/src/utils/msc_tree.cc b/src/utils/msc_tree.cc new file mode 100644 index 00000000..72e04c2f --- /dev/null +++ b/src/utils/msc_tree.cc @@ -0,0 +1,1101 @@ +/* + * ModSecurity for Apache 2.x, http://www.modsecurity.org/ + * Copyright (c) 2004-2013 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 + +#include "msc_tree.h" + +extern "C" { + +CPTTree *CPTCreateRadixTree() { + CPTTree *tree = NULL; + + tree = (CPTTree *)malloc(sizeof(CPTTree)); + + if(tree == NULL) + return NULL; + + memset(tree, 0, sizeof(CPTTree)); + + return tree; +} + +void ConvertIPNetmask(unsigned char *buffer, unsigned char netmask, unsigned int 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() { + TreeNode *node = NULL; + + node = (TreeNode *)malloc(sizeof(TreeNode)); + + if(node == NULL) + return NULL; + + memset(node, 0, sizeof(TreeNode)); + return node; +} + +CPTData *CPTCreateCPTData(unsigned char netmask) { + + CPTData *prefix_data = (CPTData *)malloc(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, unsigned char *ipdata, unsigned int ip_bitmask, + unsigned char netmask) { + + if(prefix == NULL) + return NULL; + + memcpy(prefix->buffer, ipdata, ip_bitmask/8); + prefix->bitlen = ip_bitmask; + + prefix->prefix_data = CPTCreateCPTData(netmask); + + if(prefix->prefix_data == NULL) + return NULL; + + return prefix; +} + +TreePrefix *CPTCreatePrefix(unsigned char *ipdata, unsigned int ip_bitmask, + unsigned char netmask) { + + TreePrefix *prefix = NULL; + int bytes = ip_bitmask/8; + + if ((ip_bitmask % 8 != 0) || (ipdata == NULL)) { + return NULL; + } + + prefix = (TreePrefix *)malloc(sizeof(TreePrefix)); + if (prefix == NULL) + return NULL; + + memset(prefix, 0, sizeof(TreePrefix)); + + prefix->buffer = (unsigned char *)malloc(bytes); + + if(prefix->buffer == NULL) + return NULL; + + memset(prefix->buffer, 0, bytes); + + return InsertDataPrefix(prefix, ipdata, ip_bitmask, netmask); +} + +void CPTAppendToCPTDataList(CPTData *n, CPTData **list) { + CPTData *temp = NULL, *prev = NULL; + + if (n == NULL) { + return; + } + + if (list == NULL) { + return; + } + + prev = *list; + temp = *list; + + while (temp != NULL) { + if (n->netmask > temp->netmask) + break; + prev = temp; + temp = temp->next; + } + + if (temp == *list) { + n->next = *list; + *list = n; + } else { + n->next = prev->next; + prev->next = n; + } + + return; +} + +int TreePrefixContainNetmask(TreePrefix *prefix, unsigned char netmask) { + CPTData *prefix_data = NULL; + + if (prefix == NULL) { + return 0; + } + + prefix_data = prefix->prefix_data; + + while (prefix_data != NULL) { + if (prefix_data->netmask == netmask) + return 1; + prefix_data = prefix_data->next; + } + + return 0; +} + +int CheckBitmask(unsigned char netmask, unsigned int 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, unsigned char netmask, unsigned int 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 = (unsigned char *)malloc(node->count * sizeof(unsigned char)); + + 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, unsigned char netmask, unsigned char 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 = (unsigned char *) malloc(node->count * sizeof(unsigned char)); + + 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(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree, unsigned char netmask) { + unsigned char *buffer = NULL; + unsigned char bitlen = 0; + int bit_validation = 0, test_bit = 0; + int i = 0, j = 0, temp = 0; + unsigned int 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); + + if (prefix == NULL) { + return NULL; + } + + if (tree->head == NULL) { + node = CPTCreateNode(); + 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; + + while(prefix_data != NULL) { + if (prefix_data->netmask == netmask) + ++found; + prefix_data = prefix_data->next; + } + + if (found != 0) { + + CPTData *prefix_data = CPTCreateCPTData(netmask); + 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 = (unsigned char *)malloc(node->count * sizeof(unsigned char)); + + 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); + } + return node; + } + + new_node = CPTCreateNode(); + + 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(); + + 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 = (unsigned char *)malloc((node->count - i) * sizeof(unsigned char)); + + 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, unsigned int netmask) { + + while(prefix_data != NULL) { + if (prefix_data->netmask == netmask) { + return 1; + } + prefix_data = prefix_data->next; + } + + return 0; +} + +int TreePrefixNetmask(TreePrefix *prefix, unsigned int netmask, int flag) { + CPTData *prefix_data = NULL; + int ret = 0; + + if (prefix == NULL) { + //if (msr && 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 && msr->txcfg->debuglog_level >= 9) { + // msr_log(msr, 9, "TreePrefixNetmask: Cannot find a prefix with correct netmask."); + //} + return 0; + } else { + //if (msr && msr->txcfg->debuglog_level >= 9) { + // msr_log(msr, 9, "TreePrefixNetmask: Found a prefix with correct netmask."); + //} + return 1; + } + } + + //if (msr && 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(unsigned char *buffer, unsigned int ip_bitmask, TreeNode *node) { + unsigned int x, y; + + if(node == NULL) { + //if (msr && msr->txcfg->debuglog_level >= 9) { + // msr_log(msr, 9, "CPTRetriveNode: Node tree is NULL."); + //} + return NULL; + } + + if(buffer == NULL) { + //if (msr && 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 && 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(unsigned char *ipdata, unsigned char 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 && 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(ipdata, ip_bitmask, node); + + if (node && node->bit != ip_bitmask) { + //if (msr && 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 && 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(node->prefix, netmask_node->netmasks[j], FALSE)) { + //if (msr && 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(node->prefix, netmask_node->netmasks[j], FALSE)) { + //if (msr && msr->txcfg->debuglog_level >= 9) { + // msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address"); + //} + return node; + } + } + } + + j++; + } + + return CPTFindElementIPNetblock(ipdata, ip_bitmask, netmask_node->parent); +} + +TreeNode *CPTFindElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree) { + TreeNode *node = NULL; + int mask = 0, bytes = 0; + unsigned char temp_data[NETMASK_256-1]; + + if (tree == NULL) { + //if (msr && 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 && 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 && 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(temp_data, ip_bitmask, node); + + if (node && (node->bit != ip_bitmask)) { + //if (msr && 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 && msr->txcfg->debuglog_level >= 9) { + // msr_log(msr, 9, "CPTFindElement: Node tree is NULL."); + //} + return node; + } + + if(node->prefix == NULL) { + //if (msr && 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(node->prefix, ip_bitmask, TRUE)) { + //if (msr && 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(node->prefix, ip_bitmask, TRUE)) { + //if (msr && msr->txcfg->debuglog_level >= 9) { + //msr_log(msr, 9, "CPTFindElement: Node found for provided ip address"); + //} + return node; + } + } + } + + return CPTFindElementIPNetblock(temp_data, ip_bitmask, node); +} + +TreeNode *CPTIpMatch(unsigned char *ipdata, CPTTree *tree, int type) { + + if(tree == NULL) { + //if (msr && 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 && 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 && msr->txcfg->debuglog_level >= 9) { + //msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type); + //} + return CPTFindElement(ipdata, NETMASK_32, tree); + case IPV6_TREE: + //if (msr && msr->txcfg->debuglog_level >= 9) { + //msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type); + //} + return CPTFindElement(ipdata, NETMASK_128, tree); + default: + //if (msr && 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) { + unsigned long ip, ret; + unsigned char netmask_v4 = NETMASK_32, netmask_v6 = NETMASK_128; + char ip_strv4[NETMASK_32], ip_strv6[NETMASK_128]; + struct in_addr addr4; + struct in6_addr addr6; + int pos = 0; + char *ptr = NULL; + + if(tree == NULL) + return NULL; + + pos = strchr(buffer, '/') - buffer; + + switch(type) { + + case IPV4_TREE: + memset(&addr4, 0, sizeof(addr4)); + memset(ip_strv4, 0x0, NETMASK_32); + + strncpy(ip_strv4, buffer, sizeof(ip_strv4)); + *(ip_strv4 + (sizeof(ip_strv4) - 1)) = '\0'; + + ptr = strdup(ip_strv4); + netmask_v4 = is_netmask_v4(ptr); + + if (netmask_v4 > NETMASK_32) { + free(ptr); + ptr = NULL; + return NULL; + } + + if(ptr != NULL) { + free(ptr); + ptr = NULL; + } + + if(netmask_v4 == 0) { + return NULL; + } + else if (netmask_v4 != NETMASK_32 && pos < strlen(ip_strv4)) { + ip_strv4[pos] = '\0'; + } + + ret = inet_pton(AF_INET, ip_strv4, &addr4); + + if (ret <= 0) { + return NULL; + } + + ip = addr4.s_addr; + + tree->count++; + + return CPTAddElement((unsigned char *)&ip, NETMASK_32, tree, netmask_v4); + + case IPV6_TREE: + memset(&addr6, 0, sizeof(addr6)); + memset(ip_strv6, 0x0, NETMASK_128); + + strncpy(ip_strv6, buffer, sizeof(ip_strv6)); + *(ip_strv6 + sizeof(ip_strv6) - 1) = '\0'; + + ptr = strdup(ip_strv6); + netmask_v6 = is_netmask_v6(ptr); + + if (netmask_v6 > NETMASK_128) { + free(ptr); + ptr = NULL; + return NULL; + } + + if(ptr != NULL) { + free(ptr); + ptr = NULL; + } + + if(netmask_v6 == 0) { + return NULL; + } + else if (netmask_v6 != NETMASK_128 && pos < strlen(ip_strv6)) { + ip_strv6[pos] = '\0'; + } + + ret = inet_pton(AF_INET6, ip_strv6, &addr6); + + if (ret <= 0) + { + return NULL; + } + + tree->count++; + + return CPTAddElement((unsigned char *)&addr6.s6_addr, NETMASK_128, tree, netmask_v6); + default: + return NULL; + } + + return NULL; +} + + + + + + + + + + +int tree_contains_ip(TreeRoot *rtree, + const char *value, char **error_msg) +{ + struct in_addr in; + struct in6_addr in6; + + if (rtree == NULL) + { + return 0; + } + + if (strchr(value, ':') == NULL) { + if (inet_pton(AF_INET, value, &in) <= 0) { + //*error_msg = apr_psprintf(mp, "IPmatch: bad IPv4 " \ + // "specification \"%s\".", value); + return -1; + } + + if (CPTIpMatch((unsigned char *)&in.s_addr, rtree->ipv4_tree, + IPV4_TREE) != NULL) { + return 1; + } + } + else { + if (inet_pton(AF_INET6, value, &in6) <= 0) { + //*error_msg = apr_psprintf(mp, "IPmatch: bad IPv6 " \ + // "specification \"%s\".", value); + return -1; + } + + if (CPTIpMatch((unsigned char *)&in6.s6_addr, rtree->ipv6_tree, + IPV6_TREE) != NULL) { + return 1; + } + } + + + return 0; +} + +int ip_tree_from_param( + const char *param, TreeRoot **rtree, char **error_msg) +{ + char *param_copy = strdup(param); + char *saved = NULL; + char *str = NULL; + TreeNode *tnode = NULL; + + if (create_radix_tree(rtree, error_msg)) + { + return -1; + } + + str = strtok_r(param_copy, ",", &saved); + while (str != NULL) + { + if (strchr(str, ':') == NULL) + { + tnode = TreeAddIP(str, (*rtree)->ipv4_tree, IPV4_TREE); + } + else + { + tnode = TreeAddIP(str, (*rtree)->ipv6_tree, IPV6_TREE); + } + + if (tnode == NULL) + { + //*error_msg = apr_psprintf("Could not add entry " \ + // "\"%s\" from: %s.", str, param); + free(param_copy); + return -1; + } + + str = strtok_r(NULL, ",", &saved); + } + free(param_copy); + + return 0; +} + + +/** \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(ip_strv4 == NULL) + return netmask_v4; + + 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(ip_strv6 == NULL) + return netmask_v6; + + if ((mask_str = strchr(ip_strv6, '/'))) { + *(mask_str++) = '\0'; + + if (strchr(mask_str, ':') != NULL) { + return 0; + } + + cidr = atoi(mask_str); + if ((cidr < 0) || (cidr > 128)) { + return 0; + } + netmask_v6 = (unsigned char)cidr; + } + + return netmask_v6; +} + + +int create_radix_tree(TreeRoot **rtree, char **error_msg) +{ + *rtree = (TreeRoot *)malloc(sizeof(TreeRoot)); + if (*rtree == NULL) + { + //*error_msg = apr_psprintf(mp, "Failed allocating " \ + // "memory to TreeRoot."); + goto root_node_failed; + } + memset(*rtree, 0, sizeof(TreeRoot)); + + (*rtree)->ipv4_tree = CPTCreateRadixTree(); + if ((*rtree)->ipv4_tree == NULL) + { + //*error_msg = apr_psprintf(mp, "IPmatch: Tree initialization " \ + // "failed."); + goto ipv4_tree_failed; + } + + (*rtree)->ipv6_tree = CPTCreateRadixTree(); + if ((*rtree)->ipv6_tree == NULL) + { + //*error_msg = apr_psprintf(mp, "IPmatch: Tree initialization " \ + // "failed."); + goto ipv6_tree_failed; + } + + return 0; + +ipv6_tree_failed: +ipv4_tree_failed: +root_node_failed: + return -1; +} + +} diff --git a/src/utils/msc_tree.h b/src/utils/msc_tree.h new file mode 100644 index 00000000..a1e1e34f --- /dev/null +++ b/src/utils/msc_tree.h @@ -0,0 +1,104 @@ +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2013 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. +*/ + + +/* + * + * TODO: This is an improved copy of the ModSecurity 2.9 file, this may need + * some other enhancements and/or fixes. + * + */ + +#ifndef SRC_UTILS_MSC_TREE_H_ +#define SRC_UTILS_MSC_TREE_H_ + + +typedef struct CPTData CPTData; +typedef struct TreePrefix TreePrefix; +typedef struct TreeNode TreeNode; +typedef struct CPTTree CPTTree; +typedef struct TreeRoot TreeRoot; + +#define IPV4_TREE 0x1 +#define IPV6_TREE 0x2 + +#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 + +#define FALSE 0 +#define TRUE 1 + +extern "C" { + +struct CPTData { + unsigned char netmask; + struct CPTData *next; +}; + +struct TreePrefix { + unsigned char *buffer; + unsigned int bitlen; + CPTData *prefix_data; +}; + +struct TreeNode { + unsigned int bit; + int count; + unsigned char *netmasks; + TreePrefix *prefix; + struct TreeNode *left, *right; + struct TreeNode *parent; +}; + +struct CPTTree { + int count; + TreeNode *head; +}; + +struct TreeRoot { + CPTTree *ipv4_tree; + CPTTree *ipv6_tree; +}; + +CPTTree *CPTCreateRadixTree(); +TreeNode *CPTIpMatch(unsigned char *ipdata, CPTTree *tree, int type); +TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type); + +unsigned char is_netmask_v4(char *ip_strv4); + +unsigned char is_netmask_v6(char *ip_strv6); + +/** @ingroup ModSecurity_Legacy */ +int tree_contains_ip(TreeRoot *rtree, + const char *value, char **error_msg); + +int ip_tree_from_param(const char *param, TreeRoot **rtree, char **error_msg); +int create_radix_tree(TreeRoot **rtree, char **error_msg); +} + +#endif // SRC_UTILS_MSC_TREE_H_ diff --git a/test/unit/unit.cc b/test/unit/unit.cc index 93afcaef..e776f1fb 100644 --- a/test/unit/unit.cc +++ b/test/unit/unit.cc @@ -122,7 +122,6 @@ int main(int argc, char **argv) { } delete vec; } - }