]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
decode/flow/esp: Add ESP decoder & flow 5743/head
authorEmmanuel Thompson <eet6646@gmail.com>
Mon, 4 May 2020 15:38:16 +0000 (11:38 -0400)
committerVictor Julien <victor@inliniac.net>
Mon, 18 Jan 2021 20:04:31 +0000 (21:04 +0100)
- Adds an ESP (Encapsulating Security Payload) header decoder
- Tracks ESP flows via the SPI field

15 files changed:
src/Makefile.am
src/decode-esp.c [new file with mode: 0644]
src/decode-esp.h [new file with mode: 0644]
src/decode-events.c
src/decode-events.h
src/decode-ipv4.c
src/decode-ipv6.c
src/decode.c
src/decode.h
src/flow-hash.c
src/flow-util.c
src/flow.h
src/output-json-flow.c
src/output-json-netflow.c
src/runmode-unittests.c

index 825ce65d0dc0778ded39cf3d691071c36995dd0c..137f3101082e9801e0c584e80d5eac09c39cae10 100755 (executable)
@@ -81,6 +81,7 @@ decode-ppp.c decode-ppp.h \
 decode-pppoe.c decode-pppoe.h \
 decode-raw.c decode-raw.h \
 decode-sctp.c decode-sctp.h \
+decode-esp.c decode-esp.h \
 decode-sll.c decode-sll.h \
 decode-tcp.c decode-tcp.h \
 decode-teredo.c decode-teredo.h \
