]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect: bsize keyword
authorVictor Julien <victor@inliniac.net>
Sun, 10 Dec 2017 20:39:50 +0000 (21:39 +0100)
committerVictor Julien <victor@inliniac.net>
Wed, 14 Feb 2018 13:25:46 +0000 (14:25 +0100)
Allows matching on stickybuffers. Like dsize, it allows matching on
exact values, greater than and less than, and ranges.

For streaming buffers, such as HTTP bodies, the final size of the
body is only known at the end of the transaction.

src/Makefile.am
src/detect-bsize.c [new file with mode: 0644]
src/detect-bsize.h [new file with mode: 0644]
src/detect-engine-content-inspection.c
src/detect-engine-register.c
src/detect-engine-register.h
src/tests/detect-bsize.c [new file with mode: 0644]

index 010fe01ddb52b20c4918dff6626db0db4c8660cc..15f4488a1c7216f7d8c21c9a6df712505c31f10a 100644 (file)
@@ -85,6 +85,7 @@ detect-app-layer-protocol.c detect-app-layer-protocol.h \
 detect-asn1.c detect-asn1.h \
 detect-base64-data.c detect-base64-data.h \
 detect-base64-decode.c detect-base64-decode.h \
+detect-bsize.c detect-bsize.h \
 detect-byte-extract.c detect-byte-extract.h \
 detect-bytejump.c detect-bytejump.h \
 detect-bytetest.c detect-bytetest.h \
