Skip to content

Commit

Permalink
dns: add dns.rcode keyword
Browse files Browse the repository at this point in the history
Feature #6621
It matches the rcode field in DNS
It's an unsigned integer match
valid ranges = [0-23]
Does not support prefilter
Supports flow in client direction
  • Loading branch information
hadiqaalamdar committed Jan 24, 2024
1 parent 3cb7112 commit 97cd65b
Show file tree
Hide file tree
Showing 8 changed files with 263 additions and 2 deletions.
35 changes: 35 additions & 0 deletions doc/userguide/rules/dns-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,41 @@ Match on DNS requests where the **opcode** is NOT 0::

dns.opcode:!0;

dns.rcode
---------

This keyword matches on the **rcode** found in the DNS header flags.
It uses an 8-bit unsigned integer as value.
It has a range of values from [0-23] which are assigned as shown here: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6

Syntax
~~~~~~

::

dns.rcode:[!]<number>
dns.rcode:[!]<number1>-<number2>

Examples
~~~~~~~~

Match on DNS requests and responses with **rcode** 4::

dns.rcode:4;

Match on DNS requests where the **rcode** is NOT 0::

dns.rcode:!0;

Match on DNS requests where the **rcode** is between 7 and 15, exclusive (7-15):

dns.rcode:7-15;

Match on DNS requests where the **rcode** is not between 7 and 15:

dns.rcode:!7-15;


dns.query
---------

Expand Down
1 change: 1 addition & 0 deletions rust/src/detect/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub enum DetectUintMode {

#[derive(Debug)]
#[repr(C)]
#[derive(PartialEq)]
pub struct DetectUintData<T> {
pub arg1: T,
pub arg2: T,
Expand Down
116 changes: 114 additions & 2 deletions rust/src/dns/detect.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2019 Open Information Security Foundation
/* Copyright (C) 2024 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
Expand All @@ -16,7 +16,8 @@
*/

use super::dns::DNSTransaction;
use crate::core::*;
use crate::core::Direction;
use crate::detect::uint::{detect_match_uint, DetectUintData};
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};

Expand Down Expand Up @@ -114,9 +115,42 @@ pub unsafe extern "C" fn rs_dns_detect_opcode_free(ptr: *mut c_void) {
}
}

/// Perform the DNS rcode match.
///
/// 1 will be returned on match, otherwise 0 will be returned.
#[no_mangle]
pub extern "C" fn rs_dns_rcode_match(
tx: &mut DNSTransaction, detect: &mut DetectUintData<u8>, flags: u8,
) -> u8 {
let header_flags = if flags & Direction::ToServer as u8 != 0 {
if let Some(request) = &tx.request {
request.header.flags
} else {
return 0;
}
} else if flags & Direction::ToClient as u8 != 0 {
if let Some(response) = &tx.response {
response.header.flags
} else {
return 0;
}
} else {
// Not to server or to client??
return 0;
};

let rcode = (header_flags & 0xf) as u8;

if detect_match_uint(detect, rcode) {
return 1;
}
return 0;
}

#[cfg(test)]
mod test {
use super::*;
use crate::detect::uint::{detect_parse_uint, DetectUintMode};

#[test]
fn parse_opcode_good() {
Expand Down Expand Up @@ -188,4 +222,82 @@ mod test {
0b0010_0000_0000_0000,
));
}
#[test]
fn parse_rcode_good() {
assert_eq!(
detect_parse_uint::<u8>("1").unwrap().1,
DetectUintData {
mode: DetectUintMode::DetectUintModeEqual,
arg1: 1,
arg2: 0,
}
);
assert_eq!(
detect_parse_uint::<u8>("123").unwrap().1,
DetectUintData {
mode: DetectUintMode::DetectUintModeEqual,
arg1: 123,
arg2: 0,
}
);
assert_eq!(
detect_parse_uint::<u8>("!123").unwrap().1,
DetectUintData {
mode: DetectUintMode::DetectUintModeNe,
arg1: 123,
arg2: 0,
}
);
assert_eq!(
detect_parse_uint::<u8>("7-15").unwrap().1,
DetectUintData {
mode: DetectUintMode::DetectUintModeRange,
arg1: 7,
arg2: 15,
}
);
assert!(detect_parse_uint::<u8>("").is_err());
assert!(detect_parse_uint::<u8>("!").is_err());
assert!(detect_parse_uint::<u8>("! ").is_err());
assert!(detect_parse_uint::<u8>("!asdf").is_err());
}

