]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/iponly: fix netmask handling
authorVictor Julien <vjulien@oisf.net>
Tue, 15 Feb 2022 19:43:27 +0000 (20:43 +0100)
committerVictor Julien <vjulien@oisf.net>
Thu, 17 Feb 2022 16:00:26 +0000 (17:00 +0100)
If the ipaddress was not the address range start, it was not masked to turn
it into that. So 1.2.3.4/24 was not stored as address 1.2.3.0 with netmask 24,
but as 1.2.3.4 with netmask 24. This was then propagated into the radix tree,
where it was used as an exact key in exact lookups, giving unexpected results.

This patch implements the netmask handling for IPv4 and IPv6, and adds a set
of tests for it.

Bug: #5081.
Bug: #5066.

src/detect-engine-address.c
src/detect-engine-iponly.c

index 8c8eb99fe724b45c71a1a9d81a8e1ab96ab8ffda..46c9b72387ec0f2f41519d3fc5a494a755b0397b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2021 Open Information Security Foundation
+/* Copyright (C) 2007-2022 Open Information Security Foundation
  *
  * You can copy, redistribute or modify this Program under the terms of
  * the GNU General Public License version 2 as published by the Free
@@ -2006,20 +2006,37 @@ static int AddressTestParse03(void)
 
 static int AddressTestParse04(void)
 {
-    int result = 1;
     DetectAddress *dd = DetectAddressParseSingle("1.2.3.4/255.255.255.0");
+    FAIL_IF_NULL(dd);
+
+    char left[16], right[16];
+    PrintInet(AF_INET, (const void *)&dd->ip.addr_data32[0], left, sizeof(left));
+    PrintInet(AF_INET, (const void *)&dd->ip2.addr_data32[0], right, sizeof(right));
+    SCLogDebug("left %s right %s", left, right);
+    FAIL_IF_NOT(dd->ip.addr_data32[0] == SCNtohl(16909056));
+    FAIL_IF_NOT(dd->ip2.addr_data32[0] == SCNtohl(16909311));
+    FAIL_IF_NOT(strcmp(left, "1.2.3.0") == 0);
+    FAIL_IF_NOT(strcmp(right, "1.2.3.255") == 0);
+
+    DetectAddressFree(dd);
+    PASS;
+}
 
-    if (dd) {
-        if (dd->ip.addr_data32[0] != SCNtohl(16909056)||
-            dd->ip2.addr_data32[0] != SCNtohl(16909311)) {
-            result = 0;
-        }
+/** \test that address range sets proper start address */
+static int AddressTestParse04bug5081(void)
+{
+    DetectAddress *dd = DetectAddressParseSingle("1.2.3.64/26");
+    FAIL_IF_NULL(dd);
 
-        DetectAddressFree(dd);
-        return result;
-    }
+    char left[16], right[16];
+    PrintInet(AF_INET, (const void *)&dd->ip.addr_data32[0], left, sizeof(left));
+    PrintInet(AF_INET, (const void *)&dd->ip2.addr_data32[0], right, sizeof(right));
+    SCLogDebug("left %s right %s", left, right);
+    FAIL_IF_NOT(strcmp(left, "1.2.3.64") == 0);
+    FAIL_IF_NOT(strcmp(right, "1.2.3.127") == 0);
 
-    return 0;
+    DetectAddressFree(dd);
+    PASS;
 }
 
 static int AddressTestParse05(void)
@@ -5040,6 +5057,7 @@ void DetectAddressTests(void)
     UtRegisterTest("AddressTestParse02", AddressTestParse02);
     UtRegisterTest("AddressTestParse03", AddressTestParse03);
     UtRegisterTest("AddressTestParse04", AddressTestParse04);
+    UtRegisterTest("AddressTestParse04bug5081", AddressTestParse04bug5081);
     UtRegisterTest("AddressTestParse05", AddressTestParse05);
     UtRegisterTest("AddressTestParse06", AddressTestParse06);
     UtRegisterTest("AddressTestParse07", AddressTestParse07);
index 53e1b3d2c33da3bcc47c67a518aa80b339321a7d..d66c4e115ff50718742a4648efbcabf51403c758 100644 (file)
@@ -56,6 +56,7 @@
 #include "util-byte.h"
 #include "util-profiling.h"
 #include "util-validate.h"
+#include "util-cidr.h"
 
 #ifdef OS_WIN32
 #include <winsock.h>
@@ -176,6 +177,7 @@ static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem **pdd, const char *str)
                     goto error;
 
                 dd->netmask = cidr;
+                netmask = CIDRGet(cidr);
             } else {
                 /* 1.2.3.4/255.255.255.0 format */
                 r = inet_pton(AF_INET, mask, &in);
@@ -183,12 +185,12 @@ static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem **pdd, const char *str)
                     goto error;
 
                 netmask = in.s_addr;
-
                 if (netmask != 0) {
+                    uint32_t m = netmask;
                     /* Extract cidr netmask */
-                    while ((0x01 & netmask) == 0) {
+                    while ((0x01 & m) == 0) {
                         dd->netmask++;
-                        netmask = netmask >> 1;
+                        m = m >> 1;
                     }
                     dd->netmask = 32 - dd->netmask;
                 }
@@ -198,7 +200,7 @@ static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem **pdd, const char *str)
             if (r <= 0)
                 goto error;
 