diff --git a/src/detect-bsize.c b/src/detect-bsize.c
new file mode 100644 (file)
index 0000000..fad18b1
--- /dev/null
@@ -0,0 +1,322 @@
+/* Copyright (C) 2017 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
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements the bsize generic buffer length keyword
+ */
+
+#include "suricata-common.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-content.h"
+
+#include "detect-bsize.h"
+
+#include "util-misc.h"
+
+/*prototypes*/
+static int DetectBsizeSetup (DetectEngineCtx *, Signature *, const char *);
+static void DetectBsizeFree (void *);
+#ifdef UNITTESTS
+static void DetectBsizeRegisterTests (void);
+#endif
+
+/**
+ * \brief Registration function for bsize: keyword
+ */
+
+void DetectBsizeRegister(void)
+{
+    sigmatch_table[DETECT_BSIZE].name = "bsize";
+    sigmatch_table[DETECT_BSIZE].desc = "match on the length of a buffer";
+    sigmatch_table[DETECT_BSIZE].url = DOC_URL DOC_VERSION "/rules/payload-keywords.html#bsize";
+    sigmatch_table[DETECT_BSIZE].Match = NULL;
+    sigmatch_table[DETECT_BSIZE].Setup = DetectBsizeSetup;
+    sigmatch_table[DETECT_BSIZE].Free = DetectBsizeFree;
+#ifdef UNITTESTS
+    sigmatch_table[DETECT_BSIZE].RegisterTests = DetectBsizeRegisterTests;
+#endif
+}
+
+#define DETECT_BSIZE_LT 0
+#define DETECT_BSIZE_GT 1
+#define DETECT_BSIZE_RA 2
+#define DETECT_BSIZE_EQ 3
+
+typedef struct DetectBsizeData {
+    uint8_t mode;
+    uint64_t lo;
+    uint64_t hi;
+} DetectBsizeData;
+
+/** \brief bsize match function
+ *
+ *  \param ctx match ctx
+ *  \param buffer_size size of the buffer
+ *  \param eof is the buffer closed?
+ *
+ *  \retval r 1 match, 0 no match, -1 can't match
+ *
+ *  \todo check logic around < vs <=
+ */
+int DetectBsizeMatch(const SigMatchCtx *ctx, const uint64_t buffer_size, bool eof)
+{
+    const DetectBsizeData *bsz = (const DetectBsizeData *)ctx;
+    switch (bsz->mode) {
+        case DETECT_BSIZE_LT:
+            if (buffer_size < bsz->lo) {
+                return 1;
+            }
+            return -1;
+
+        case DETECT_BSIZE_GT:
+            if (buffer_size > bsz->lo) {
+                return 1;
+            } else if (eof) {
+                return -1;
+            }
+            return 0;
+
+        case DETECT_BSIZE_EQ:
+            if (buffer_size == bsz->lo) {
+                return 1;
+            } else if (buffer_size > bsz->lo) {
+                return -1;
+            } else if (eof) {
+                return -1;
+            } else {
+                return 0;
+            }
+
+        case DETECT_BSIZE_RA:
+            if (buffer_size > bsz->lo && buffer_size < bsz->hi) {
+                return 1;
+            } else if (buffer_size <= bsz->lo && eof) {
+                return -1;
+            } else if (buffer_size <= bsz->lo) {
+                return 0;
+            } else if (buffer_size >= bsz->hi) {
+                return -1;
+            }
+    }
+    return 0;
+}
+
+#define ERR(...) do { \
+    char _buf[2048];              \
+    snprintf(_buf, sizeof(_buf), __VA_ARGS__);  \
+    SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, "bsize: bad input, %s", _buf); \
+} while(0)
+
+/**
+ * \brief This function is used to parse bsize options passed via bsize: keyword
+ *
+ * \param bsizestr Pointer to the user provided bsize options
+ *
+ * \retval bsized pointer to DetectBsizeData on success
+ * \retval NULL on failure
+ */
+
+static DetectBsizeData *DetectBsizeParse (const char *str)
+{
+    uint32_t lo = 0;
+    uint32_t hi = 0;
+
+    if (str == NULL)
+        return NULL;
+
+    size_t len = strlen(str);
+    if (len == 0)
+        return NULL;
+
+    /* allow for leading spaces */
+    while (isspace(*str))
+        (str++);
+    len = strlen(str);
+    if (len == 0)
+        return NULL;
+
+    int mode = DETECT_BSIZE_EQ;
+    switch (*str) {
+        case '>':
+            mode = DETECT_BSIZE_GT;
+            str++;
+            break;
+        case '<':
+            mode = DETECT_BSIZE_LT;
+            str++;
+            break;
+    }
+
+    /* allow for spaces between mode and value */
+    while (isspace(*str))
+        (str++);
+
+    char str1[11], *p = str1;
+    memset(str1, 0, sizeof(str1));
+    while (*str && isdigit(*str)) {
+        if (p - str1 >= (int)sizeof(str1))
+            return NULL;
+        *p++ = *str++;
+    }
+    /* skip trailing space */
+    while (*str && isspace(*str)) {
+        str++;
+    }
+    if (*str == '\0') {
+        // done
+        SCLogDebug("str1 '%s'", str1);
+
+        uint64_t val = 0;
+        if (ParseSizeStringU64(str1, &val) < 0) {
+            return NULL;
+        }
+        lo = val;
+
+    } else if (*str == '<') {
+        str++;
+        if (*str != '>') {
+            ERR("only '<>' allowed");
+            return NULL;
+        }
+        str++;
+
+        // range
+        if (mode != DETECT_BSIZE_EQ) {
+            ERR("mode already set");
+            return NULL;
+        }
+        mode = DETECT_BSIZE_RA;
+
+        uint64_t val = 0;
+        if (ParseSizeStringU64(str1, &val) < 0) {
+            return NULL;
+        }
+        lo = val;
+
+        /* allow for spaces between mode and value */
+        while (*str && isspace(*str))
+            (str++);
+
+        char str2[11];
+        p = str2;
+        memset(str2, 0, sizeof(str2));
+        while (*str && isdigit(*str)) {
+            if (p - str2 >= (int)sizeof(str2))
+                return NULL;
+            *p++ = *str++;
+        }
+        /* skip trailing space */
+        while (*str && isspace(*str)) {
+            str++;
+        }
+        if (*str == '\0') {
+            // done
+            SCLogDebug("str2 '%s'", str2);
+
+            if (ParseSizeStringU64(str2, &val) < 0) {
+                ERR("'%s' is not a valid u32", str2);
+                return NULL;
+            }
+            hi = val;
+            if (lo >= hi) {
+                ERR("%u > %u", lo, hi);
+                return NULL;
+            }
+
+        } else {
+            ERR("trailing data");
+            return NULL;
+        }
+
+    } else {
+        ERR("'%s'", str);
+        return NULL;
+    }
+
+    DetectBsizeData *bsz = SCCalloc(1, sizeof(*bsz));
+    if (bsz == NULL) {
+        return NULL;
+    }
+    bsz->mode = (uint8_t)mode;
+    bsz->lo = lo;
+    bsz->hi = hi;
+    return bsz;
+}
+
+/**
+ * \brief this function is used to parse bsize data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param bsizestr pointer to the user provided bsize options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectBsizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *sizestr)
+{
+    SCEnter();
+    SigMatch *sm = NULL;
+
+    int list = s->init_data->list;
+    if (list == DETECT_SM_LIST_NOTSET)
+        SCReturnInt(-1);
+
+    DetectBsizeData *bsz = DetectBsizeParse(sizestr);
+    if (bsz == NULL)
+        goto error;
+    sm = SigMatchAlloc();
+    if (sm == NULL)
+        goto error;
+    sm->type = DETECT_BSIZE;
+    sm->ctx = (void *)bsz;
+
+    SigMatchAppendSMToList(s, sm, list);
+
+    SCReturnInt(0);
+
+error:
+    DetectBsizeFree(bsz);
+    SCReturnInt(-1);
+}
+
+/**
+ * \brief this function will free memory associated with DetectBsizeData
+ *
+ * \param ptr pointer to DetectBsizeData
+ */
+void DetectBsizeFree(void *ptr)
+{
+    if (ptr == NULL)
+        return;
+
+    DetectBsizeData *bsz = (DetectBsizeData *)ptr;
+    SCFree(bsz);
+}
+
+#ifdef UNITTESTS
+#include "tests/detect-bsize.c"
+#endif
diff --git a/src/detect-bsize.h b/src/detect-bsize.h
new file mode 100644 (file)
index 0000000..6851abc
--- /dev/null
@@ -0,0 +1,32 @@
+/* Copyright (C) 2017 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
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __DETECT_BSIZE_H__
+#define        __DETECT_BSIZE_H__
+
+//bool DetectBsizeValidateContent(const Signature *s, int list, const char **);
+//void DetectBsizeApplyToContent(Signature *s, int list);
+void DetectBsizeRegister(void);
+int DetectBsizeMatch(const SigMatchCtx *ctx, const uint64_t buffer_size, bool eof);
+
+#endif /* __DETECT_URILEN_H__ */
index 3e4471c9d7a9c3cccbc8cf257f0937ae70ddc22e..baafb9072a618e31601fcccf52909437e927ff57 100644 (file)
@@ -41,6 +41,7 @@
 #include "detect-engine-content-inspection.h"
 #include "detect-uricontent.h"
 #include "detect-urilen.h"