diff --git a/src/decode-esp.c b/src/decode-esp.c
new file mode 100644 (file)
index 0000000..575035e
--- /dev/null
@@ -0,0 +1,198 @@
+/* Copyright (C) 2020 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
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \ingroup decode
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * Decode Encapsulating Security Payload (ESP)
+ */
+
+#include "suricata-common.h"
+#include "decode-esp.h"
+#include "flow.h"
+
+static int DecodeESPPacket(ThreadVars *tv, Packet *p, const uint8_t *pkt, uint16_t len)
+{
+    if (unlikely(len < ESP_HEADER_LEN)) {
+        ENGINE_SET_INVALID_EVENT(p, ESP_PKT_TOO_SMALL);
+        return -1;
+    }
+
+    p->esph = (ESPHdr *)pkt;
+
+    p->payload = (uint8_t *)pkt + sizeof(ESPHdr);
+    p->payload_len = len - sizeof(ESPHdr);
+
+    p->proto = IPPROTO_ESP;
+
+    return 0;
+}
+
+/**
+ * \brief Function to decode IPSEC-ESP packets
+ * \param tv thread vars
+ * \param dtv decoder thread vars
+ * \param p packet
+ * \param pkt raw packet data
+ * \param len length in bytes of pkt array
+ * \retval TM_ECODE_OK or TM_ECODE_FAILED on serious error
+ */
+int DecodeESP(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, const uint8_t *pkt, uint16_t len)
+{
+    StatsIncr(tv, dtv->counter_esp);
+
+    if (unlikely(DecodeESPPacket(tv, p, pkt, len) < 0)) {
+        CLEAR_ESP_PACKET(p);
+        return TM_ECODE_FAILED;
+    }
+
+    SCLogDebug("ESP spi: %" PRIu32 " sequence: %" PRIu32, ESP_GET_SPI(p), ESP_GET_SEQUENCE(p));
+
+    FlowSetupPacket(p);
+
+    return TM_ECODE_OK;
+}
+
+#ifdef UNITTESTS
+
+#include "util-unittest.h"
+
+/** \test Successful decoding */
+static int DecodeESPTest01(void)
+{
+    uint8_t raw_esp[] = { 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x08 };
+
+    Packet *p = PacketGetFromAlloc();
+    FAIL_IF_NULL(p);
+
+    ThreadVars tv;
+    DecodeThreadVars dtv;
+
+    memset(&tv, 0, sizeof(ThreadVars));
+    memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+    int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
+    FAIL_IF(ret != TM_ECODE_OK);
+
+    FAIL_IF(p->proto != IPPROTO_ESP);
+    FAIL_IF(p->payload_len != sizeof(raw_esp) - ESP_HEADER_LEN);
+    FAIL_IF(ESP_GET_SPI(p) != 0x7b);
+    FAIL_IF(ESP_GET_SEQUENCE(p) != 0x08);
+
+    SCFree(p);
+
+    PASS;
+}
+
+/** \test Successful decoding, with payload data */
+static int DecodeESPTest02(void)
+{
+    uint8_t raw_esp[] = { 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x08, 0xFF, 0xFF };
+
+    Packet *p = PacketGetFromAlloc();
+    FAIL_IF_NULL(p);
+
+    ThreadVars tv;
+    DecodeThreadVars dtv;
+
+    memset(&tv, 0, sizeof(ThreadVars));
+    memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+    int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
+    FAIL_IF(ret != TM_ECODE_OK);
+
+    FAIL_IF(p->proto != IPPROTO_ESP);
+    FAIL_IF(p->payload_len != sizeof(raw_esp) - ESP_HEADER_LEN);
+    FAIL_IF(memcmp(p->payload, raw_esp + ESP_HEADER_LEN, p->payload_len) != 0);
+    FAIL_IF(ESP_GET_SPI(p) != 0x7b);
+    FAIL_IF(ESP_GET_SEQUENCE(p) != 0x08);
+
+    SCFree(p);
+
+    PASS;
+}
+
+/** \test Failure decoding, not enough data */
+static int DecodeESPTest03(void)
+{
+    uint8_t raw_esp[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+    Packet *p = PacketGetFromAlloc();
+    FAIL_IF_NULL(p);
+
+    ThreadVars tv;
+    DecodeThreadVars dtv;
+
+    memset(&tv, 0, sizeof(ThreadVars));
+    memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+    int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
+    FAIL_IF(ret != TM_ECODE_FAILED);
+
+    // expect ESP_PKT_TOO_SMALL
+    FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, ESP_PKT_TOO_SMALL));
+
+    SCFree(p);
+
+    PASS;
+}
+
+/** \test Failure decoding, no data */
+static int DecodeESPTest04(void)
+{
+    uint8_t raw_esp[] = {};
+
+    Packet *p = PacketGetFromAlloc();
+    FAIL_IF_NULL(p);
+
+    ThreadVars tv;
+    DecodeThreadVars dtv;
+
+    memset(&tv, 0, sizeof(ThreadVars));
+    memset(&dtv, 0, sizeof(DecodeThreadVars));
+
+    int ret = DecodeESP(&tv, &dtv, p, raw_esp, sizeof(raw_esp));
+    FAIL_IF(ret != TM_ECODE_FAILED);
+
+    // expect ESP_PKT_TOO_SMALL
+    FAIL_IF_NOT(ENGINE_ISSET_EVENT(p, ESP_PKT_TOO_SMALL));
+
+    SCFree(p);
+
+    PASS;
+}
+#endif /* UNITTESTS */
+
+void DecodeESPRegisterTests(void)
+{
+#ifdef UNITTESTS
+    UtRegisterTest("DecodeESPTest01", DecodeESPTest01);
+    UtRegisterTest("DecodeESPTest02", DecodeESPTest02);
+    UtRegisterTest("DecodeESPTest03", DecodeESPTest03);
+    UtRegisterTest("DecodeESPTest04", DecodeESPTest04);
+#endif /* UNITTESTS */
+}
+
+/**
+ * @}
+ */
diff --git a/src/decode-esp.h b/src/decode-esp.h
new file mode 100644 (file)
index 0000000..432de27
--- /dev/null
@@ -0,0 +1,51 @@
+/* Copyright (C) 2020 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
+ * Software Foundation.
+ *
+ * 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
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ */
+
+#ifndef __DECODE_ESP_H__
+#define __DECODE_ESP_H__
+
+/** \brief size of the ESP header */
+#define ESP_HEADER_LEN 8
+
+#define ESP_GET_RAW_SPI(esph)      SCNtohl((esph)->spi)
+#define ESP_GET_RAW_SEQUENCE(esph) SCNtohl((esph)->sequence)
+
+/** \brief Get the spi field off a packet */
+#define ESP_GET_SPI(p) ESP_GET_RAW_SPI(p->esph)
+
+/** \brief Get the sequence field off a packet */
+#define ESP_GET_SEQUENCE(p) ESP_GET_RAW_SEQUENCE(p->esph)
+
+/** \brief ESP Header */
+typedef struct ESPHdr_ {
+    uint32_t spi;      /** < ESP Security Parameters Index */
+    uint32_t sequence; /** < ESP sequence number */
+} __attribute__((__packed__)) ESPHdr;
+
+#define CLEAR_ESP_PACKET(p)                                                                        \
+    {                                                                                              \
+        (p)->esph = NULL;                                                                          \
+    }                                                                                              \
+    while (0)
+
+void DecodeESPRegisterTests(void);
+
+#endif /* __DECODE_ESP_H__ */
index 2f2373cd0b2d365b1151f55f78cd2120bd0a78b4..ee8fa24b75e3243e4b9be232a4c34d0fe7f69ac3 100644 (file)
@@ -425,6 +425,12 @@ const struct DecodeEvents_ DEvents[] = {
             SCTP_PKT_TOO_SMALL,
     },
 