-            dd->ip[0] = in.s_addr;
+            dd->ip[0] = in.s_addr & netmask;
 
         } else if ((ip2 = strchr(ip, '-')) != NULL)  {
             /* 1.2.3.4-1.2.3.6 range format */
@@ -267,8 +269,8 @@ static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem **pdd, const char *str)
         }
     } else {
         /* IPv6 Address */
-        struct in6_addr in6;
-        uint32_t ip6addr[4];
+        struct in6_addr in6, mask6;
+        uint32_t ip6addr[4], netmask[4];
 
         dd->family = AF_INET6;
 
@@ -286,7 +288,14 @@ static int IPOnlyCIDRItemParseSingle(IPOnlyCIDRItem **pdd, const char *str)
                 goto error;
             }
 
-            memcpy(dd->ip, &in6.s6_addr, sizeof(ip6addr));
+            memcpy(&ip6addr, &in6.s6_addr, sizeof(ip6addr));
+            CIDRGetIPv6(dd->netmask, &mask6);
+            memcpy(&netmask, &mask6.s6_addr, sizeof(netmask));
+
+            dd->ip[0] = ip6addr[0] & netmask[0];
+            dd->ip[1] = ip6addr[1] & netmask[1];
+            dd->ip[2] = ip6addr[2] & netmask[2];
+            dd->ip[3] = ip6addr[3] & netmask[3];
         } else {
             r = inet_pton(AF_INET6, ip, &in6);
             if (r <= 0)
@@ -2312,6 +2321,97 @@ static int IPOnlyTestSig18(void)
     PASS;
 }
 
+/** \test build IP-only tree */
+static int IPOnlyTestBug5066v1(void)
+{
+    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+    FAIL_IF(de_ctx == NULL);
+    de_ctx->flags |= DE_QUIET;
+
+    Signature *s = DetectEngineAppendSig(
+            de_ctx, "alert ip [1.2.3.4/24,1.2.3.64/27] any -> any any (sid:1;)");
+    FAIL_IF_NULL(s);
+    s = DetectEngineAppendSig(de_ctx, "alert ip [1.2.3.4/24] any -> any any (sid:2;)");
+    FAIL_IF_NULL(s);
+
+    SigGroupBuild(de_ctx);
+
+    DetectEngineCtxFree(de_ctx);
+    PASS;
+}
+
+static int IPOnlyTestBug5066v2(void)
+{
+    IPOnlyCIDRItem *x = IPOnlyCIDRItemNew();
+    FAIL_IF_NULL(x);
+
+    FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "1.2.3.4/24") != 0);
+
+    char ip[16];
+    PrintInet(AF_INET, (const void *)&x->ip[0], ip, sizeof(ip));
+    SCLogDebug("ip %s netmask %d", ip, x->netmask);
+
+    FAIL_IF_NOT(strcmp(ip, "1.2.3.0") == 0);
+    FAIL_IF_NOT(x->netmask == 24);
+
+    IPOnlyCIDRListFree(x);
+    PASS;
+}
+
+static int IPOnlyTestBug5066v3(void)
+{
+    IPOnlyCIDRItem *x = IPOnlyCIDRItemNew();
+    FAIL_IF_NULL(x);
+
+    FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "1.2.3.64/26") != 0);
+
+    char ip[16];
+    PrintInet(AF_INET, (const void *)&x->ip[0], ip, sizeof(ip));
+    SCLogDebug("ip %s netmask %d", ip, x->netmask);
+
+    FAIL_IF_NOT(strcmp(ip, "1.2.3.64") == 0);
+    FAIL_IF_NOT(x->netmask == 26);
+
+    IPOnlyCIDRListFree(x);
+    PASS;
+}
+
+static int IPOnlyTestBug5066v4(void)
+{
+    IPOnlyCIDRItem *x = IPOnlyCIDRItemNew();
+    FAIL_IF_NULL(x);
+
+    FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "2000::1:1/122") != 0);
+
+    char ip[64];
+    PrintInet(AF_INET6, (const void *)&x->ip, ip, sizeof(ip));
+    SCLogDebug("ip %s netmask %d", ip, x->netmask);
+
+    FAIL_IF_NOT(strcmp(ip, "2000:0000:0000:0000:0000:0000:0001:0000") == 0);
+    FAIL_IF_NOT(x->netmask == 122);
+
+    IPOnlyCIDRListFree(x);
+    PASS;
+}
+
+static int IPOnlyTestBug5066v5(void)
+{
+    IPOnlyCIDRItem *x = IPOnlyCIDRItemNew();
+    FAIL_IF_NULL(x);
+
+    FAIL_IF(IPOnlyCIDRItemParseSingle(&x, "2000::1:40/122") != 0);
+
+    char ip[64];
+    PrintInet(AF_INET6, (const void *)&x->ip, ip, sizeof(ip));
+    SCLogDebug("ip %s netmask %d", ip, x->netmask);
+
+    FAIL_IF_NOT(strcmp(ip, "2000:0000:0000:0000:0000:0000:0001:0040") == 0);
+    FAIL_IF_NOT(x->netmask == 122);
+
+    IPOnlyCIDRListFree(x);
+    PASS;
+}
+
 #endif /* UNITTESTS */
 
 void IPOnlyRegisterTests(void)
@@ -2348,6 +2448,12 @@ void IPOnlyRegisterTests(void)
 
     UtRegisterTest("IPOnlyTestSig17", IPOnlyTestSig17);
     UtRegisterTest("IPOnlyTestSig18", IPOnlyTestSig18);
+
+    UtRegisterTest("IPOnlyTestBug5066v1", IPOnlyTestBug5066v1);
+    UtRegisterTest("IPOnlyTestBug5066v2", IPOnlyTestBug5066v2);
+    UtRegisterTest("IPOnlyTestBug5066v3", IPOnlyTestBug5066v3);
+    UtRegisterTest("IPOnlyTestBug5066v4", IPOnlyTestBug5066v4);
+    UtRegisterTest("IPOnlyTestBug5066v5", IPOnlyTestBug5066v5);
 #endif
 
     return;