+#include "detect-bsize.h"
 #include "detect-lua.h"
 #include "detect-base64-decode.h"
 #include "detect-base64-data.h"
@@ -530,7 +531,20 @@ int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx
 
         goto match;
 
-        /* we should never get here, but bail out just in case */
+    } else if (smd->type == DETECT_BSIZE) {
+
+        bool eof = (flags & DETECT_CI_FLAGS_END);
+        const uint64_t data_size = buffer_len + stream_start_offset;
+        int r = DetectBsizeMatch(smd->ctx, data_size, eof);
+        if (r < 0) {
+            det_ctx->discontinue_matching = 1;
+            goto no_match;
+
+        } else if (r == 0) {
+            goto no_match;
+        }
+        goto match;
+
     } else if (smd->type == DETECT_AL_URILEN) {
         SCLogDebug("inspecting uri len");
 
index 54f421709a1d0ccd7a0ebc8e15a4f2df6eab9282..81c37405aeae654df2a102ea21bd1c5b64c939a0 100644 (file)
 #include "detect-dce-opnum.h"
 #include "detect-dce-stub-data.h"
 #include "detect-urilen.h"
+#include "detect-bsize.h"
 #include "detect-detection-filter.h"
 #include "detect-http-client-body.h"
 #include "detect-http-server-body.h"
@@ -462,6 +463,7 @@ void SigTableSetup(void)
     DetectNfsProcedureRegister();
     DetectNfsVersionRegister();
     DetectUrilenRegister();
+    DetectBsizeRegister();
     DetectDetectionFilterRegister();
     DetectAsn1Register();
     DetectSshProtocolRegister();
index 17d03102fd38deadbf515c484f4c121b38b1faa7..a5761a501beef4adffe0b92557ca0409975b9d57 100644 (file)
@@ -99,6 +99,8 @@ enum {
     DETECT_GID,
     DETECT_MARK,
 
+    DETECT_BSIZE,
+
     DETECT_AL_TLS_VERSION,
     DETECT_AL_TLS_SUBJECT,
     DETECT_AL_TLS_ISSUERDN,
diff --git a/src/tests/detect-bsize.c b/src/tests/detect-bsize.c
new file mode 100644 (file)
index 0000000..221ea52
--- /dev/null
@@ -0,0 +1,134 @@
+/* Copyright (C) 2017 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.
+ */
+
+#include "../util-unittest.h"
+
+#define TEST_OK(str, m, lo, hi) {                       \
+    DetectBsizeData *bsz = DetectBsizeParse((str));     \
+    FAIL_IF_NULL(bsz);                                  \
+    FAIL_IF_NOT(bsz->mode == (m));                      \
+    DetectBsizeFree(bsz);                               \
+    SCLogDebug("str %s OK", (str));                     \
+}
+#define TEST_FAIL(str) {                                \
+    DetectBsizeData *bsz = DetectBsizeParse((str));     \
+    FAIL_IF_NOT_NULL(bsz);                              \
+}
+
+static int DetectBsizeTest01(void)
+{
+    TEST_OK("50", DETECT_BSIZE_EQ, 50, 0);
+    TEST_OK(" 50", DETECT_BSIZE_EQ, 50, 0);
+    TEST_OK("  50", DETECT_BSIZE_EQ, 50, 0);
+    TEST_OK("  50 ", DETECT_BSIZE_EQ, 50, 0);
+    TEST_OK("  50  ", DETECT_BSIZE_EQ, 50, 0);
+
+    TEST_FAIL("AA");
+    TEST_FAIL("5A");
+    TEST_FAIL("A5");
+    PASS;
+}
+
+static int DetectBsizeTest02(void)
+{
+    TEST_OK(">50", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK("> 50", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(">  50", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(" >50", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(" > 50", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(" >  50", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(" >50 ", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(" > 50  ", DETECT_BSIZE_GT, 50, 0);
+    TEST_OK(" >  50   ", DETECT_BSIZE_GT, 50, 0);
+
+    TEST_FAIL(">>50");
+    TEST_FAIL("<>50");
+    TEST_FAIL(" > 50A");
+    PASS;
+}
+
+static int DetectBsizeTest03(void)
+{
+    TEST_OK("<50", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK("< 50", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK("<  50", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK(" <50", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK(" < 50", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK(" <  50", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK(" <50 ", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK(" < 50  ", DETECT_BSIZE_LT, 50, 0);
+    TEST_OK(" <  50   ", DETECT_BSIZE_LT, 50, 0);
+
+    TEST_FAIL(">>50");
+    TEST_FAIL(" < 50A");
+    PASS;
+}
+
+static int DetectBsizeTest04(void)
+{
+    TEST_OK("50<>100", DETECT_BSIZE_RA, 50, 100);
+
+    TEST_FAIL("50<$50");
+    TEST_FAIL("100<>50");
+    TEST_FAIL(">50<>100");
+    PASS;
+}
+
+#undef TEST_OK
+#undef TEST_FAIL
+
+#define TEST_OK(rule)                                                                       \
+{                                                                                           \
+    DetectEngineCtx *de_ctx = DetectEngineCtxInit();                                        \
+    FAIL_IF_NULL(de_ctx);                                                                   \
+    Signature *s = DetectEngineAppendSig(de_ctx, (rule));                                   \
+    FAIL_IF_NULL(s);                                                                        \
+    DetectEngineCtxFree(de_ctx);                                                            \
+}
+
+#define TEST_FAIL(rule)                                                                     \
+{                                                                                           \
+    DetectEngineCtx *de_ctx = DetectEngineCtxInit();                                        \
+    FAIL_IF_NULL(de_ctx);                                                                   \
+    Signature *s = DetectEngineAppendSig(de_ctx, (rule));                                   \
+    FAIL_IF_NOT_NULL(s);                                                                    \
+    DetectEngineCtxFree(de_ctx);                                                            \
+}
+
+static int DetectBsizeSigTest01(void)
+{
+    TEST_OK("alert http any any -> any any (http_request_line; bsize:10; sid:1;)");
+    TEST_OK("alert http any any -> any any (file_data; bsize:>1000; sid:2;)");
+
+    TEST_FAIL("alert tcp any any -> any any (content:\"abc\"; bsize:10; sid:3;)");
+    TEST_FAIL("alert http any any -> any any (content:\"GET\"; http_method; bsize:10; sid:4;)");
+    TEST_FAIL("alert http any any -> any any (http_request_line; content:\"GET\"; bsize:<10>; sid:5;)");
+    PASS;
+}
+
+#undef TEST_OK
+#undef TEST_FAIL
+
+static void DetectBsizeRegisterTests(void)
+{
+    UtRegisterTest("DetectBsizeTest01 EQ", DetectBsizeTest01);
+    UtRegisterTest("DetectBsizeTest02 GT", DetectBsizeTest02);
+    UtRegisterTest("DetectBsizeTest03 LT", DetectBsizeTest03);
+    UtRegisterTest("DetectBsizeTest04 RA", DetectBsizeTest04);
+
+    UtRegisterTest("DetectBsizeSigTest01", DetectBsizeSigTest01);
+}