]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
dns: add dns.answer.name keyword
authorJason Ish <jason.ish@oisf.net>
Mon, 23 Oct 2023 21:28:40 +0000 (15:28 -0600)
committerVictor Julien <victor@inliniac.net>
Wed, 13 Dec 2023 18:17:17 +0000 (19:17 +0100)
This sticky buffer will allow content matching on the answer names.
While ansers typically only occur in DNS responses, we allow the buffer
to be used in request context as well as the request message format
allows it.

Feature: #6496

rust/src/dns/dns.rs
src/Makefile.am
src/detect-dns-answer-name.c [new file with mode: 0644]
src/detect-dns-answer-name.h [new file with mode: 0644]
src/detect-engine-register.c
src/detect-engine-register.h

index aa0042f0b2595f3151d4d5f4e8d6e9cc2b727671..8933c680db5641386ac119183a07fcd483f22a38 100644 (file)
@@ -870,6 +870,31 @@ pub unsafe extern "C" fn rs_dns_tx_get_query_name(
     return 0;
 }
 
+/// Get the DNS response answer name and index i.
+#[no_mangle]
+pub unsafe extern "C" fn SCDnsTxGetAnswerName(
+    tx: &mut DNSTransaction, to_client: bool, i: u32, buf: *mut *const u8, len: *mut u32,
+) -> bool {
+    let answers = if to_client {
+        tx.response.as_ref().map(|response| &response.answers)
+    } else {
+        tx.request.as_ref().map(|request| &request.answers)
+    };
+    let index = i as usize;
+
+    if let Some(answers) = answers {
+        if let Some(answer) = answers.get(index) {
+            if !answer.name.is_empty() {
+                *buf = answer.name.as_ptr();
+                *len = answer.name.len() as u32;
+                return true;
+            }
+        }
+    }
+
+    false
+}
+
 /// Get the DNS transaction ID of a transaction.
 //
 /// extern uint16_t rs_dns_tx_get_tx_id(RSDNSTransaction *);
index 4695c2d35f51fb3c68b674260d47c0f2cf4eb98f..25bec98e2e7beab6aa23f8a0a3579f1b92071147 100755 (executable)
@@ -121,6 +121,7 @@ noinst_HEADERS = \
        detect-detection-filter.h \
        detect-distance.h \
        detect-dnp3.h \
+       detect-dns-answer-name.h \
        detect-dns-opcode.h \
        detect-dns-query.h \
        detect-dsize.h \
@@ -736,6 +737,7 @@ libsuricata_c_a_SOURCES = \
        detect-detection-filter.c \
        detect-distance.c \
        detect-dnp3.c \
+       detect-dns-answer-name.c \
        detect-dns-opcode.c \
        detect-dns-query.c \
        detect-dsize.c \