+    /* ESP EVENTS */
+    {
+            "decoder.esp.pkt_too_small",
+            ESP_PKT_TOO_SMALL,
+    },
+
     /* Fragmentation reasembly events. */
     {
             "decoder.ipv4.frag_pkt_too_large",
index 2c8467f1cd429f0ecf1ec4d18cc4ae0f44bbbb18..c34ae80ac4b7cbd47d9ee3c861f5f15f99456aa6 100644 (file)
@@ -157,6 +157,9 @@ enum {
     /* SCTP EVENTS */
     SCTP_PKT_TOO_SMALL, /**< sctp packet smaller than minimum size */
 
+    /* ESP EVENTS */
+    ESP_PKT_TOO_SMALL, /**< esp packet smaller than minimum size */
+
     /* Fragmentation reasembly events. */
     IPV4_FRAG_PKT_TOO_LARGE,
     IPV6_FRAG_PKT_TOO_LARGE,
index 5d9d12a8b93e31a7ed0c82592a12be9975b33588..60bb9e63d9508f7c21127c22a270cd7d4a2a3d84 100644 (file)
@@ -575,6 +575,11 @@ int DecodeIPV4(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
             DecodeSCTP(tv, dtv, p, pkt + IPV4_GET_HLEN(p),
                       IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p));
             break;
+
+        case IPPROTO_ESP:
+            DecodeESP(tv, dtv, p, pkt + IPV4_GET_HLEN(p), IPV4_GET_IPLEN(p) - IPV4_GET_HLEN(p));
+            break;
+
         case IPPROTO_IPV6:
             {
                 /* spawn off tunnel packet */
index b20440145ac74ebd8985dbed15ef38850e08b189..05ef92d1bde92af9d49d36bf63d0209e414da766 100644 (file)
@@ -151,7 +151,6 @@ DecodeIPV6ExtHdrs(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
     char exthdr_fh_done = 0;
     int hh = 0;
     int rh = 0;
-    int eh = 0;
     int ah = 0;
 
     while(1)
@@ -462,23 +461,8 @@ DecodeIPV6ExtHdrs(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p,
             case IPPROTO_ESP:
             {
                 IPV6_SET_L4PROTO(p,nh);
-                hdrextlen = sizeof(IPV6EspHdr);
-                if (hdrextlen > plen) {
-                    ENGINE_SET_INVALID_EVENT(p, IPV6_TRUNC_EXTHDR);
-                    SCReturn;
-                }
-
-                if (eh) {
-                    ENGINE_SET_EVENT(p, IPV6_EXTHDR_DUPL_EH);
-                    SCReturn;
-                }
-
-                eh = 1;
-
-                nh = IPPROTO_NONE;
-                pkt += hdrextlen;
-                plen -= hdrextlen;
-                break;
+                DecodeESP(tv, dtv, p, pkt, plen);
+                SCReturn;
             }
             case IPPROTO_AH:
             {
index 93aec6338cbe76f3a41a696cb117dbc9da6b570f..384a7d94c2b73e1a09f29f475e7bb80acb12dc7b 100644 (file)
@@ -500,6 +500,7 @@ void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
     dtv->counter_tcp = StatsRegisterCounter("decoder.tcp", tv);
     dtv->counter_udp = StatsRegisterCounter("decoder.udp", tv);
     dtv->counter_sctp = StatsRegisterCounter("decoder.sctp", tv);
+    dtv->counter_esp = StatsRegisterCounter("decoder.esp", tv);
     dtv->counter_icmpv4 = StatsRegisterCounter("decoder.icmpv4", tv);
     dtv->counter_icmpv6 = StatsRegisterCounter("decoder.icmpv6", tv);
     dtv->counter_ppp = StatsRegisterCounter("decoder.ppp", tv);
index 0ff20ff1fc2eb2a697f3cd29d6322038b9742618..ea2613ecb72fb335d3ddf65ceb5bd2062e2400e3 100644 (file)
@@ -90,6 +90,7 @@ enum PktSrcEnum {
 #include "decode-tcp.h"
 #include "decode-udp.h"
 #include "decode-sctp.h"
+#include "decode-esp.h"
 #include "decode-raw.h"
 #include "decode-null.h"
 #include "decode-vlan.h"
@@ -215,7 +216,6 @@ typedef struct Address_ {
     } while (0)
 
 
-
 #define GET_IPV4_SRC_ADDR_U32(p) ((p)->src.addr_data32[0])
 #define GET_IPV4_DST_ADDR_U32(p) ((p)->dst.addr_data32[0])
 #define GET_IPV4_SRC_ADDR_PTR(p) ((p)->src.addr_data32)
@@ -535,6 +535,8 @@ typedef struct Packet_
 
     SCTPHdr *sctph;
 
+    ESPHdr *esph;
+
     ICMPV4Hdr *icmpv4h;
 
     ICMPV6Hdr *icmpv6h;
@@ -656,6 +658,7 @@ typedef struct DecodeThreadVars_
     uint16_t counter_raw;
     uint16_t counter_null;
     uint16_t counter_sctp;
+    uint16_t counter_esp;
     uint16_t counter_ppp;
     uint16_t counter_geneve;
     uint16_t counter_gre;
@@ -797,6 +800,9 @@ void CaptureStatsSetup(ThreadVars *tv, CaptureStats *s);
         if ((p)->sctph != NULL) {                                                                  \
             CLEAR_SCTP_PACKET((p));                                                                \
         }                                                                                          \
+        if ((p)->esph != NULL) {                                                                   \
+            CLEAR_ESP_PACKET((p));                                                                 \
+        }                                                                                          \
         if ((p)->icmpv4h != NULL) {                                                                \
             CLEAR_ICMPV4_PACKET((p));                                                              \
         }                                                                                          \
@@ -956,6 +962,7 @@ int DecodeICMPV6(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, ui
 int DecodeTCP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
 int DecodeUDP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
 int DecodeSCTP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
+int DecodeESP(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint16_t);
 int DecodeGRE(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);
 int DecodeVLAN(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);
 int DecodeIEEE8021ah(ThreadVars *, DecodeThreadVars *, Packet *, const uint8_t *, uint32_t);
index ebbd836e81a0f5216378890a5977844bc19f97ef..c2a22a15ea1c4cfe99fe25c06afed8aefd52c275 100644 (file)
@@ -407,6 +407,27 @@ static inline int FlowCompareICMPv4(Flow *f, const Packet *p)
     return 0;
 }
 
+/**
+ *  \brief See if a IP-ESP packet belongs to a flow by comparing the SPI
+ *
+ *  \param f flow
+ *  \param p ESP packet
+ *
+ *  \retval 1 match
+ *  \retval 0 no match
+ */
+static inline int FlowCompareESP(Flow *f, const Packet *p)
+{
+    const uint32_t *f_src = f->src.address.address_un_data32;
+    const uint32_t *f_dst = f->dst.address.address_un_data32;
+    const uint32_t *p_src = p->src.address.address_un_data32;
+    const uint32_t *p_dst = p->dst.address.address_un_data32;
+
+    return CmpAddrs(f_src, p_src) && CmpAddrs(f_dst, p_dst) && f->proto == p->proto &&
+           f->recursion_level == p->recursion_level && CmpVlanIds(f->vlan_id, p->vlan_id) &&
+           f->esp.spi == ESP_GET_SPI(p);
+}
+
 void FlowSetupPacket(Packet *p)
 {
     p->flags |= PKT_WANTS_FLOW;
@@ -417,6 +438,8 @@ static inline int FlowCompare(Flow *f, const Packet *p)
 {
     if (p->proto == IPPROTO_ICMP) {
         return FlowCompareICMPv4(f, p);
+    } else if (p->proto == IPPROTO_ESP) {
+        return FlowCompareESP(f, p);
     } else {
         return CmpFlowPacket(f, p);
     }
index b16f7c26e400692495dfceb99612598aa182ac4d..a47069a38bd57cf1b9fe3f77bd969266f096e418 100644 (file)
@@ -187,6 +187,8 @@ void FlowInit(Flow *f, const Packet *p)
     } else if (p->sctph != NULL) { /* XXX MACRO */
         SET_SCTP_SRC_PORT(p,&f->sp);
         SET_SCTP_DST_PORT(p,&f->dp);
+    } else if (p->esph != NULL) {
+        f->esp.spi = ESP_GET_SPI(p);
     } /* XXX handle default */
 #ifdef DEBUG
     else {
index 198a899b4e3a728583d139792ea1a62e5e213b6b..1ad3bdb99fa505b70ede3cbe448d7de4cc791a27 100644 (file)
@@ -354,6 +354,10 @@ typedef struct Flow_
             uint8_t type;   /**< icmp type */
             uint8_t code;   /**< icmp code */
         } icmp_s;
+
+        struct {
+            uint32_t spi; /**< esp spi */
+        } esp;
     };
     union {
         Port dp;        /**< tcp/udp destination port */
index 1a0b823334e54f2a0cd5aba55db23b9e98b6e325..3921bc3dda796fdb41c17feb7dc6e5abcf9fa1cb 100644 (file)
@@ -171,6 +171,9 @@ static JsonBuilder *CreateEveHeaderFromFlow(const Flow *f)
                 jb_set_uint(jb, "response_icmp_code", f->icmp_d.code);
             }
             break;
+        case IPPROTO_ESP:
+            jb_set_uint(jb, "spi", f->esp.spi);
+            break;
     }
     return jb;
 }
index 5700327224e13ef6d6f2fec5cad269225d3ccb32..1e986eadcef38ecb2b81fd7a7d0afb08564260d4 100644 (file)
@@ -179,6 +179,9 @@ static JsonBuilder *CreateEveHeaderFromNetFlow(const Flow *f, int dir)
             jb_set_uint(js, "icmp_code", code);
             break;
         }
+        case IPPROTO_ESP:
+            jb_set_uint(js, "spi", f->esp.spi);
+            break;
     }
     return js;
 }
index 6c6111a3b8b65a913a8026cdb0d3fa37a0b3097c..8aae154e3023a25e44e062cd2da5febf72042d57 100644 (file)
@@ -153,6 +153,7 @@ static void RegisterUnittests(void)
     DecodeTCPRegisterTests();
     DecodeUDPV4RegisterTests();
     DecodeGRERegisterTests();
+    DecodeESPRegisterTests();
     DecodeMPLSRegisterTests();
     DecodeNSHRegisterTests();
     AppLayerProtoDetectUnittestsRegister();
@@ -292,4 +293,3 @@ void RunUnittests(int list_unittests, const char *regex_arg)
     FatalError(SC_ERR_FATAL, "Unittests are not build-in");
 #endif /* UNITTESTS */
 }
-