#[test]
fn test_match_rcode() {
assert!(detect_match_uint(
&DetectUintData {
mode: DetectUintMode::DetectUintModeEqual,
arg1: 0,
arg2: 0,
},
0b0000_0000_0000_0000,
));

assert!(!detect_match_uint(
&DetectUintData {
mode: DetectUintMode::DetectUintModeNe,
arg1: 0,
arg2: 0,
},
0b0000_0000_0000_0000,
));

assert!(detect_match_uint(
&DetectUintData {
mode: DetectUintMode::DetectUintModeEqual,
arg1: 4,
arg2: 0,
},
0b0000_0000_0000_0100 as u8,
));

assert!(!detect_match_uint(
&DetectUintData {
mode: DetectUintMode::DetectUintModeNe,
arg1: 4,
arg2: 0,
},
0b0000_0000_0000_0100 as u8,
));
}
}
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ noinst_HEADERS = \
detect-dnp3.h \
detect-dns-answer-name.h \
detect-dns-opcode.h \
detect-dns-rcode.h \
detect-dns-query.h \
detect-dns-query-name.h \
detect-dsize.h \
Expand Down Expand Up @@ -742,6 +743,7 @@ libsuricata_c_a_SOURCES = \
detect-dnp3.c \
detect-dns-answer-name.c \
detect-dns-opcode.c \
detect-dns-rcode.c \
detect-dns-query.c \
detect-dns-query-name.c \
detect-dsize.c \
Expand Down
85 changes: 85 additions & 0 deletions src/detect-dns-rcode.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* Copyright (C) 2024 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-rcode.h"
#include "rust.h"
#include "detect-engine-uint.h"

static int dns_rcode_list_id = 0;

static void DetectDnsRcodeFree(DetectEngineCtx *, void *ptr);

static int DetectDnsRcodeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
SCEnter();

if (DetectSignatureSetAppProto(s, ALPROTO_DNS) != 0) {
SCReturnInt(-1);
}

void *detect = DetectU8Parse(str);
if (detect == NULL) {
SCLogError("failed to parse dns.rcode: %s", str);
SCReturnInt(-1);
}

if (SigMatchAppendSMToList(
de_ctx, s, DETECT_AL_DNS_RCODE, (SigMatchCtx *)detect, dns_rcode_list_id) == NULL) {
DetectDnsRcodeFree(de_ctx, detect);
SCReturnInt(-1);
}

SCReturnInt(0);
}

static void DetectDnsRcodeFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCEnter();
if (ptr != NULL) {
rs_detect_u8_free(ptr);
}
SCReturn;
}

static int DetectDnsRcodeMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
void *txv, const Signature *s, const SigMatchCtx *ctx)
{
return rs_dns_rcode_match(txv, (void *)ctx, flags);
}

void DetectDnsRcodeRegister(void)
{
sigmatch_table[DETECT_AL_DNS_RCODE].name = "dns.rcode";
sigmatch_table[DETECT_AL_DNS_RCODE].desc = "Match the DNS header rcode flag.";
sigmatch_table[DETECT_AL_DNS_RCODE].url = "/rules/dns-keywords.html#dns-rcode";
sigmatch_table[DETECT_AL_DNS_RCODE].Setup = DetectDnsRcodeSetup;
sigmatch_table[DETECT_AL_DNS_RCODE].Free = DetectDnsRcodeFree;
sigmatch_table[DETECT_AL_DNS_RCODE].Match = NULL;
sigmatch_table[DETECT_AL_DNS_RCODE].AppLayerTxMatch = DetectDnsRcodeMatch;

DetectAppLayerInspectEngineRegister(
"dns.rcode", ALPROTO_DNS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);

DetectAppLayerInspectEngineRegister(
"dns.rcode", ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);

dns_rcode_list_id = DetectBufferTypeGetByName("dns.rcode");
}
23 changes: 23 additions & 0 deletions src/detect-dns-rcode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* Copyright (C) 2024 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_RCODE_H__
#define __DETECT_DNS_RCODE_H__

void DetectDnsRcodeRegister(void);

#endif /* __DETECT_DNS_RCODE_H__ */
2 changes: 2 additions & 0 deletions src/detect-engine-register.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "detect-engine-payload.h"
#include "detect-engine-dcepayload.h"
#include "detect-dns-opcode.h"
#include "detect-dns-rcode.h"
#include "detect-dns-query.h"
#include "detect-dns-answer-name.h"
#include "detect-dns-query-name.h"
Expand Down Expand Up @@ -522,6 +523,7 @@ void SigTableSetup(void)

DetectDnsQueryRegister();
DetectDnsOpcodeRegister();
DetectDnsRcodeRegister();
DetectDnsAnswerNameRegister();
DetectDnsQueryNameRegister();
DetectModbusRegister();
Expand Down
1 change: 1 addition & 0 deletions src/detect-engine-register.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ enum DetectKeywordId {

DETECT_AL_DNS_QUERY,
DETECT_AL_DNS_OPCODE,
DETECT_AL_DNS_RCODE,
DETECT_AL_DNS_ANSWER_NAME,
DETECT_AL_DNS_QUERY_NAME,
DETECT_AL_TLS_SNI,
Expand Down

0 comments on commit 97cd65b

Please sign in to comment.