]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
radix: improve address range handling
authorVictor Julien <vjulien@oisf.net>
Thu, 17 Feb 2022 12:32:17 +0000 (13:32 +0100)
committerVictor Julien <vjulien@oisf.net>
Thu, 17 Feb 2022 16:00:26 +0000 (17:00 +0100)
Handle non-exact address ranges from string. This can come directly
from user input, so here it is accepted but the address is converted
to the address range start. A warning will be issued.

Debug validation checks are added to catch this.

This issue could lead to bad input from iprep (with cidr), defrag config
and htp server personalities to produce a bad radix tree.

Bug: #5084.
Bug: #5085.
Bug: #5086.

src/util-radix-tree.c

index 6b4f24c02f2559e6f79f0e0c6b0418b7b7bdda45..c980fe68a29f4f0c65887056bf49a89bff45b6bc 100644 (file)
@@ -31,6 +31,8 @@
 #include "util-unittest.h"
 #include "util-memcmp.h"
 #include "util-byte.h"
+#include "util-cidr.h"
+#include "util-print.h"
 
 /**
  * \brief Allocates and returns a new instance of SCRadixUserData.
@@ -881,6 +883,49 @@ SCRadixNode *SCRadixAddKeyIPV6(uint8_t *key_stream, SCRadixTree *tree,
     return node;
 }
 
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+static void SCRadixValidateIPv4Key(uint8_t *key, const uint8_t netmask)
+{
+    uint32_t address;
+    memcpy(&address, key, sizeof(address));
+    uint32_t mask = CIDRGet(netmask);
+    if (address != (address & mask)) {
+        uint32_t masked = address & mask;
+        char ostr[16], nstr[16];
+        PrintInet(AF_INET, (void *)&address, ostr, sizeof(ostr));
+        PrintInet(AF_INET, (void *)&masked, nstr, sizeof(nstr));
+        SCLogNotice("input %s/%u != expected %s/%u", ostr, netmask, nstr, netmask);
+        abort();
+    }
+}
+
+static void SCRadixValidateIPv6Key(uint8_t *key, const uint8_t netmask)
+{
+    uint32_t address[4];
+    memcpy(&address, key, sizeof(address));
+
+    uint32_t mask[4];
+    memset(&mask, 0, sizeof(mask));
+    struct in6_addr mask6;
+    CIDRGetIPv6(netmask, &mask6);
+    memcpy(&mask, &mask6.s6_addr, sizeof(mask));
+
+    uint32_t masked[4];
+    masked[0] = address[0] & mask[0];
+    masked[1] = address[1] & mask[1];
+    masked[2] = address[2] & mask[2];
+    masked[3] = address[3] & mask[3];
+
+    if (memcmp(masked, address, sizeof(masked)) != 0) {
+        char ostr[64], nstr[64];
+        PrintInet(AF_INET6, (void *)&address, ostr, sizeof(ostr));
+        PrintInet(AF_INET6, (void *)&masked, nstr, sizeof(nstr));
+        SCLogNotice("input %s/%u != expected %s/%u", ostr, netmask, nstr, netmask);
+        abort();
+    }
+}
+#endif
+
 /**
  * \brief Adds a new IPV4 netblock to the Radix tree
  *
@@ -896,6 +941,9 @@ SCRadixNode *SCRadixAddKeyIPV6(uint8_t *key_stream, SCRadixTree *tree,
 SCRadixNode *SCRadixAddKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
                                        void *user, uint8_t netmask)
 {
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+    SCRadixValidateIPv4Key(key_stream, netmask);
+#endif
     SCRadixNode *node = SCRadixAddKey(key_stream, 32, tree, user, netmask);
 
     return node;
@@ -916,6 +964,9 @@ SCRadixNode *SCRadixAddKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
 SCRadixNode *SCRadixAddKeyIPV6Netblock(uint8_t *key_stream, SCRadixTree *tree,
                                        void *user, uint8_t netmask)
 {
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+    SCRadixValidateIPv6Key(key_stream, netmask);
+#endif
     SCRadixNode *node = SCRadixAddKey(key_stream, 128, tree, user, netmask);
 
     return node;
@@ -966,7 +1017,19 @@ SCRadixNode *SCRadixAddKeyIPV4String(const char *str, SCRadixTree *tree, void *u
         return NULL;
     }
     ip = addr.s_addr;
-
+    if (netmask != 32) {
+        uint32_t mask = CIDRGet(netmask);
+        uint32_t masked = ip & mask;
+        if (masked != ip) {
+            char nstr[16];
+            PrintInet(AF_INET, (void *)&masked, nstr, sizeof(nstr));
+            SCLogWarning(SC_ERR_INVALID_IP_NETBLOCK, "adding '%s' as '%s/%u'", str, nstr, netmask);
+            ip = masked;
+        }
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+        SCRadixValidateIPv4Key((uint8_t *)&ip, netmask);
+#endif
+    }
     return SCRadixAddKey((uint8_t *)&ip, 32, tree, user, netmask);
 }
 
@@ -1014,6 +1077,20 @@ SCRadixNode *SCRadixAddKeyIPV6String(const char *str, SCRadixTree *tree, void *u
         return NULL;
     }
 
+    if (netmask != 128) {
+        struct in6_addr mask6;
+        CIDRGetIPv6(netmask, &mask6);
+        for (int i = 0; i < 16; i++) {
+            addr.s6_addr[i] &= mask6.s6_addr[i];
+        }
+        char nstr[64];
+        PrintInet(AF_INET6, (void *)&addr.s6_addr, nstr, sizeof(nstr));
+        SCLogWarning(SC_ERR_INVALID_IP_NETBLOCK, "adding '%s' as '%s/%u'", str, nstr, netmask);
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+        SCRadixValidateIPv6Key((uint8_t *)&addr.s6_addr, netmask);
+#endif
+    }
+
     return SCRadixAddKey(addr.s6_addr, 128, tree, user, netmask);
 }
 
@@ -1271,6 +1348,9 @@ void SCRadixRemoveKeyGeneric(uint8_t *key_stream, uint16_t key_bitlen,
 void SCRadixRemoveKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
                                   uint8_t netmask)
 {
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+    SCRadixValidateIPv4Key(key_stream, netmask);
+#endif
     SCRadixRemoveKey(key_stream, 32, tree, netmask);
     return;
 }
@@ -1302,6 +1382,9 @@ void SCRadixRemoveKeyIPV4(uint8_t *key_stream, SCRadixTree *tree)
 void SCRadixRemoveKeyIPV6Netblock(uint8_t *key_stream, SCRadixTree *tree,
                                   uint8_t netmask)
 {
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+    SCRadixValidateIPv6Key(key_stream, netmask);
+#endif
     SCRadixRemoveKey(key_stream, 128, tree, netmask);
     return;
 }
@@ -1502,6 +1585,9 @@ SCRadixNode *SCRadixFindKeyIPV4BestMatch(uint8_t *key_stream, SCRadixTree *tree,
 SCRadixNode *SCRadixFindKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
                                         uint8_t netmask, void **user_data_result)
 {
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+    SCRadixValidateIPv4Key(key_stream, netmask);
+#endif
     SCRadixNode *node = SCRadixFindKey(key_stream, 32, netmask, tree, 1, user_data_result);
     return node;
 }
@@ -1516,6 +1602,9 @@ SCRadixNode *SCRadixFindKeyIPV4Netblock(uint8_t *key_stream, SCRadixTree *tree,
 SCRadixNode *SCRadixFindKeyIPV6Netblock(uint8_t *key_stream, SCRadixTree *tree,
                                         uint8_t netmask, void **user_data_result)
 {
+#if defined(DEBUG_VALIDATION) || defined(UNITTESTS)
+    SCRadixValidateIPv6Key(key_stream, netmask);
+#endif
     SCRadixNode *node = SCRadixFindKey(key_stream, 128, netmask, tree, 1, user_data_result);
     return node;
 }