]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
base64_decode, base64_data: decode and match base64 1758/head
authorJason Ish <ish@unx.ca>
Wed, 30 Sep 2015 15:49:47 +0000 (09:49 -0600)
committerVictor Julien <victor@inliniac.net>
Mon, 23 Nov 2015 11:13:40 +0000 (12:13 +0100)
13 files changed:
src/Makefile.am
src/detect-base64-data.c [new file with mode: 0644]
src/detect-base64-data.h [new file with mode: 0644]
src/detect-base64-decode.c [new file with mode: 0644]
src/detect-base64-decode.h [new file with mode: 0644]
src/detect-engine-content-inspection.c
src/detect-engine-content-inspection.h
src/detect-engine.c
src/detect-parse.c
src/detect.c
src/detect.h
src/util-error.c
src/util-error.h

index 98e094c19afb1ce476d1915efba1db53e226d293..4af253a8965da73be1123d79d73de61e632d7472 100644 (file)
@@ -71,6 +71,8 @@ detect-ack.c detect-ack.h \
 detect-app-layer-event.c detect-app-layer-event.h \
 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-byte-extract.c detect-byte-extract.h \
 detect-bytejump.c detect-bytejump.h \
 detect-bytetest.c detect-bytetest.h \
diff --git a/src/detect-base64-data.c b/src/detect-base64-data.c
new file mode 100644 (file)
index 0000000..b056b97
--- /dev/null
@@ -0,0 +1,236 @@
+/* Copyright (C) 2015 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 "suricata-common.h"
+#include "detect.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-parse.h"
+
+#include "util-unittest.h"
+
+static int DetectBase64DataSetup(DetectEngineCtx *, Signature *, char *);
+static void DetectBase64DataRegisterTests(void);
+
+void DetectBase64DataRegister(void)
+{
+    sigmatch_table[DETECT_BASE64_DATA].name = "base64_data";
+    sigmatch_table[DETECT_BASE64_DATA].desc =
+        "Content match base64 decoded data.";
+    sigmatch_table[DETECT_BASE64_DATA].url =
+        "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#base64_data";
+    sigmatch_table[DETECT_BASE64_DATA].Setup = DetectBase64DataSetup;
+    sigmatch_table[DETECT_BASE64_DATA].RegisterTests =
+        DetectBase64DataRegisterTests;
+
+    sigmatch_table[DETECT_BASE64_DATA].flags |= SIGMATCH_NOOPT;
+}
+
+static int DetectBase64DataSetup(DetectEngineCtx *de_ctx, Signature *s,
+    char *str)
+{
+    SigMatch *pm = NULL;
+
+    /* Check for a preceding base64_decode. */
+    pm = SigMatchGetLastSMFromLists(s, 28,
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+        DETECT_BASE64_DECODE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+    if (pm == NULL) {
+        SCLogError(SC_ERR_INVALID_SIGNATURE,
+            "\"base64_data\" keyword seen without preceding base64_decode.");
+        return -1;
+    }
+
+    s->list = DETECT_SM_LIST_BASE64_DATA;
+    return 0;
+}
+
+int DetectBase64DataDoMatch(DetectEngineCtx *de_ctx,
+    DetectEngineThreadCtx *det_ctx, Signature *s, Flow *f)
+{
+    if (det_ctx->base64_decoded_len) {
+        return DetectEngineContentInspection(de_ctx, det_ctx, s,
+            s->sm_lists[DETECT_SM_LIST_BASE64_DATA], f, det_ctx->base64_decoded,
+            det_ctx->base64_decoded_len, 0,
+            DETECT_ENGINE_CONTENT_INSPECTION_MODE_BASE64, NULL);
+    }
+
+    return 0;
+}
+
+#ifdef UNITTESTS
+
+#include "detect-engine.h"
+
+static int DetectBase64DataSetupTest01(void)
+{
+    DetectEngineCtx *de_ctx = NULL;
+    SigMatch *sm;
+    int retval = 0;
+
+    de_ctx = DetectEngineCtxInit();
+    if (de_ctx == NULL) {
+        goto end;
+    }
+
+    de_ctx->flags |= DE_QUIET;
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert smtp any any -> any any (msg:\"DetectBase64DataSetupTest\"; "
+        "base64_decode; base64_data; content:\"content\"; sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        printf("SigInit failed: ");
+        goto end;
+    }
+
+    sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+    if (sm == NULL) {
+        printf("DETECT_SM_LIST_PMATCH should not be NULL: ");
+        goto end;
+    }
+    if (sm->type != DETECT_BASE64_DECODE) {
+        printf("sm->type should be DETECT_BASE64_DECODE: ");
+        goto end;
+    }
+
+    if (de_ctx->sig_list->sm_lists[DETECT_SM_LIST_BASE64_DATA] == NULL) {
+        printf("DETECT_SM_LIST_BASE64_DATA should not be NULL: ");
+       goto end;
+    }
+
+    retval = 1;
+end:
+    if (de_ctx != NULL) {
+        SigGroupCleanup(de_ctx);
+        SigCleanSignatures(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    return retval;
+}
+
+static int DetectBase64DataSetupTest02(void)
+{
+    DetectEngineCtx *de_ctx = NULL;
+    SigMatch *sm;
+    int retval = 0;
+
+    de_ctx = DetectEngineCtxInit();
+    if (de_ctx == NULL) {
+        goto end;
+    }
+
+    de_ctx->flags |= DE_QUIET;
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert smtp any any -> any any ( "
+        "msg:\"DetectBase64DataSetupTest\"; "
+        "file_data; "
+        "content:\"SGV\"; "
+        "base64_decode: bytes 16; "
+        "base64_data; "
+        "content:\"content\"; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        printf("SigInit failed: ");
+        goto end;
+    }
+
+    sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_PMATCH];
+    if (sm != NULL) {
+        printf("DETECT_SM_LIST_PMATCH is not NULL: ");
+        goto end;
+    }
+
+    sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_FILEDATA];
+    if (sm == NULL) {
+        printf("DETECT_SM_LIST_FILEDATA is NULL: ");
+        goto end;
+    }
+
+    sm = de_ctx->sig_list->sm_lists[DETECT_SM_LIST_BASE64_DATA];
+    if (sm == NULL) {
+        printf("DETECT_SM_LIST_BASE64_DATA is NULL: ");
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (de_ctx != NULL) {
+        SigGroupCleanup(de_ctx);
+        SigCleanSignatures(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    return retval;
+}
+
+static int DetectBase64DataSetupTest03(void)
+{
+    DetectEngineCtx *de_ctx = NULL;
+    int retval = 0;
+
+    de_ctx = DetectEngineCtxInit();
+    if (de_ctx == NULL) {
+        goto end;
+    }
+
+    de_ctx->flags |= DE_QUIET;
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert smtp any any -> any any ( "
+        "msg:\"DetectBase64DataSetupTest\"; "
+        "base64_decode: bytes 16; "
+        "base64_data; "
+        "content:\"content\"; "
+        "file_data; "
+        "content:\"SGV\"; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list != NULL) {
+        printf("SigInit should have failed: ");
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (de_ctx != NULL) {
+        SigGroupCleanup(de_ctx);
+        SigCleanSignatures(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    return retval;
+}
+
+#endif
+
+static void DetectBase64DataRegisterTests(void)
+{
+#ifdef UNITTESTS
+    UtRegisterTest("DetectBase64DataSetupTest01", DetectBase64DataSetupTest01,
+        1);
+    UtRegisterTest("DetectBase64DataSetupTest02", DetectBase64DataSetupTest02,
+        1);
+    UtRegisterTest("DetectBase64DataSetupTest03", DetectBase64DataSetupTest03,
+        1);
+#endif /* UNITTESTS */
+}
diff --git a/src/detect-base64-data.h b/src/detect-base64-data.h
new file mode 100644 (file)
index 0000000..12fa60d
--- /dev/null
@@ -0,0 +1,25 @@
+/* Copyright (C) 2015 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.
+ */
+
+#ifndef __DETECT_BASE64_DATA_H__
+#define __DETECT_BASE64_DATA_H__
+
+void DetectBase64DataRegister(void);
+int DetectBase64DataDoMatch(DetectEngineCtx *, DetectEngineThreadCtx *,
+    Signature *, Flow *);
+
+#endif /* __DETECT_BASE64_DATA_H__ */
diff --git a/src/detect-base64-decode.c b/src/detect-base64-decode.c
new file mode 100644 (file)
index 0000000..bd9baea
--- /dev/null
@@ -0,0 +1,778 @@
+/* Copyright (C) 2015 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 "suricata-common.h"
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-base64-decode.h"
+#include "util-base64.h"
+#include "util-byte.h"
+#include "util-print.h"
+
+/* Arbitrary maximum buffer size for decoded base64 data. */
+#define BASE64_DECODE_MAX 65535
+
+static const char decode_pattern[] = "\\s*(bytes\\s+(\\d+),?)?"
+    "\\s*(offset\\s+(\\d+),?)?"
+    "\\s*(\\w+)?";
+static pcre *decode_pcre = NULL;
+static pcre_extra *decode_pcre_study = NULL;
+
+static int DetectBase64DecodeSetup(DetectEngineCtx *, Signature *, char *);
+static void DetectBase64DecodeFree(void *);
+static void DetectBase64DecodeRegisterTests(void);
+
+void DetectBase64DecodeRegister(void)
+{
+    const char *pcre_errptr;
+    int pcre_erroffset;
+
+    sigmatch_table[DETECT_BASE64_DECODE].name = "base64_decode";
+    sigmatch_table[DETECT_BASE64_DECODE].desc =
+        "Decodes base64 encoded data.";
+    sigmatch_table[DETECT_BASE64_DECODE].url =
+        "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Payload_keywords#base64_decode";
+    sigmatch_table[DETECT_BASE64_DECODE].Setup = DetectBase64DecodeSetup;
+    sigmatch_table[DETECT_BASE64_DECODE].Free = DetectBase64DecodeFree;
+    sigmatch_table[DETECT_BASE64_DECODE].RegisterTests =
+        DetectBase64DecodeRegisterTests;
+
+    sigmatch_table[DETECT_BASE64_DECODE].flags |= SIGMATCH_PAYLOAD;
+    sigmatch_table[DETECT_BASE64_DECODE].flags |= SIGMATCH_OPTIONAL_OPT;
+
+    decode_pcre = pcre_compile(decode_pattern, 0, &pcre_errptr, &pcre_erroffset,
+        NULL);
+    if (decode_pcre == NULL) {
+        SCLogError(SC_ERR_PCRE_COMPILE, "Failed to compile pattern \"%s\" at"
+            " offset %d: %s", decode_pattern, pcre_erroffset, pcre_errptr);
+        exit(EXIT_FAILURE);
+    }
+
+    decode_pcre_study = pcre_study(decode_pcre, 0, &pcre_errptr);
+    if (pcre_errptr != NULL) {
+        SCLogError(SC_ERR_PCRE_STUDY, "Failed to study pattern \"%s\": %s",
+            decode_pattern, pcre_errptr);
+        exit(EXIT_FAILURE);
+    }
+}
+
+int DetectBase64DecodeDoMatch(DetectEngineThreadCtx *det_ctx, Signature *s,
+    const SigMatch *sm, uint8_t *payload, uint32_t payload_len)
+{
+    DetectBase64Decode *data = (DetectBase64Decode *)sm->ctx;
+    int decode_len;
+
+#if 0
+    printf("Input data:\n");
+    PrintRawDataFp(stdout, payload, payload_len);
+#endif
+
+    if (data->relative) {
+        payload += det_ctx->buffer_offset;
+        payload_len -= det_ctx->buffer_offset;
+    }
+
+    if (data->offset) {
+        if (data->offset >= payload_len) {
+            return 0;
+        }
+        payload = payload + data->offset;
+        payload_len -= data->offset;
+    }
+
+    decode_len = MIN(payload_len, data->bytes);
+
+#if 0
+    printf("Decoding:\n");
+    PrintRawDataFp(stdout, payload, decode_len);
+#endif
+
+    det_ctx->base64_decoded_len = DecodeBase64(det_ctx->base64_decoded,
+        payload, decode_len, 0);
+    SCLogDebug("Decoded %d bytes from base64 data.",
+        det_ctx->base64_decoded_len);
+#if 0
+    if (det_ctx->base64_decoded_len) {
+        printf("Decoded data:\n");
+        PrintRawDataFp(stdout, det_ctx->base64_decoded,
+            det_ctx->base64_decoded_len);
+    }
+#endif
+
+    return det_ctx->base64_decoded_len > 0;
+}
+
+static int DetectBase64DecodeParse(const char *str, uint32_t *bytes,
+    uint32_t *offset, uint8_t *relative)
+{
+    static const int max = 30;
+    int ov[max];
+    int pcre_rc;
+    const char *bytes_str = NULL;
+    const char *offset_str = NULL;
+    const char *relative_str = NULL;
+    int retval = 0;
+
+    *bytes = 0;
+    *offset = 0;
+    *relative = 0;
+
+    pcre_rc = pcre_exec(decode_pcre, decode_pcre_study, str, strlen(str), 0, 0,
+        ov, max);
+    if (pcre_rc < 3) {
+        goto error;
+    }
+
+    if (pcre_rc >= 3) {
+        if (pcre_get_substring((char *)str, ov, max, 2, &bytes_str) > 0) {
+            if (ByteExtractStringUint32(bytes, 10, 0, bytes_str) <= 0) {
+                SCLogError(SC_ERR_INVALID_RULE_ARGUMENT,
+                    "Bad value for bytes: \"%s\"", bytes_str);
+                goto error;
+            }
+        }
+     }
+
+    if (pcre_rc >= 5) {
+        if (pcre_get_substring((char *)str, ov, max, 4, &offset_str)) {
+            if (ByteExtractStringUint32(offset, 10, 0, offset_str) <= 0) {
+                SCLogError(SC_ERR_INVALID_RULE_ARGUMENT,
+                    "Bad value for offset: \"%s\"", offset_str);
+                goto error;
+            }
+        }
+    }
+
+    if (pcre_rc >= 6) {
+        if (pcre_get_substring((char *)str, ov, max, 5, &relative_str)) {
+            if (strcmp(relative_str, "relative") == 0) {
+                *relative = 1;
+            }
+            else {
+                SCLogError(SC_ERR_INVALID_RULE_ARGUMENT,
+                    "Invalid argument: \"%s\"", relative_str);
+                goto error;
+            }
+        }
+    }
+
+    retval = 1;
+error:
+    if (bytes_str != NULL) {
+        pcre_free_substring(bytes_str);
+    }
+    if (offset_str != NULL) {
+        pcre_free_substring(offset_str);
+    }
+    if (relative_str != NULL) {
+        pcre_free_substring(relative_str);
+    }
+    return retval;
+}
+
+static int DetectBase64DecodeSetup(DetectEngineCtx *de_ctx, Signature *s,
+    char *str)
+{
+    uint32_t bytes = 0;
+    uint32_t offset = 0;
+    uint8_t relative = 0;
+    DetectBase64Decode *data = NULL;
+    int sm_list;
+    SigMatch *sm = NULL;
+    SigMatch *pm = NULL;
+
+    if (str != NULL) {
+        if (!DetectBase64DecodeParse(str, &bytes, &offset, &relative)) {
+            goto error;
+        }
+    }
+    data = SCCalloc(1, sizeof(DetectBase64Decode));
+    if (unlikely(data == NULL)) {
+        goto error;
+    }
+    data->bytes = bytes;
+    data->offset = offset;
+    data->relative = relative;
+
+    if (s->list != DETECT_SM_LIST_NOTSET) {
+        sm_list = s->list;
+#if 0
+        if (data->relative) {
+            pm = SigMatchGetLastSMFromLists(s, 4,
+                DETECT_CONTENT, s->sm_lists_tail[sm_list],
+                DETECT_PCRE, s->sm_lists_tail[sm_list]);
+        }
+#endif
+    }
+    else {
+        /* Copied from detect-isdataat.c. */
+        pm = SigMatchGetLastSMFromLists(s, 168,
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+            DETECT_CONTENT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+            DETECT_PCRE, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+            DETECT_BYTETEST, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+            DETECT_BYTEJUMP, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+            DETECT_BYTE_EXTRACT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_PMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_UMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCBDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_FILEDATA],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HMDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HCDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRUDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSMDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HSCDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HUADMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HHHDMATCH],
+            DETECT_ISDATAAT, s->sm_lists_tail[DETECT_SM_LIST_HRHHDMATCH]);
+        if (pm == NULL) {
+            sm_list = DETECT_SM_LIST_PMATCH;
+        }
+        else {
+            sm_list = SigMatchListSMBelongsTo(s, pm);
+        }
+    }
+
+    sm = SigMatchAlloc();
+    if (sm == NULL) {
+        goto error;
+    }
+    sm->type = DETECT_BASE64_DECODE;
+    sm->ctx = (SigMatchCtx *)data;
+    SigMatchAppendSMToList(s, sm, sm_list);
+
+    if (!data->bytes) {
+        data->bytes = BASE64_DECODE_MAX;
+    }
+    if (data->bytes > de_ctx->base64_decode_max_len) {
+        de_ctx->base64_decode_max_len = data->bytes;
+    }
+
+    return 0;
+error:
+    if (data != NULL) {
+        SCFree(data);
+    }
+    return -1;
+}
+
+static void DetectBase64DecodeFree(void *ptr)
+{
+    DetectBase64Decode *data = ptr;
+    SCFree(data);
+}
+
+
+#ifdef UNITTESTS
+
+#include "detect.h"
+#include "detect-engine.h"
+#include "detect-parse.h"
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "app-layer-parser.h"
+#include "flow-util.h"
+#include "stream-tcp.h"
+
+static int DetectBase64TestDecodeParse(void)
+{
+    int retval = 0;
+    uint32_t bytes = 0;
+    uint32_t offset = 0;
+    uint8_t relative = 0;
+
+    if (!DetectBase64DecodeParse("bytes 1", &bytes, &offset, &relative)) {
+        goto end;
+    }
+    if (bytes != 1 || offset != 0 || relative != 0) {
+        goto end;
+    }
+
+    if (!DetectBase64DecodeParse("offset 9", &bytes, &offset, &relative)) {
+        goto end;
+    }
+    if (bytes != 0 || offset != 9 || relative != 0) {
+        goto end;
+    }
+
+    if (!DetectBase64DecodeParse("relative", &bytes, &offset, &relative)) {
+        goto end;
+    }
+    if (bytes != 0 || offset != 0 || relative != 1) {
+        goto end;
+    }
+
+    if (!DetectBase64DecodeParse("bytes 1, offset 2", &bytes, &offset,
+            &relative)) {
+        goto end;
+    }
+    if (bytes != 1 || offset != 2 || relative != 0) {
+        goto end;
+    }
+
+    if (!DetectBase64DecodeParse("bytes 1, offset 2, relative", &bytes, &offset,
+            &relative)) {
+        goto end;
+    }
+    if (bytes != 1 || offset != 2 || relative != 1) {
+        goto end;
+    }
+
+    if (!DetectBase64DecodeParse("offset 2, relative", &bytes, &offset,
+            &relative)) {
+        goto end;
+    }
+    if (bytes != 0 || offset != 2 || relative != 1) {
+        goto end;
+    }
+
+    /* Misspelled relative. */
+    if (DetectBase64DecodeParse("bytes 1, offset 2, relatve", &bytes, &offset,
+            &relative)) {
+        goto end;
+    }
+
+    /* Misspelled bytes. */
+    if (DetectBase64DecodeParse("byts 1, offset 2, relatve", &bytes, &offset,
+            &relative)) {
+        goto end;
+    }
+
+    /* Misspelled offset. */
+    if (DetectBase64DecodeParse("bytes 1, offst 2, relatve", &bytes, &offset,
+            &relative)) {
+        goto end;
+    }
+
+    /* Misspelled empty string. */
+    if (DetectBase64DecodeParse("", &bytes, &offset, &relative)) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    return retval;
+}
+
+/**
+ * Test keyword setup on basic content.
+ */
+static int DetectBase64DecodeTestSetup(void)
+{
+    DetectEngineCtx *de_ctx = NULL;
+    Signature *s;
+    int retval = 0;
+
+    de_ctx = DetectEngineCtxInit();
+    if (de_ctx == NULL) {
+        goto end;
+    }
+
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert tcp any any -> any any ("
+        "msg:\"DetectBase64DecodeTestSetup\"; "
+        "base64_decode; content:\"content\"; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        goto end;
+    }
+    s = de_ctx->sig_list;
+    if (s == NULL) {
+        goto end;
+    }
+    if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (de_ctx != NULL) {
+        SigGroupCleanup(de_ctx);
+        SigCleanSignatures(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    return retval;
+}
+
+/**
+ * Test keyword setup when the prior rule has a content modifier on
+ * it.
+ */
+static int DetectBase64DecodeHttpHeaderTestSetup(void)
+{
+    DetectEngineCtx *de_ctx = NULL;
+    Signature *s;
+    int retval = 0;
+
+    de_ctx = DetectEngineCtxInit();
+    if (de_ctx == NULL) {
+        goto end;
+    }
+
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert tcp any any -> any any ("
+        "msg:\"DetectBase64DecodeTestSetup\"; "
+        "content:\"Authorization: basic \"; http_header; "
+        "base64_decode; content:\"content\"; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        goto end;
+    }
+    s = de_ctx->sig_list;
+    if (s == NULL) {
+        goto end;
+    }
+
+    /* I'm not complete sure if this list should not be NULL. */
+    if (s->sm_lists_tail[DETECT_SM_LIST_PMATCH] == NULL) {
+        goto end;
+    }
+
+    /* Test that the http header list is not NULL. */
+    if (s->sm_lists_tail[DETECT_SM_LIST_HHDMATCH] == NULL) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (de_ctx != NULL) {
+        SigGroupCleanup(de_ctx);
+        SigCleanSignatures(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    return retval;
+}
+
+static int DetectBase64DecodeTestDecode(void)
+{
+    ThreadVars tv;
+    DetectEngineCtx *de_ctx = NULL;
+    DetectEngineThreadCtx *det_ctx = NULL;
+    Packet *p = NULL;
+    int retval = 0;
+
+    uint8_t payload[] = {
+        'S', 'G', 'V', 's', 'b', 'G', '8', 'g',
+        'V', '2', '9', 'y', 'b', 'G', 'Q', '=',
+    };
+
+    memset(&tv, 0, sizeof(tv));
+
+    if ((de_ctx = DetectEngineCtxInit()) == NULL) {
+        goto end;
+    }
+
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert tcp any any -> any any (msg:\"base64 test\"; "
+        "base64_decode; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        goto end;
+    }
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+    p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+    if (p == NULL) {
+        goto end;
+    }
+
+    SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+    if (det_ctx->base64_decoded_len == 0) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (det_ctx != NULL) {
+        DetectEngineThreadCtxDeinit(&tv, det_ctx);
+    }
+    if (de_ctx != NULL) {
+        SigCleanSignatures(de_ctx);
+        SigGroupCleanup(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    if (p != NULL) {
+        UTHFreePacket(p);
+    }
+    return retval;
+}
+
+static int DetectBase64DecodeTestDecodeWithOffset(void)
+{
+    ThreadVars tv;
+    DetectEngineCtx *de_ctx = NULL;
+    DetectEngineThreadCtx *det_ctx = NULL;
+    Packet *p = NULL;
+    int retval = 0;
+
+    uint8_t payload[] = {
+        'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+        'S', 'G', 'V', 's', 'b', 'G', '8', 'g',
+        'V', '2', '9', 'y', 'b', 'G', 'Q', '=',
+    };
+    char decoded[] = "Hello World";
+
+    memset(&tv, 0, sizeof(tv));
+
+    if ((de_ctx = DetectEngineCtxInit()) == NULL) {
+        goto end;
+    }
+
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert tcp any any -> any any (msg:\"base64 test\"; "
+        "base64_decode: offset 8; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        goto end;
+    }
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+    p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+    if (p == NULL) {
+        goto end;
+    }
+
+    SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+    if (det_ctx->base64_decoded_len != (int)strlen(decoded)) {
+        goto end;
+    }
+    if (memcmp(det_ctx->base64_decoded, decoded, strlen(decoded))) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (det_ctx != NULL) {
+        DetectEngineThreadCtxDeinit(&tv, det_ctx);
+    }
+    if (de_ctx != NULL) {
+        SigCleanSignatures(de_ctx);
+        SigGroupCleanup(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    if (p != NULL) {
+        UTHFreePacket(p);
+    }
+    return retval;
+}
+
+static int DetectBase64DecodeTestDecodeLargeOffset(void)
+{
+    ThreadVars tv;
+    DetectEngineCtx *de_ctx = NULL;
+    DetectEngineThreadCtx *det_ctx = NULL;
+    Packet *p = NULL;
+    int retval = 0;
+
+    uint8_t payload[] = {
+        'S', 'G', 'V', 's', 'b', 'G', '8', 'g',
+        'V', '2', '9', 'y', 'b', 'G', 'Q', '=',
+    };
+
+    memset(&tv, 0, sizeof(tv));
+
+    if ((de_ctx = DetectEngineCtxInit()) == NULL) {
+        goto end;
+    }
+
+    /* Offset is out of range. */
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert tcp any any -> any any (msg:\"base64 test\"; "
+        "base64_decode: bytes 16, offset 32; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        goto end;
+    }
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+    p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+    if (p == NULL) {
+        goto end;
+    }
+
+    SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+    if (det_ctx->base64_decoded_len != 0) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (det_ctx != NULL) {
+        DetectEngineThreadCtxDeinit(&tv, det_ctx);
+    }
+    if (de_ctx != NULL) {
+        SigCleanSignatures(de_ctx);
+        SigGroupCleanup(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    if (p != NULL) {
+        UTHFreePacket(p);
+    }
+    return retval;
+}
+
+static int DetectBase64DecodeTestDecodeRelative(void)
+{
+    ThreadVars tv;
+    DetectEngineCtx *de_ctx = NULL;
+    DetectEngineThreadCtx *det_ctx = NULL;
+    Packet *p = NULL;
+    int retval = 0;
+
+    uint8_t payload[] = {
+        'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+        'S', 'G', 'V', 's', 'b', 'G', '8', 'g',
+        'V', '2', '9', 'y', 'b', 'G', 'Q', '=',
+    };
+    char decoded[] = "Hello World";
+
+    memset(&tv, 0, sizeof(tv));
+
+    if ((de_ctx = DetectEngineCtxInit()) == NULL) {
+        goto end;
+    }
+
+    de_ctx->sig_list = SigInit(de_ctx,
+        "alert tcp any any -> any any (msg:\"base64 test\"; "
+        "content:\"aaaaaaaa\"; "
+        "base64_decode: relative; "
+        "sid:1; rev:1;)");
+    if (de_ctx->sig_list == NULL) {
+        goto end;
+    }
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
+
+    p = UTHBuildPacket(payload, sizeof(payload), IPPROTO_TCP);
+    if (p == NULL) {
+        goto end;
+    }
+
+    SigMatchSignatures(&tv, de_ctx, det_ctx, p);
+    if (det_ctx->base64_decoded_len != (int)strlen(decoded)) {
+        goto end;
+    }
+    if (memcmp(det_ctx->base64_decoded, decoded, strlen(decoded))) {
+        goto end;
+    }
+
+    retval = 1;
+end:
+    if (det_ctx != NULL) {
+        DetectEngineThreadCtxDeinit(&tv, det_ctx);
+    }
+    if (de_ctx != NULL) {
+        SigCleanSignatures(de_ctx);
+        SigGroupCleanup(de_ctx);
+        DetectEngineCtxFree(de_ctx);
+    }
+    if (p != NULL) {
+        UTHFreePacket(p);
+    }
+    return retval;
+}
+
+#endif
+
+static void DetectBase64DecodeRegisterTests(void)
+{
+#ifdef UNITTESTS
+    UtRegisterTest("DetectBase64TestDecodeParse", DetectBase64TestDecodeParse,
+        1);
+    UtRegisterTest("DetectBase64DecodeTestSetup", DetectBase64DecodeTestSetup,
+        1);
+    UtRegisterTest("DetectBase64DecodeHttpHeaderTestSetup",
+        DetectBase64DecodeHttpHeaderTestSetup, 1);
+    UtRegisterTest("DetectBase64DecodeTestDecode", DetectBase64DecodeTestDecode,
+        1);
+    UtRegisterTest("DetectBase64DecodeTestDecodeWithOffset",
+        DetectBase64DecodeTestDecodeWithOffset, 1);
+    UtRegisterTest("DetectBase64DecodeTestDecodeLargeOffset",
+        DetectBase64DecodeTestDecodeLargeOffset, 1);
+    UtRegisterTest("DetectBase64DecodeTestDecodeRelative",
+        DetectBase64DecodeTestDecodeRelative, 1);
+#endif /* UNITTESTS */
+}
diff --git a/src/detect-base64-decode.h b/src/detect-base64-decode.h
new file mode 100644 (file)
index 0000000..a1ce388
--- /dev/null
@@ -0,0 +1,33 @@
+/* Copyright (C) 2015 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.
+ */
+
+#ifndef __DETECT_BASE64_DECODE_H__
+#define __DETECT_BASE64_DECODE_H__
+
+#include "app-layer-template.h"
+
+typedef struct DetectBase64Decode_ {
+    uint32_t bytes;
+    uint32_t offset;
+    uint8_t relative;
+} DetectBase64Decode;
+
+void DetectBase64DecodeRegister(void);
+int DetectBase64DecodeDoMatch(DetectEngineThreadCtx *, Signature *,
+    const SigMatch *, uint8_t *, uint32_t);
+
+#endif /* __DETECT_BASE64_DECODE_H__ */
index a434ca5ad8c800fa46218b66ffc555364489621a..17df02ce9a1a80cc02857e9c912b713134205cbc 100644 (file)
@@ -42,6 +42,8 @@
 #include "detect-uricontent.h"
 #include "detect-urilen.h"
 #include "detect-lua.h"
+#include "detect-base64-decode.h"
+#include "detect-base64-data.h"
 
 #include "app-layer-dcerpc.h"
 
@@ -551,6 +553,16 @@ int DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx
         SCLogDebug("lua match");
         goto match;
 #endif /* HAVE_LUA */
+    } else if (sm->type == DETECT_BASE64_DECODE) {
+        if (DetectBase64DecodeDoMatch(det_ctx, s, sm, buffer, buffer_len)) {
+            if (s->sm_arrays[DETECT_SM_LIST_BASE64_DATA] != NULL) {
+                KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+                if (DetectBase64DataDoMatch(de_ctx, det_ctx, s, f)) {
+                    /* Base64 is a terminal list. */
+                    goto final_match;
+                }
+            }
+        }
     } else {
         SCLogDebug("sm->type %u", sm->type);
 #ifdef DEBUG
@@ -569,8 +581,8 @@ match:
         KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
         int r = DetectEngineContentInspection(de_ctx, det_ctx, s, sm->next, f, buffer, buffer_len, stream_start_offset, inspection_mode, data);
         SCReturnInt(r);
-    } else {
-        KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
-        SCReturnInt(1);
     }
+final_match:
+    KEYWORD_PROFILING_END(det_ctx, sm->type, 1);
+    SCReturnInt(1);
 }
index dc0b5026bee78d38233c318c6ef4b2cdf6318db3..5e80b9b8ccd56ee79e80bec3fdd3a11fb2050e44 100644 (file)
@@ -50,6 +50,7 @@ enum {
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_HRHHD,
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_DNSQUERY,
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_FD_SMTP,
+    DETECT_ENGINE_CONTENT_INSPECTION_MODE_BASE64,
     DETECT_ENGINE_CONTENT_INSPECTION_MODE_TEMPLATE_BUFFER,
 };
 
index 431f4b2aa7329fbbf456e8e91be775e58aeca1cc..63542dc83b7289e5c60e619c3b2980ec5842018f 100644 (file)
@@ -1338,6 +1338,16 @@ static TmEcode ThreadCtxDoInit (DetectEngineCtx *de_ctx, DetectEngineThreadCtx *
         return TM_ECODE_FAILED;
     }
 
+    /* Allocate space for base64 decoded data. */
+    if (de_ctx->base64_decode_max_len) {
+        det_ctx->base64_decoded = SCMalloc(de_ctx->base64_decode_max_len);
+        if (det_ctx->base64_decoded == NULL) {
+            return TM_ECODE_FAILED;
+        }
+        det_ctx->base64_decoded_len_max = de_ctx->base64_decode_max_len;
+        det_ctx->base64_decoded_len = 0;
+    }
+
     DetectEngineThreadCtxInitKeywords(de_ctx, det_ctx);
 #ifdef PROFILING
     SCProfilingRuleThreadSetup(de_ctx->profile_ctx, det_ctx);
@@ -1542,6 +1552,11 @@ void DetectEngineThreadCtxFree(DetectEngineThreadCtx *det_ctx)
         SCFree(det_ctx->hcbd);
     }
 
+    /* Decoded base64 data. */
+    if (det_ctx->base64_decoded != NULL) {
+        SCFree(det_ctx->base64_decoded);
+    }
+
     if (det_ctx->de_ctx != NULL) {
         DetectEngineThreadCtxDeinitKeywords(det_ctx->de_ctx, det_ctx);
 #ifdef UNITTESTS
@@ -2646,6 +2661,9 @@ const char *DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type)
         case DETECT_SM_LIST_MODBUS_MATCH:
             return "modbus";
 
+        case DETECT_SM_LIST_BASE64_DATA:
+            return "base64_data";
+
         case DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH:
             return "template_buffer";
 
index 9ac53e03c2d572f5b45acbcd1515699e489fd948..d0f46714ec66e3b768afc17b4fb8cc5fd8cfcc78 100644 (file)
@@ -1319,6 +1319,20 @@ int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
         }
     }
 
+    if (s->sm_lists[DETECT_SM_LIST_BASE64_DATA] != NULL) {
+        uint16_t idx = s->sm_lists[DETECT_SM_LIST_BASE64_DATA]->idx;
+        for (int list = 0; list < DETECT_SM_LIST_MAX; list++) {
+            if (list != DETECT_SM_LIST_BASE64_DATA &&
+                s->sm_lists[list] != NULL) {
+                if (s->sm_lists[list]->idx > idx) {
+                    SCLogError(SC_ERR_INVALID_SIGNATURE, "Rule buffer "
+                        "cannot be reset after base64_data.");
+                    SCReturnInt(0);
+                }
+            }
+        }
+    }
+
 #ifdef HAVE_LUA
     DetectLuaPostSetup(s);
 #endif
index c9a16ead5c908e3f02704015cdc12e4ae0455bdb..65f18dc67a80fbcc0b3ebf22acc0a52499f5561b 100644 (file)
@@ -60,6 +60,8 @@
 #include "detect-engine-event.h"
 #include "decode.h"
 
+#include "detect-base64-decode.h"
+#include "detect-base64-data.h"
 #include "detect-ipopts.h"
 #include "detect-flags.h"
 #include "detect-fragbits.h"
@@ -1254,6 +1256,8 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh
     p->alerts.cnt = 0;
     det_ctx->filestore_cnt = 0;
 
+    det_ctx->base64_decoded_len = 0;
+
     /* No need to perform any detection on this packet, if the the given flag is set.*/
     if (p->flags & PKT_NOPACKET_INSPECTION) {
         SCReturnInt(0);
@@ -5130,6 +5134,8 @@ void SigTableSetup(void)
     DetectDnsQueryRegister();
     DetectModbusRegister();
     DetectAppLayerProtocolRegister();
+    DetectBase64DecodeRegister();
+    DetectBase64DataRegister();
     DetectTemplateRegister();
     DetectTemplateBufferRegister();
 }
index 8d131b92a378ad352858bc694544e1e8a4a4189b..89ce35ab1ca2112adfe5b4e48e1dc981edf61e69 100644 (file)
@@ -126,6 +126,8 @@ enum DetectSigmatchListEnum {
 
     DETECT_SM_LIST_MODBUS_MATCH,
 
+    DETECT_SM_LIST_BASE64_DATA,
+
     DETECT_SM_LIST_TEMPLATE_BUFFER_MATCH,
 
     /* list for post match actions: flowbit set, flowint increment, etc */
@@ -687,6 +689,9 @@ typedef struct DetectEngineCtx_ {
      *  we can't lookup by proto, address, port as we don't have these */
     struct SigGroupHead_ *decoder_event_sgh;
 
+    /* Maximum size of the buffer for decoded base64 data. */
+    uint32_t base64_decode_max_len;
+
     /** Store rule file and line so that parsers can use them in errors. */
     char *rule_file;
     int rule_line;
@@ -883,6 +888,10 @@ typedef struct DetectEngineThreadCtx_ {
     void **keyword_ctxs_array;
     int keyword_ctxs_size;
 
+    uint8_t *base64_decoded;
+    int base64_decoded_len;
+    int base64_decoded_len_max;
+
 #ifdef PROFILING
     struct SCProfileData_ *rule_perf_data;
     int rule_perf_data_size;
@@ -1221,6 +1230,8 @@ enum {
     DETECT_AL_MODBUS,
 
     DETECT_XBITS,
+    DETECT_BASE64_DECODE,
+    DETECT_BASE64_DATA,
 
     DETECT_TEMPLATE,
     DETECT_AL_TEMPLATE_BUFFER,
index 461e18702e76481d5a28be402b5f427572409996..af266d334fea448c0768ddc5026c32fddf455861 100644 (file)
@@ -310,6 +310,7 @@ const char * SCErrorToString(SCError err)
         CASE_CODE (SC_ERR_MT_NO_SELECTOR);
         CASE_CODE (SC_ERR_MT_DUPLICATE_TENANT);
         CASE_CODE (SC_ERR_NO_JSON_SUPPORT);
+        CASE_CODE (SC_ERR_INVALID_RULE_ARGUMENT);
     }
 
     return "UNKNOWN_ERROR";
index b7df4766f1852be939106d96019bbc5c696828d3..d0b2471567905dd9b197bca20ccfa1d5d7116db9 100644 (file)
@@ -299,6 +299,8 @@ typedef enum {
     SC_ERR_MT_NO_SELECTOR,
     SC_ERR_MT_DUPLICATE_TENANT,
     SC_ERR_NO_JSON_SUPPORT,
+    SC_ERR_INVALID_RULE_ARGUMENT, /**< Generic error code for invalid
+                                   * rule argument. */
 } SCError;
 
 const char *SCErrorToString(SCError);