diff --git a/src/detect-dns-answer-name.c b/src/detect-dns-answer-name.c
new file mode 100644 (file)
index 0000000..bc64f55
--- /dev/null
@@ -0,0 +1,172 @@
+/* Copyright (C) 2023 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
+ *
+ * Detect keyword for DNS answer name: dns.answer.name
+ */
+
+#include "detect.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-prefilter.h"
+#include "detect-engine-content-inspection.h"
+#include "detect-dns-answer-name.h"
+#include "util-profiling.h"
+#include "rust.h"
+
+typedef struct PrefilterMpm {
+    int list_id;
+    const MpmCtx *mpm_ctx;
+    const DetectEngineTransforms *transforms;
+} PrefilterMpm;
+
+static int detect_buffer_id = 0;
+
+static int DetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+    if (DetectBufferSetActiveList(de_ctx, s, detect_buffer_id) < 0) {
+        return -1;
+    }
+    if (DetectSignatureSetAppProto(s, ALPROTO_DNS) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static InspectionBuffer *GetBuffer(DetectEngineThreadCtx *det_ctx, uint8_t flags,
+        const DetectEngineTransforms *transforms, void *txv, uint32_t index, int list_id)
+{
+    InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, index);
+    if (buffer == NULL) {
+        return NULL;
+    }
+    if (buffer->initialized) {
+        return buffer;
+    }
+
+    bool to_client = (flags & STREAM_TOSERVER) == 0;
+    const uint8_t *data = NULL;
+    uint32_t data_len = 0;
+
+    if (!SCDnsTxGetAnswerName(txv, to_client, index, &data, &data_len)) {
+        InspectionBufferSetupMultiEmpty(buffer);
+        return NULL;
+    }
+    InspectionBufferSetupMulti(buffer, transforms, data, data_len);
+    buffer->flags = DETECT_CI_FLAGS_SINGLE;
+    return buffer;
+}
+
+static uint8_t DetectEngineInspectCb(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f,
+        uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
+{
+    const DetectEngineTransforms *transforms = NULL;
+    if (!engine->mpm) {
+        transforms = engine->v2.transforms;
+    }
+
+    for (uint32_t i = 0;; i++) {
+        InspectionBuffer *buffer = GetBuffer(det_ctx, flags, transforms, txv, i, engine->sm_list);
+        if (buffer == NULL || buffer->inspect == NULL) {
+            break;
+        }
+
+        const bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd,
+                NULL, f, buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
+        if (match) {
+            return DETECT_ENGINE_INSPECT_SIG_MATCH;
+        }
+    }
+
+    return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
+}
+
+static void PrefilterTx(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
+        void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
+{
+    SCEnter();
+
+    const PrefilterMpm *ctx = (const PrefilterMpm *)pectx;
+    const MpmCtx *mpm_ctx = ctx->mpm_ctx;
+    const int list_id = ctx->list_id;
+
+    for (uint32_t i = 0;; i++) {
+        InspectionBuffer *buffer = GetBuffer(det_ctx, flags, ctx->transforms, txv, i, list_id);
+        if (buffer == NULL) {
+            break;
+        }
+
+        if (buffer->inspect_len >= mpm_ctx->minlen) {
+            (void)mpm_table[mpm_ctx->mpm_type].Search(
+                    mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
+            PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
+        }
+    }
+}
+
+static void PrefilterMpmFree(void *ptr)
+{
+    SCFree(ptr);
+}
+
+static int PrefilterMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
+        const DetectBufferMpmRegistry *mpm_reg, int list_id)
+{
+    PrefilterMpm *pectx = SCCalloc(1, sizeof(*pectx));
+    if (pectx == NULL) {
+        return -1;
+    }
+    pectx->list_id = list_id;
+    pectx->mpm_ctx = mpm_ctx;
+    pectx->transforms = &mpm_reg->transforms;
+
+    return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTx, mpm_reg->app_v2.alproto,
+            mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmFree, mpm_reg->pname);
+}
+
+void DetectDnsAnswerNameRegister(void)
+{
+    static const char *keyword = "dns.answer.name";
+    sigmatch_table[DETECT_AL_DNS_ANSWER_NAME].name = keyword;
+    sigmatch_table[DETECT_AL_DNS_ANSWER_NAME].desc = "DNS answer name sticky buffer";
+    sigmatch_table[DETECT_AL_DNS_ANSWER_NAME].url = "/rules/dns-keywords.html#dns-answer-name";
+    sigmatch_table[DETECT_AL_DNS_ANSWER_NAME].Setup = DetectSetup;
+    sigmatch_table[DETECT_AL_DNS_ANSWER_NAME].flags |= SIGMATCH_NOOPT;
+    sigmatch_table[DETECT_AL_DNS_ANSWER_NAME].flags |= SIGMATCH_INFO_STICKY_BUFFER;
+
+    /* Register in the TO_SERVER direction, even though this is not
+       normal, it could be provided as part of a request. */
+    DetectAppLayerInspectEngineRegister(
+            keyword, ALPROTO_DNS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectCb, NULL);
+    DetectAppLayerMpmRegister(
+            keyword, SIG_FLAG_TOSERVER, 2, PrefilterMpmRegister, NULL, ALPROTO_DNS, 1);
+
+    /* Register in the TO_CLIENT direction. */
+    DetectAppLayerInspectEngineRegister(
+            keyword, ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectCb, NULL);
+    DetectAppLayerMpmRegister(
+            keyword, SIG_FLAG_TOCLIENT, 2, PrefilterMpmRegister, NULL, ALPROTO_DNS, 1);
+
+    DetectBufferTypeSetDescriptionByName(keyword, "dns answer name");
+    DetectBufferTypeSupportsMultiInstance(keyword);
+
+    detect_buffer_id = DetectBufferTypeGetByName(keyword);
+}
diff --git a/src/detect-dns-answer-name.h b/src/detect-dns-answer-name.h
new file mode 100644 (file)
index 0000000..4f84b48
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2023 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_DNS_ANSWER_NAME_H__
+#define __DETECT_DNS_ANSWER_NAME_H__
+
+void DetectDnsAnswerNameRegister(void);
+
+#endif /* __DETECT_DNS_ANSWER_NAME_H__ */
index b27f5a511f9cfdb0a0a5ab30dad0cd258a759a49..258bbef3f5433b0c09149d8c8144803e8a00589a 100644 (file)
@@ -49,6 +49,7 @@
 #include "detect-engine-dcepayload.h"
 #include "detect-dns-opcode.h"
 #include "detect-dns-query.h"
+#include "detect-dns-answer-name.h"
 #include "detect-tls-sni.h"
 #include "detect-tls-certs.h"
 #include "detect-tls-cert-fingerprint.h"
@@ -517,6 +518,7 @@ void SigTableSetup(void)
 
     DetectDnsQueryRegister();
     DetectDnsOpcodeRegister();
+    DetectDnsAnswerNameRegister();
     DetectModbusRegister();
     DetectCipServiceRegister();
     DetectEnipCommandRegister();
index 273aa10d7c9b3807930ddd65263202a22fe98c0c..0a529954e953dd5e0c7e01dc2bcf2a759a844014 100644 (file)
@@ -227,6 +227,7 @@ enum DetectKeywordId {
 
     DETECT_AL_DNS_QUERY,
     DETECT_AL_DNS_OPCODE,
+    DETECT_AL_DNS_ANSWER_NAME,
     DETECT_AL_TLS_SNI,
     DETECT_AL_TLS_CERTS,
     DETECT_AL_TLS_CERT_ISSUER,