]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
dns/detect: dns.opcode keyword
authorJason Ish <jason.ish@oisf.net>
Mon, 16 Sep 2019 05:43:14 +0000 (23:43 -0600)
committerJason Ish <jason.ish@oisf.net>
Mon, 16 Sep 2019 20:46:55 +0000 (14:46 -0600)
Add a rule keyword, dns.opcode to match on the opcode flag
found in the DNS request and response headers.

Only exact matches are allowed with negation.

Examples:
  - dns.opcode:4;
  - dns.opcode:!1;

rust/gen-c-headers.py
rust/src/dns/detect.rs [new file with mode: 0644]
rust/src/dns/mod.rs
src/Makefile.am
src/detect-dns-opcode.c [new file with mode: 0644]
src/detect-dns-opcode.h [new file with mode: 0644]
src/detect-engine-register.c
src/detect-engine-register.h

index 90caae8eca7b4cf0ac1e77ac93de72460a789107..ecf8b0fd90ab72a7c20408bceba754f35b46a605 100755 (executable)
@@ -61,6 +61,7 @@ type_map = {
     "c_void": "void",
 
     "std::os::raw::c_char": "char",
+    "c_char": "char",
     "std::os::raw::c_int": "int",
     "c_int": "int",
     "std::os::raw::int8_t": "int8_t",
@@ -104,6 +105,7 @@ type_map = {
     "CLuaState": "lua_State",
     "Store": "Store",
     "AppProto": "AppProto",
+    "DetectDnsOpcode": "void *",
 }
 
 def convert_type(rs_type):
diff --git a/rust/src/dns/detect.rs b/rust/src/dns/detect.rs
new file mode 100644 (file)
index 0000000..bbf3c71
--- /dev/null
@@ -0,0 +1,209 @@
+/* Copyright (C) 2019 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.
+ */
+
+use super::dns::DNSTransaction;
+use crate::core;
+use std::ffi::CStr;
+use std::os::raw::{c_char, c_void};
+
+#[derive(Debug, PartialEq)]
+pub struct DetectDnsOpcode {
+    negate: bool,
+    opcode: u8,
+}
+
+/// Parse a DNS opcode argument returning the code and if it is to be
+/// negated or not.
+///
+/// For now only an indication that an error occurred is returned, not
+/// the details of the error.
+fn parse_opcode(opcode: &str) -> Result<DetectDnsOpcode, ()> {
+    let mut negated = false;
+    for (i, c) in opcode.chars().enumerate() {
+        match c {
+            ' ' | '\t' => {
+                continue;
+            }
+            '!' => {
+                negated = true;
+            }
+            _ => {
+                let code: u8 = (&opcode[i..]).parse().or_else(|_| Err(()))?;
+                return Ok(DetectDnsOpcode {
+                    negate: negated,
+                    opcode: code,
+                });
+            }
+        }
+    }
+    Err(())
+}
+
+/// Perform the DNS opcode match.
+///
+/// 1 will be returned on match, otherwise 0 will be returned.
+#[no_mangle]
+pub extern "C" fn rs_dns_opcode_match(
+    tx: &mut DNSTransaction,
+    detect: &mut DetectDnsOpcode,
+    flags: u8,
+) -> u8 {
+    let header_flags = if flags & core::STREAM_TOSERVER != 0 {
+        if let Some(request) = &tx.request {
+            request.header.flags
+        } else {
+            return 0;
+        }
+    } else if flags & core::STREAM_TOCLIENT != 0 {
+        if let Some(response) = &tx.response {
+            response.header.flags
+        } else {
+            return 0;
+        }
+    } else {
+        // Not to server or to client??
+        return 0;
+    };
+
+    if match_opcode(detect, header_flags) {
+        1
+    } else {
+        0
+    }
+}
+
+fn match_opcode(detect: &DetectDnsOpcode, flags: u16) -> bool {
+    let opcode = ((flags >> 11) & 0xf) as u8;
+    if detect.negate {
+        detect.opcode != opcode
+    } else {
+        detect.opcode == opcode
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_detect_dns_opcode_parse(carg: *const c_char) -> *mut c_void {
+    if carg.is_null() {
+        return std::ptr::null_mut();
+    }
+    let arg = match CStr::from_ptr(carg).to_str() {
+        Ok(arg) => arg,
+        _ => {
+            return std::ptr::null_mut();
+        }
+    };
+
+    match parse_opcode(&arg) {
+        Ok(detect) => std::mem::transmute(Box::new(detect)),
+        Err(_) => std::ptr::null_mut(),
+    }
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rs_dns_detect_opcode_free(ptr: *mut c_void) {
+    if ptr != std::ptr::null_mut() {
+        let _: Box<DetectDnsOpcode> = std::mem::transmute(ptr);
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn parse_opcode_good() {
+        assert_eq!(
+            parse_opcode("1"),
+            Ok(DetectDnsOpcode {
+                negate: false,
+                opcode: 1
+            })
+        );
+        assert_eq!(
+            parse_opcode("123"),
+            Ok(DetectDnsOpcode {
+                negate: false,
+                opcode: 123
+            })
+        );
+        assert_eq!(
+            parse_opcode("!123"),
+            Ok(DetectDnsOpcode {
+                negate: true,
+                opcode: 123
+            })
+        );
+        assert_eq!(
+            parse_opcode("!123"),
+            Ok(DetectDnsOpcode {
+                negate: true,
+                opcode: 123
+            })
+        );
+        assert_eq!(parse_opcode(""), Err(()));
+        assert_eq!(parse_opcode("!"), Err(()));
+        assert_eq!(parse_opcode("!   "), Err(()));
+        assert_eq!(parse_opcode("!asdf"), Err(()));
+    }
+
+    #[test]
+    fn test_match_opcode() {
+        assert_eq!(
+            match_opcode(
+                &DetectDnsOpcode {
+                    negate: false,
+                    opcode: 0,
+                },
+                0b0000_0000_0000_0000,
+            ),
+            true
+        );
+
+        assert_eq!(
+            match_opcode(
+                &DetectDnsOpcode {
+                    negate: true,
+                    opcode: 0,
+                },
+                0b0000_0000_0000_0000,
+            ),
+            false
+        );
+
+        assert_eq!(
+            match_opcode(
+                &DetectDnsOpcode {
+                    negate: false,
+                    opcode: 4,
+                },
+                0b0010_0000_0000_0000,
+            ),
+            true
+        );
+
+        assert_eq!(
+            match_opcode(
+                &DetectDnsOpcode {
+                    negate: true,
+                    opcode: 4,
+                },
+                0b0010_0000_0000_0000,
+            ),
+            false
+        );
+    }
+}
index 5da27124fbc95da4551cb0ea943da33000764af3..685adcfa3c6bf3e49a5085a3aa2f23a2c22ec016 100644 (file)
@@ -18,6 +18,7 @@
 pub mod parser;
 pub mod dns;
 pub mod log;
+pub mod detect;
 
 #[cfg(feature = "lua")]
 pub mod lua;
index 587fa492cccbcd16d28bd194f41fa8c44167071d..38d116bbc3fceab85391cd31b1d1429a4b2fbb4b 100644 (file)
@@ -112,6 +112,7 @@ detect-depth.c detect-depth.h \
 detect-detection-filter.c detect-detection-filter.h \
 detect-distance.c detect-distance.h \
 detect-dnp3.c detect-dnp3.h \
+detect-dns-opcode.c detect-dns-opcode.h \
 detect-dns-query.c detect-dns-query.h \
 detect-tls-ja3-hash.c detect-tls-ja3-hash.h \
 detect-tls-ja3-string.c detect-tls-ja3-string.h \
diff --git a/src/detect-dns-opcode.c b/src/detect-dns-opcode.c
new file mode 100644 (file)
index 0000000..1d90b06
--- /dev/null
@@ -0,0 +1,106 @@
+/* Copyright (C) 2019 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-parse.h"
+#include "detect-engine.h"
+#include "detect-dns-opcode.h"
+#include "app-layer-dns-common.h"
+#include "rust-dns-detect-gen.h"
+
+static int dns_opcode_list_id = 0;
+
+static void DetectDnsOpcodeFree(void *ptr);
+
+static int DetectDnsOpcodeSetup(DetectEngineCtx *de_ctx, Signature *s,
+   const char *str)
+{
+    SCEnter();
+
+    if (DetectSignatureSetAppProto(s, ALPROTO_DNS) != 0) {
+        return -1;
+    }
+
+    void *detect = rs_detect_dns_opcode_parse(str);
+    if (detect == NULL) {
+        SCLogNotice("Failed to parse dns.opcode: %s", str);
+        return -1;
+    }
+
+    SigMatch *sm = SigMatchAlloc();
+    if (unlikely(sm == NULL)) {
+        goto error;
+    }
+
+    sm->type = DETECT_AL_DNS_OPCODE;
+    sm->ctx = (void *)detect;
+    SigMatchAppendSMToList(s, sm, dns_opcode_list_id);
+    
+    SCReturnInt(0);
+
+error:
+    DetectDnsOpcodeFree(detect);
+    SCReturnInt(-1);
+}
+
+static void DetectDnsOpcodeFree(void *ptr)
+{
+    SCEnter();
+    if (ptr != NULL) {
+        rs_dns_detect_opcode_free(ptr);
+    }
+    SCReturn;
+}
+
+static int DetectDnsOpcodeMatch(DetectEngineThreadCtx *det_ctx,
+    Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
+    const SigMatchCtx *ctx)
+{
+    return rs_dns_opcode_match(txv, (void *)ctx, flags);
+}
+
+static int DetectEngineInspectRequestGenericDnsOpcode(ThreadVars *tv,
+        DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+        const Signature *s, const SigMatchData *smd,
+        Flow *f, uint8_t flags, void *alstate,
+        void *txv, uint64_t tx_id)
+{
+    return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
+       f, flags, alstate, txv, tx_id);
+}
+
+void DetectDnsOpcodeRegister(void)
+{
+    sigmatch_table[DETECT_AL_DNS_OPCODE].name  = "dns.opcode";
+    sigmatch_table[DETECT_AL_DNS_OPCODE].desc  = "Match the DNS header opcode flag.";
+    sigmatch_table[DETECT_AL_DNS_OPCODE].Setup = DetectDnsOpcodeSetup;
+    sigmatch_table[DETECT_AL_DNS_OPCODE].Free  = DetectDnsOpcodeFree;
+    sigmatch_table[DETECT_AL_DNS_OPCODE].Match = NULL;
+    sigmatch_table[DETECT_AL_DNS_OPCODE].AppLayerTxMatch =
+        DetectDnsOpcodeMatch;
+
+    DetectAppLayerInspectEngineRegister("dns.opcode",
+            ALPROTO_DNS, SIG_FLAG_TOSERVER, 0,
+            DetectEngineInspectRequestGenericDnsOpcode);
+
+    DetectAppLayerInspectEngineRegister("dns.opcode",
+            ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0,
+            DetectEngineInspectRequestGenericDnsOpcode);
+
+    dns_opcode_list_id = DetectBufferTypeGetByName("dns.opcode");
+}
diff --git a/src/detect-dns-opcode.h b/src/detect-dns-opcode.h
new file mode 100644 (file)
index 0000000..0518a58
--- /dev/null
@@ -0,0 +1,23 @@
+/* Copyright (C) 2019 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_OPCODE_H__
+#define __DETECT_DNS_OPCODE_H__
+
+void DetectDnsOpcodeRegister(void);
+
+#endif /* __DETECT_DNS_OPCODE_H__ */
index 294a4b7d76f0d5ea776a633e010d92fee1b12881..7a2cd382abbf0893e28e1c6b7a37ccab735c554c 100644 (file)
@@ -45,6 +45,7 @@
 
 #include "detect-engine-payload.h"
 #include "detect-engine-dcepayload.h"
+#include "detect-dns-opcode.h"
 #include "detect-dns-query.h"
 #include "detect-tls-sni.h"
 #include "detect-tls-certs.h"
@@ -436,6 +437,7 @@ void SigTableSetup(void)
     DetectHttpStatCodeRegister();
 
     DetectDnsQueryRegister();
+    DetectDnsOpcodeRegister();
     DetectModbusRegister();
     DetectCipServiceRegister();
     DetectEnipCommandRegister();
index 7cc05f33c35b1b3f6337555e21f782bcb67fc62e..4c3e5ff6d53100568bef6e089a0b5fa4a8601754 100644 (file)
@@ -195,6 +195,7 @@ enum {
     DETECT_IPREP,
 
     DETECT_AL_DNS_QUERY,
+    DETECT_AL_DNS_OPCODE,
     DETECT_AL_TLS_SNI,
     DETECT_AL_TLS_CERTS,
     DETECT_AL_TLS_CERT_ISSUER,