mirror of https://github.com/OISF/suricata
quic: Add QUIC App Layer
Parses quic and logs a CYU hash for gquic framespull/6819/head
parent
f714484591
commit
7e51987263
@ -0,0 +1,195 @@
|
|||||||
|
/* Copyright (C) 2021 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::{
|
||||||
|
frames::Frame,
|
||||||
|
parser::{QuicHeader, QuicVersion},
|
||||||
|
};
|
||||||
|
use md5::{Digest, Md5};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct Cyu {
|
||||||
|
pub string: String,
|
||||||
|
pub hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cyu {
|
||||||
|
pub(crate) fn new(string: String, hash: String) -> Self {
|
||||||
|
Self { string, hash }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generate(header: &QuicHeader, frames: &[Frame]) -> Vec<Cyu> {
|
||||||
|
let version = match header.version {
|
||||||
|
QuicVersion::Q043 => Some("43"),
|
||||||
|
QuicVersion::Q044 => Some("44"),
|
||||||
|
QuicVersion::Q045 => Some("44"),
|
||||||
|
QuicVersion::Q046 => Some("46"),
|
||||||
|
_ => {
|
||||||
|
SCLogDebug!(
|
||||||
|
"Cannot match QUIC version {:?} to CYU version",
|
||||||
|
header.version
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cyu_hashes = Vec::new();
|
||||||
|
|
||||||
|
if let Some(version) = version {
|
||||||
|
for frame in frames {
|
||||||
|
if let Frame::Stream(stream) = frame {
|
||||||
|
if let Some(tags) = &stream.tags {
|
||||||
|
let tags = tags
|
||||||
|
.iter()
|
||||||
|
.map(|(tag, _value)| tag.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("-");
|
||||||
|
|
||||||
|
let cyu_string = format!("{},{}", version, tags);
|
||||||
|
|
||||||
|
let mut hasher = Md5::new();
|
||||||
|
hasher.update(&cyu_string.as_bytes());
|
||||||
|
let hash = hasher.finalize();
|
||||||
|
|
||||||
|
let cyu_hash = format!("{:x}", hash);
|
||||||
|
|
||||||
|
cyu_hashes.push(Cyu::new(cyu_string, cyu_hash));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cyu_hashes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::quic::frames::{Frame, Stream, StreamTag};
|
||||||
|
use crate::quic::parser::{PublicFlags, QuicType};
|
||||||
|
use test_case::test_case;
|
||||||
|
|
||||||
|
macro_rules! mock_header_and_frames {
|
||||||
|
($version:expr, $($variants:expr),+) => {{
|
||||||
|
let header = QuicHeader::new(
|
||||||
|
PublicFlags::new(0x80),
|
||||||
|
QuicType::Initial,
|
||||||
|
$version,
|
||||||
|
vec![],
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
|
||||||
|
let frames = vec![
|
||||||
|
Frame::Stream(Stream {
|
||||||
|
fin: false,
|
||||||
|
stream_id: vec![],
|
||||||
|
offset: vec![],
|
||||||
|
tags: Some(vec![$(($variants, vec![])),*])
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
(header, frames)
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salesforce tests here:
|
||||||
|
// https://engineering.salesforce.com/gquic-protocol-analysis-and-fingerprinting-in-zeek-a4178855d75f
|
||||||
|
#[test_case(
|
||||||
|
mock_header_and_frames!(
|
||||||
|
// version
|
||||||
|
QuicVersion::Q046,
|
||||||
|
// tags
|
||||||
|
StreamTag::Pad, StreamTag::Sni,
|
||||||
|
StreamTag::Stk, StreamTag::Ver,
|
||||||
|
StreamTag::Ccs, StreamTag::Nonc,
|
||||||
|
StreamTag::Aead, StreamTag::Uaid,
|
||||||
|
StreamTag::Scid, StreamTag::Tcid,
|
||||||
|
StreamTag::Pdmd, StreamTag::Smhl,
|
||||||
|
StreamTag::Icsl, StreamTag::Nonp,
|
||||||
|
StreamTag::Pubs, StreamTag::Mids,
|
||||||
|
StreamTag::Scls, StreamTag::Kexs,
|
||||||
|
StreamTag::Xlct, StreamTag::Csct,
|
||||||
|
StreamTag::Copt, StreamTag::Ccrt,
|
||||||
|
StreamTag::Irtt, StreamTag::Cfcw,
|
||||||
|
StreamTag::Sfcw
|
||||||
|
),
|
||||||
|
Cyu {
|
||||||
|
string: "46,PAD-SNI-STK-VER-CCS-NONC-AEAD-UAID-SCID-TCID-PDMD-SMHL-ICSL-NONP-PUBS-MIDS-SCLS-KEXS-XLCT-CSCT-COPT-CCRT-IRTT-CFCW-SFCW".to_string(),
|
||||||
|
hash: "a46560d4548108cf99308319b3b85346".to_string(),
|
||||||
|
}; "test cyu 1"
|
||||||
|
)]
|
||||||
|
#[test_case(
|
||||||
|
mock_header_and_frames!(
|
||||||
|
// version
|
||||||
|
QuicVersion::Q043,
|
||||||
|
// tags
|
||||||
|
StreamTag::Pad, StreamTag::Sni,
|
||||||
|
StreamTag::Ver, StreamTag::Ccs,
|
||||||
|
StreamTag::Pdmd, StreamTag::Icsl,
|
||||||
|
StreamTag::Mids, StreamTag::Cfcw,
|
||||||
|
StreamTag::Sfcw
|
||||||
|
),
|
||||||
|
Cyu {
|
||||||
|
string: "43,PAD-SNI-VER-CCS-PDMD-ICSL-MIDS-CFCW-SFCW".to_string(),
|
||||||
|
hash: "e030dea1f2eea44ac7db5fe4de792acd".to_string(),
|
||||||
|
}; "test cyu 2"
|
||||||
|
)]
|
||||||
|
#[test_case(
|
||||||
|
mock_header_and_frames!(
|
||||||
|
// version
|
||||||
|
QuicVersion::Q043,
|
||||||
|
// tags
|
||||||
|
StreamTag::Pad, StreamTag::Sni,
|
||||||
|
StreamTag::Stk, StreamTag::Ver,
|
||||||
|
StreamTag::Ccs, StreamTag::Scid,
|
||||||
|
StreamTag::Pdmd, StreamTag::Icsl,
|
||||||
|
StreamTag::Mids, StreamTag::Cfcw,
|
||||||
|
StreamTag::Sfcw
|
||||||
|
),
|
||||||
|
Cyu {
|
||||||
|
string: "43,PAD-SNI-STK-VER-CCS-SCID-PDMD-ICSL-MIDS-CFCW-SFCW".to_string(),
|
||||||
|
hash: "0811fab28e41e8c8a33e220a15b964d9".to_string(),
|
||||||
|
}; "test cyu 3"
|
||||||
|
)]
|
||||||
|
#[test_case(
|
||||||
|
mock_header_and_frames!(
|
||||||
|
// version
|
||||||
|
QuicVersion::Q043,
|
||||||
|
// tags
|
||||||
|
StreamTag::Pad, StreamTag::Sni,
|
||||||
|
StreamTag::Stk, StreamTag::Ver,
|
||||||
|
StreamTag::Ccs, StreamTag::Nonc,
|
||||||
|
StreamTag::Aead, StreamTag::Scid,
|
||||||
|
StreamTag::Pdmd, StreamTag::Icsl,
|
||||||
|
StreamTag::Pubs, StreamTag::Mids,
|
||||||
|
StreamTag::Kexs, StreamTag::Xlct,
|
||||||
|
StreamTag::Cfcw, StreamTag::Sfcw
|
||||||
|
),
|
||||||
|
Cyu {
|
||||||
|
string: "43,PAD-SNI-STK-VER-CCS-NONC-AEAD-SCID-PDMD-ICSL-PUBS-MIDS-KEXS-XLCT-CFCW-SFCW".to_string(),
|
||||||
|
hash: "d8b208b236d176c89407500dbefb04c2".to_string(),
|
||||||
|
}; "test cyu 4"
|
||||||
|
)]
|
||||||
|
fn test_cyu_generate(input: (QuicHeader, Vec<Frame>), expected: Cyu) {
|
||||||
|
let (header, frames) = input;
|
||||||
|
|
||||||
|
let cyu = Cyu::generate(&header, &frames);
|
||||||
|
assert_eq!(1, cyu.len());
|
||||||
|
assert_eq!(expected, cyu[0]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/* Copyright (C) 2021 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 crate::quic::quic::{QuicTransaction};
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_tx_get_cyu_hash(
|
||||||
|
tx: &QuicTransaction, i: u32, buffer: *mut *const u8, buffer_len: *mut u32,
|
||||||
|
) -> u8 {
|
||||||
|
if (i as usize) < tx.cyu.len() {
|
||||||
|
let cyu = &tx.cyu[i as usize];
|
||||||
|
|
||||||
|
let p = &cyu.hash;
|
||||||
|
|
||||||
|
*buffer = p.as_ptr();
|
||||||
|
*buffer_len = p.len() as u32;
|
||||||
|
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
*buffer = ptr::null();
|
||||||
|
*buffer_len = 0;
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_tx_get_cyu_string(
|
||||||
|
tx: &QuicTransaction, i: u32, buffer: *mut *const u8, buffer_len: *mut u32,
|
||||||
|
) -> u8 {
|
||||||
|
if (i as usize) < tx.cyu.len() {
|
||||||
|
let cyu = &tx.cyu[i as usize];
|
||||||
|
|
||||||
|
let p = &cyu.string;
|
||||||
|
|
||||||
|
*buffer = p.as_ptr();
|
||||||
|
*buffer_len = p.len() as u32;
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
*buffer = ptr::null();
|
||||||
|
*buffer_len = 0;
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_quic_tx_get_version(tx: &QuicTransaction) -> u32 {
|
||||||
|
tx.header.version.into()
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/* Copyright (C) 2021 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 nom::error::{ErrorKind, ParseError};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum QuicError {
|
||||||
|
StreamTagNoMatch(u32),
|
||||||
|
InvalidPacket,
|
||||||
|
Incomplete,
|
||||||
|
NomError(ErrorKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ParseError<I> for QuicError {
|
||||||
|
fn from_error_kind(_input: I, kind: ErrorKind) -> Self {
|
||||||
|
QuicError::NomError(kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append(_input: I, kind: ErrorKind, _other: Self) -> Self {
|
||||||
|
QuicError::NomError(kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for QuicError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
QuicError::StreamTagNoMatch(tag) => {
|
||||||
|
write!(f, "Could not match stream tag: 0x{:x}", tag)
|
||||||
|
}
|
||||||
|
QuicError::Incomplete => write!(f, "Incomplete data"),
|
||||||
|
QuicError::InvalidPacket => write!(f, "Invalid packet"),
|
||||||
|
QuicError::NomError(e) => write!(f, "Internal parser error {:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for QuicError {}
|
||||||
|
|
||||||
|
impl From<nom::Err<QuicError>> for QuicError {
|
||||||
|
fn from(err: nom::Err<QuicError>) -> Self {
|
||||||
|
match err {
|
||||||
|
nom::Err::Incomplete(_) => QuicError::Incomplete,
|
||||||
|
nom::Err::Error(e) => e,
|
||||||
|
nom::Err::Failure(e) => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,236 @@
|
|||||||
|
/* Copyright (C) 2021 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::error::QuicError;
|
||||||
|
use nom::bytes::complete::take;
|
||||||
|
use nom::combinator::{all_consuming, complete};
|
||||||
|
use nom::multi::{count, many0};
|
||||||
|
use nom::number::complete::{be_u16, be_u32, be_u8, le_u16, le_u32};
|
||||||
|
use nom::sequence::pair;
|
||||||
|
use nom::IResult;
|
||||||
|
use num::FromPrimitive;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Tuple of StreamTag and offset
|
||||||
|
type TagOffset = (StreamTag, u32);
|
||||||
|
|
||||||
|
/// Tuple of StreamTag and value
|
||||||
|
type TagValue = (StreamTag, Vec<u8>);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) struct Stream {
|
||||||
|
pub fin: bool,
|
||||||
|
pub stream_id: Vec<u8>,
|
||||||
|
pub offset: Vec<u8>,
|
||||||
|
pub tags: Option<Vec<TagValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy, FromPrimitive)]
|
||||||
|
pub(crate) enum StreamTag {
|
||||||
|
Aead = 0x41454144,
|
||||||
|
Ccrt = 0x43435254,
|
||||||
|
Ccs = 0x43435300,
|
||||||
|
Cetv = 0x43455456,
|
||||||
|
Cfcw = 0x43464357,
|
||||||
|
Chlo = 0x43484c4f,
|
||||||
|
Copt = 0x434f5054,
|
||||||
|
Csct = 0x43534354,
|
||||||
|
Ctim = 0x4354494d,
|
||||||
|
Icsl = 0x4943534c,
|
||||||
|
Irtt = 0x49525454,
|
||||||
|
Kexs = 0x4b455853,
|
||||||
|
Mids = 0x4d494453,
|
||||||
|
Mspc = 0x4d535043,
|
||||||
|
Nonc = 0x4e4f4e43,
|
||||||
|
Nonp = 0x4e4f4e50,
|
||||||
|
Pad = 0x50414400,
|
||||||
|
Pdmd = 0x50444d44,
|
||||||
|
Pubs = 0x50554253,
|
||||||
|
Scid = 0x53434944,
|
||||||
|
Scls = 0x53434c53,
|
||||||
|
Sfcw = 0x53464357,
|
||||||
|
Smhl = 0x534d484c,
|
||||||
|
Sni = 0x534e4900,
|
||||||
|
Sno = 0x534e4f00,
|
||||||
|
Stk = 0x53544b00,
|
||||||
|
Tcid = 0x54434944,
|
||||||
|
Uaid = 0x55414944,
|
||||||
|
Ver = 0x56455200,
|
||||||
|
Xlct = 0x584c4354,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for StreamTag {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
StreamTag::Aead => "AEAD",
|
||||||
|
StreamTag::Ccrt => "CCRT",
|
||||||
|
StreamTag::Ccs => "CCS",
|
||||||
|
StreamTag::Cetv => "CETV",
|
||||||
|
StreamTag::Cfcw => "CFCW",
|
||||||
|
StreamTag::Chlo => "CHLO",
|
||||||
|
StreamTag::Copt => "COPT",
|
||||||
|
StreamTag::Csct => "CSCT",
|
||||||
|
StreamTag::Ctim => "CTIM",
|
||||||
|
StreamTag::Icsl => "ICSL",
|
||||||
|
StreamTag::Irtt => "IRTT",
|
||||||
|
StreamTag::Kexs => "KEXS",
|
||||||
|
StreamTag::Mids => "MIDS",
|
||||||
|
StreamTag::Mspc => "MSPC",
|
||||||
|
StreamTag::Nonc => "NONC",
|
||||||
|
StreamTag::Nonp => "NONP",
|
||||||
|
StreamTag::Pad => "PAD",
|
||||||
|
StreamTag::Pdmd => "PDMD",
|
||||||
|
StreamTag::Pubs => "PUBS",
|
||||||
|
StreamTag::Scid => "SCID",
|
||||||
|
StreamTag::Scls => "SCLS",
|
||||||
|
StreamTag::Sfcw => "SFCW",
|
||||||
|
StreamTag::Smhl => "SMHL",
|
||||||
|
StreamTag::Sni => "SNI",
|
||||||
|
StreamTag::Sno => "SNO",
|
||||||
|
StreamTag::Stk => "STK",
|
||||||
|
StreamTag::Tcid => "TCID",
|
||||||
|
StreamTag::Uaid => "UAID",
|
||||||
|
StreamTag::Ver => "VER",
|
||||||
|
StreamTag::Xlct => "XLCT",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) enum Frame {
|
||||||
|
Padding,
|
||||||
|
Stream(Stream),
|
||||||
|
Unknown(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_tag(input: &[u8]) -> IResult<&[u8], StreamTag, QuicError> {
|
||||||
|
let (rest, tag) = be_u32(input)?;
|
||||||
|
|
||||||
|
let tag = StreamTag::from_u32(tag).ok_or(nom::Err::Error(QuicError::StreamTagNoMatch(tag)))?;
|
||||||
|
|
||||||
|
Ok((rest, tag))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_tag_and_offset(input: &[u8]) -> IResult<&[u8], TagOffset, QuicError> {
|
||||||
|
pair(parse_tag, le_u32)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_crypto_stream(input: &[u8]) -> IResult<&[u8], Vec<TagValue>, QuicError> {
|
||||||
|
// [message tag][number of tag entries: N][pad][[tag][end offset], ...N][value data]
|
||||||
|
let (rest, _message_tag) = parse_tag(input)?;
|
||||||
|
|
||||||
|
let (rest, num_entries) = le_u16(rest)?;
|
||||||
|
let (rest, _padding) = take(2usize)(rest)?;
|
||||||
|
|
||||||
|
let (rest, tags_offset) = count(complete(parse_tag_and_offset), num_entries.into())(rest)?;
|
||||||
|
|
||||||
|
// Convert (Tag, Offset) to (Tag, Value)
|
||||||
|
let mut tags = Vec::new();
|
||||||
|
let mut previous_offset = 0;
|
||||||
|
let mut rest = rest;
|
||||||
|
for (tag, offset) in tags_offset {
|
||||||
|
// offsets should be increasing
|
||||||
|
let value_len = offset
|
||||||
|
.checked_sub(previous_offset)
|
||||||
|
.ok_or(nom::Err::Error(QuicError::InvalidPacket))?;
|
||||||
|
let (new_rest, value) = take(value_len)(rest)?;
|
||||||
|
|
||||||
|
previous_offset = offset;
|
||||||
|
rest = new_rest;
|
||||||
|
|
||||||
|
tags.push((tag, value.to_vec()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((rest, tags))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_stream_frame(input: &[u8], frame_ty: u8) -> IResult<&[u8], Frame, QuicError> {
|
||||||
|
let rest = input;
|
||||||
|
|
||||||
|
// 0b1_f_d_ooo_ss
|
||||||
|
let fin = frame_ty & 0x40 == 0x40;
|
||||||
|
let has_data_length = frame_ty & 0x20 == 0x20;
|
||||||
|
|
||||||
|
let offset_hdr_length = {
|
||||||
|
let mut offset_length = (frame_ty & 0x1c) >> 2;
|
||||||
|
if offset_length != 0 {
|
||||||
|
offset_length += 1;
|
||||||
|
}
|
||||||
|
offset_length
|
||||||
|
};
|
||||||
|
|
||||||
|
let stream_id_hdr_length = usize::from((frame_ty & 0x03) + 1);
|
||||||
|
|
||||||
|
let (rest, stream_id) = take(stream_id_hdr_length)(rest)?;
|
||||||
|
let (rest, offset) = take(offset_hdr_length)(rest)?;
|
||||||
|
|
||||||
|
let (rest, data_length) = if has_data_length {
|
||||||
|
let (rest, data_length) = be_u16(rest)?;
|
||||||
|
|
||||||
|
(rest, usize::from(data_length))
|
||||||
|
} else {
|
||||||
|
(rest, rest.len())
|
||||||
|
};
|
||||||
|
|
||||||
|
let (rest, stream_data) = take(data_length)(rest)?;
|
||||||
|
|
||||||
|
let tags = if let Ok((_, tags)) = all_consuming(parse_crypto_stream)(stream_data) {
|
||||||
|
Some(tags)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Frame::Stream(Stream {
|
||||||
|
fin,
|
||||||
|
stream_id: stream_id.to_vec(),
|
||||||
|
offset: offset.to_vec(),
|
||||||
|
tags,
|
||||||
|
}),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame {
|
||||||
|
fn decode_frame(input: &[u8]) -> IResult<&[u8], Frame, QuicError> {
|
||||||
|
let (rest, frame_ty) = be_u8(input)?;
|
||||||
|
|
||||||
|
// Special frame types
|
||||||
|
let (rest, value) = if frame_ty & 0x80 == 0x80 {
|
||||||
|
// STREAM
|
||||||
|
parse_stream_frame(rest, frame_ty)?
|
||||||
|
} else {
|
||||||
|
match frame_ty {
|
||||||
|
0x00 => (rest, Frame::Padding),
|
||||||
|
_ => ([].as_ref(), Frame::Unknown(rest.to_vec())),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((rest, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn decode_frames(input: &[u8]) -> IResult<&[u8], Vec<Frame>, QuicError> {
|
||||||
|
let (rest, frames) = many0(complete(Frame::decode_frame))(input)?;
|
||||||
|
|
||||||
|
Ok((rest, frames))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/* Copyright (C) 2021 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::quic::QuicTransaction;
|
||||||
|
use crate::jsonbuilder::{JsonBuilder, JsonError};
|
||||||
|
|
||||||
|
fn log_template(tx: &QuicTransaction, js: &mut JsonBuilder) -> Result<(), JsonError> {
|
||||||
|
js.open_object("quic")?;
|
||||||
|
js.set_uint("version", u32::from(tx.header.version).into())?;
|
||||||
|
|
||||||
|
js.open_array("cyu")?;
|
||||||
|
for cyu in &tx.cyu {
|
||||||
|
js.start_object()?;
|
||||||
|
js.set_string("hash", &cyu.hash)?;
|
||||||
|
js.set_string("string", &cyu.string)?;
|
||||||
|
js.close()?;
|
||||||
|
}
|
||||||
|
js.close()?;
|
||||||
|
|
||||||
|
js.close()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_to_json(
|
||||||
|
tx: *mut std::os::raw::c_void, js: &mut JsonBuilder,
|
||||||
|
) -> bool {
|
||||||
|
let tx = cast_pointer!(tx, QuicTransaction);
|
||||||
|
log_template(tx, js).is_ok()
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
/* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mod cyu;
|
||||||
|
pub mod detect;
|
||||||
|
mod error;
|
||||||
|
mod frames;
|
||||||
|
mod logger;
|
||||||
|
mod parser;
|
||||||
|
pub mod quic;
|
@ -0,0 +1,316 @@
|
|||||||
|
/* Copyright (C) 2021 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::error::QuicError;
|
||||||
|
use super::frames::Frame;
|
||||||
|
use nom::bytes::complete::take;
|
||||||
|
use nom::combinator::{all_consuming, map};
|
||||||
|
use nom::number::complete::{be_u32, be_u8};
|
||||||
|
use nom::IResult;
|
||||||
|
|
||||||
|
/*
|
||||||
|
gQUIC is the Google version of QUIC.
|
||||||
|
|
||||||
|
The following docs were referenced when writing this parser
|
||||||
|
|
||||||
|
References:
|
||||||
|
- https://docs.google.com/document/d/1WJvyZflAO2pq77yOLbp9NsGjC1CHetAXV8I0fQe-B_U/edit
|
||||||
|
- https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/edit
|
||||||
|
- https://www.slideshare.net/shigeki_ohtsu/quic-overview
|
||||||
|
- https://tools.ietf.org/html/draft-ietf-quic-transport-17#section-19.8
|
||||||
|
- https://github.com/salesforce/GQUIC_Protocol_Analyzer/blob/master/src/gquic-protocol.pac
|
||||||
|
*/
|
||||||
|
|
||||||
|
// List of accepted and tested quic versions format
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct QuicVersion(pub u32);
|
||||||
|
|
||||||
|
impl QuicVersion {
|
||||||
|
pub const Q043: QuicVersion = QuicVersion(0x51303433);
|
||||||
|
pub const Q044: QuicVersion = QuicVersion(0x51303434);
|
||||||
|
pub const Q045: QuicVersion = QuicVersion(0x51303435);
|
||||||
|
pub const Q046: QuicVersion = QuicVersion(0x51303436);
|
||||||
|
|
||||||
|
fn is_gquic(&self) -> bool {
|
||||||
|
*self == QuicVersion::Q043
|
||||||
|
|| *self == QuicVersion::Q044
|
||||||
|
|| *self == QuicVersion::Q045
|
||||||
|
|| *self == QuicVersion::Q046
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<QuicVersion> for u32 {
|
||||||
|
fn from(from: QuicVersion) -> Self {
|
||||||
|
from.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for QuicVersion {
|
||||||
|
fn from(from: u32) -> Self {
|
||||||
|
QuicVersion(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum QuicType {
|
||||||
|
Initial,
|
||||||
|
Retry,
|
||||||
|
Handshake,
|
||||||
|
ZeroRTT,
|
||||||
|
VersionNegotiation,
|
||||||
|
Short,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct PublicFlags {
|
||||||
|
is_long: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PublicFlags {
|
||||||
|
pub fn new(value: u8) -> Self {
|
||||||
|
let is_long = value & 0x80 == 0x80;
|
||||||
|
|
||||||
|
PublicFlags { is_long }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A QUIC packet's header.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct QuicHeader {
|
||||||
|
pub flags: PublicFlags,
|
||||||
|
pub ty: QuicType,
|
||||||
|
pub version: QuicVersion,
|
||||||
|
pub dcid: Vec<u8>,
|
||||||
|
pub scid: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub(crate) struct QuicData {
|
||||||
|
pub frames: Vec<Frame>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuicHeader {
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn new(
|
||||||
|
flags: PublicFlags, ty: QuicType, version: QuicVersion, dcid: Vec<u8>, scid: Vec<u8>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
flags,
|
||||||
|
ty,
|
||||||
|
version,
|
||||||
|
dcid,
|
||||||
|
scid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_bytes(
|
||||||
|
input: &[u8], dcid_len: usize,
|
||||||
|
) -> IResult<&[u8], QuicHeader, QuicError> {
|
||||||
|
let (rest, first) = be_u8(input)?;
|
||||||
|
let flags = PublicFlags::new(first);
|
||||||
|
|
||||||
|
if !flags.is_long {
|
||||||
|
// Decode short header
|
||||||
|
let (rest, dcid) = take(dcid_len)(rest)?;
|
||||||
|
|
||||||
|
return Ok((
|
||||||
|
rest,
|
||||||
|
QuicHeader {
|
||||||
|
flags,
|
||||||
|
ty: QuicType::Short,
|
||||||
|
version: QuicVersion(0),
|
||||||
|
dcid: dcid.to_vec(),
|
||||||
|
scid: Vec::new(),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
// Decode Long header
|
||||||
|
let (rest, version) = map(be_u32, QuicVersion)(rest)?;
|
||||||
|
|
||||||
|
let ty = if version == QuicVersion(0) {
|
||||||
|
QuicType::VersionNegotiation
|
||||||
|
} else {
|
||||||
|
// Q046 is when they started using IETF
|
||||||
|
if version.is_gquic() && version != QuicVersion::Q046 {
|
||||||
|
match first & 0x7f {
|
||||||
|
0x7f => QuicType::Initial,
|
||||||
|
0x7e => QuicType::Retry,
|
||||||
|
0x7d => QuicType::Handshake,
|
||||||
|
0x7c => QuicType::ZeroRTT,
|
||||||
|
_ => {
|
||||||
|
return Err(nom::Err::Error(QuicError::InvalidPacket));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match (first & 0x30) >> 4 {
|
||||||
|
0x00 => QuicType::Initial,
|
||||||
|
0x01 => QuicType::ZeroRTT,
|
||||||
|
0x02 => QuicType::Handshake,
|
||||||
|
0x03 => QuicType::Retry,
|
||||||
|
_ => {
|
||||||
|
return Err(nom::Err::Error(QuicError::InvalidPacket));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (rest, dcid, scid) = if version.is_gquic() {
|
||||||
|
// [DCID_LEN (4)][SCID_LEN (4)]
|
||||||
|
let (rest, lengths) = be_u8(rest)?;
|
||||||
|
|
||||||
|
let mut dcid_len = (lengths & 0xF0) >> 4;
|
||||||
|
let mut scid_len = lengths & 0x0F;
|
||||||
|
|
||||||
|
// Decode dcid length if not 0
|
||||||
|
if dcid_len != 0 {
|
||||||
|
dcid_len += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode scid length if not 0
|
||||||
|
if scid_len != 0 {
|
||||||
|
scid_len += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (rest, dcid) = take(dcid_len as usize)(rest)?;
|
||||||
|
let (rest, scid) = take(scid_len as usize)(rest)?;
|
||||||
|
|
||||||
|
(rest, dcid.to_vec(), scid.to_vec())
|
||||||
|
} else {
|
||||||
|
let (rest, dcid_len) = be_u8(rest)?;
|
||||||
|
let (rest, dcid) = take(dcid_len as usize)(rest)?;
|
||||||
|
|
||||||
|
let (rest, scid_len) = be_u8(rest)?;
|
||||||
|
let (rest, scid) = take(scid_len as usize)(rest)?;
|
||||||
|
(rest, dcid.to_vec(), scid.to_vec())
|
||||||
|
};
|
||||||
|
|
||||||
|
let rest = match ty {
|
||||||
|
QuicType::Initial => {
|
||||||
|
let (rest, _pkt_num) = be_u32(rest)?;
|
||||||
|
let (rest, _msg_auth_hash) = take(12usize)(rest)?;
|
||||||
|
|
||||||
|
rest
|
||||||
|
}
|
||||||
|
_ => rest,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
QuicHeader {
|
||||||
|
flags,
|
||||||
|
ty,
|
||||||
|
version,
|
||||||
|
dcid,
|
||||||
|
scid,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuicData {
|
||||||
|
pub(crate) fn from_bytes(input: &[u8]) -> Result<QuicData, QuicError> {
|
||||||
|
let (_, frames) = all_consuming(Frame::decode_frames)(input)?;
|
||||||
|
Ok(QuicData { frames })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::quic::frames::{Frame, Stream, StreamTag};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn public_flags_test() {
|
||||||
|
let pf = PublicFlags::new(0xcb);
|
||||||
|
assert_eq!(PublicFlags { is_long: true }, pf);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TEST_DEFAULT_CID_LENGTH: usize = 8;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_gquic_unknown_version() {
|
||||||
|
// Test that we can parse unknown versions
|
||||||
|
let data = hex::decode("cbff00001d1091d0b10ac886039973885dfa07c469431409b15e86dd0990aaf906c5de620c4538398ffa58004482d2b19e732fc58818e57cb63569ce11abc00ea4fbac0725c5690a0838c27faf88663b48eca63baf0fba52af4eff7b4117384457c5cf1c1c0e52a1843c7676a4a35cf60c4c179e7186274c121110ace964771f31090f586b283bddbf82e9dd1d6a0e41fbaf243540dfb64f4543e1e87857c77cfc1ee9f883b97b89b6321ce30436119acfdbf2b31f4d0dbac0e5ea740ee59c8619d7c431320504c67f5c3aa9be5192f28ae378e0c8305fb95f01e7cb47c27f92cad7e8d55f699a41df3afe3894939f79e5f164771a6fe987602d975a06bfe8e6906b23601d08bcf2026eac25eca958a7b19ed7ba415e4d31b474264a479c53f01e1d35745ae62a9b148e39e2d7d33176f384d6ce4beb25d2177a8e0fbe5503ea034c9a645e5a8c98098bc5db4e11a351ac72b7079db1a858e11a6c6a4a1f44e1073903029cc08e82c48e6de00f5da7a546db371a4e49d4a213339ca074832cfeb4c39731f98a1683d7fb7db8a5b48c763440d6003fdfadd6a7fb23a62074064aafd585f6a887d5648ce71099d7d21e5cc1e14645f066016a7570d885bde4f239226884ee64fb8ec1218efec83d46ca104d9104bf46637ba3a3d8d6a88967859d60f46198e3a8495f2f211d717c6ca39987d2f4f971b502809932d973736dac67e5e28152c23d844d99fe7a5def822ca97aa433603423ee7fef57e6daa4579bb8f4f14a93663c54db415da5e8b9000d673d99c065c5922ba193eada475f2366b422d42dd86dd3b86fdef67d0e71cd200e3e24b77578f90e0e60717e3a1d6078b836d262028fc73efe7b3684b635f3001225acfd249fbe950dae7c539f015a0ce51c983c4d8e01d7e73e16946e681b2148d0ae4e72fb44d1048eb25572dae0a8016434b8c9e3fd3c93045b8afe67adc6cf7ce61a46819b712a8c24980e6c75bf007adf8910badfa102cd60c96238c8719b5e2b405905cfa6840176c7f71b7d9a2f480c36806f415b93b72821f0547b06f298584be093710a381fa352c34ba221cbcf1bbcd0b7d1aea354e460f6824df14d4bf4377a4503397e70f9993a55905ba298e798d9c69386eae8d0ebf6d871ff75e2d5a546bb8ee6ad9c92d88f950e2d8bc371aaad0d948e9f81c8151c51ee17c9257df4fd27cfeb9944b301a0fff1cb0a1b18836969457edd42f6ba370ecc2e5700bbb9fc15dc9f88c9bfc12c7dda64d423179c1eff8c53cca97056e09a07e29d02b4e553141b78d224cd79ae8056d923d41bc67eec00c943e3a62304487261d0877d54c40b7453c52e6c02141c2fa6601a357d53dddf39ae6e152501813e562a0613ca727ef3b0548c1f5a7e5036a8da84e166cec45de83bf217fb8f6c9a0ea20db0b16d1d2bb9e5e305e9d1f35e3065ab7188f79b9a841d1f6000ea744df1ba49116f7558feedf70677e35985d71b1c87c988d0b1ef2e436a54a77397546513c82bf307fc4b29152cafab11c8527eeda2addd00081c3b7b836a39920322a405c4e3774f20feda9998bf703fd10e93748b7834f3f3794d5b1f3f3099c608e84b025f5675b1526e8feee91ed04f4e91e37bd8e7089ec5a48edc2537bcddbd9d118d7937e2c25fa383186efd2f48fa3f5ebe7eaf544835bb330b61af1a95158c5e").unwrap();
|
||||||
|
let (_rest, value) =
|
||||||
|
QuicHeader::from_bytes(data.as_ref(), TEST_DEFAULT_CID_LENGTH).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
QuicHeader {
|
||||||
|
flags: PublicFlags { is_long: true },
|
||||||
|
ty: QuicType::Initial,
|
||||||
|
version: QuicVersion(0xff00001d),
|
||||||
|
dcid: hex::decode("91d0b10ac886039973885dfa07c46943")
|
||||||
|
.unwrap()
|
||||||
|
.to_vec(),
|
||||||
|
scid: hex::decode("09b15e86dd0990aaf906c5de620c4538398ffa58")
|
||||||
|
.unwrap()
|
||||||
|
.to_vec()
|
||||||
|
},
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_gquic_q044() {
|
||||||
|
let test_data = hex::decode("ff513034345005cad2cc06c4d0e400000001afac230bc5b56fb89800171b800143484c4f09000000504144008f030000534e490098030000564552009c03000043435300ac03000050444d44b00300004943534cb40300004d494453b803000043464357bc03000053464357c003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003132372e302e302e310000000001e8816092921ae87eed8086a215829158353039803a09006400000000c0000000800000").unwrap();
|
||||||
|
let (rest, header) =
|
||||||
|
QuicHeader::from_bytes(test_data.as_ref(), TEST_DEFAULT_CID_LENGTH).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
QuicHeader {
|
||||||
|
flags: PublicFlags { is_long: true },
|
||||||
|
ty: QuicType::Initial,
|
||||||
|
version: QuicVersion::Q044,
|
||||||
|
dcid: hex::decode("05cad2cc06c4d0e4").unwrap().to_vec(),
|
||||||
|
scid: Vec::new(),
|
||||||
|
},
|
||||||
|
header
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = QuicData::from_bytes(rest).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
QuicData {
|
||||||
|
frames: vec![Frame::Stream(Stream {
|
||||||
|
fin: false,
|
||||||
|
stream_id: vec![0x1],
|
||||||
|
offset: vec![],
|
||||||
|
tags: Some(vec![
|
||||||
|
(StreamTag::Pad, [0x0; 911].to_vec()),
|
||||||
|
(
|
||||||
|
StreamTag::Sni,
|
||||||
|
vec![0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31]
|
||||||
|
),
|
||||||
|
(StreamTag::Ver, vec![0x0, 0x0, 0x0, 0x0]),
|
||||||
|
(
|
||||||
|
StreamTag::Ccs,
|
||||||
|
vec![
|
||||||
|
0x1, 0xe8, 0x81, 0x60, 0x92, 0x92, 0x1a, 0xe8, 0x7e, 0xed, 0x80,
|
||||||
|
0x86, 0xa2, 0x15, 0x82, 0x91
|
||||||
|
]
|
||||||
|
),
|
||||||
|
(StreamTag::Pdmd, vec![0x58, 0x35, 0x30, 0x39]),
|
||||||
|
(StreamTag::Icsl, vec![0x80, 0x3a, 0x9, 0x0]),
|
||||||
|
(StreamTag::Mids, vec![0x64, 0x0, 0x0, 0x0]),
|
||||||
|
(StreamTag::Cfcw, vec![0x0, 0xc0, 0x0, 0x0]),
|
||||||
|
(StreamTag::Sfcw, vec![0x0, 0x80, 0x0, 0x0]),
|
||||||
|
])
|
||||||
|
})]
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,329 @@
|
|||||||
|
/* Copyright (C) 2021 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::{
|
||||||
|
cyu::Cyu,
|
||||||
|
parser::{QuicData, QuicHeader},
|
||||||
|
};
|
||||||
|
use crate::applayer::{self, *};
|
||||||
|
use crate::core::{self, AppProto, Flow, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_UDP};
|
||||||
|
use std::ffi::CString;
|
||||||
|
|
||||||
|
static mut ALPROTO_QUIC: AppProto = ALPROTO_UNKNOWN;
|
||||||
|
|
||||||
|
const DEFAULT_DCID_LEN: usize = 16;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QuicTransaction {
|
||||||
|
tx_id: u64,
|
||||||
|
pub header: QuicHeader,
|
||||||
|
pub cyu: Vec<Cyu>,
|
||||||
|
|
||||||
|
de_state: Option<*mut core::DetectEngineState>,
|
||||||
|
events: *mut core::AppLayerDecoderEvents,
|
||||||
|
tx_data: AppLayerTxData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuicTransaction {
|
||||||
|
fn new(header: QuicHeader, data: QuicData) -> Self {
|
||||||
|
let cyu = Cyu::generate(&header, &data.frames);
|
||||||
|
QuicTransaction {
|
||||||
|
tx_id: 0,
|
||||||
|
header,
|
||||||
|
cyu,
|
||||||
|
de_state: None,
|
||||||
|
events: std::ptr::null_mut(),
|
||||||
|
tx_data: AppLayerTxData::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn free(&mut self) {
|
||||||
|
if !self.events.is_null() {
|
||||||
|
core::sc_app_layer_decoder_events_free_events(&mut self.events);
|
||||||
|
}
|
||||||
|
if let Some(state) = self.de_state {
|
||||||
|
core::sc_detect_engine_state_free(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for QuicTransaction {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.free();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct QuicState {
|
||||||
|
max_tx_id: u64,
|
||||||
|
transactions: Vec<QuicTransaction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QuicState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
max_tx_id: 0,
|
||||||
|
transactions: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QuicState {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free a transaction by ID.
|
||||||
|
fn free_tx(&mut self, tx_id: u64) {
|
||||||
|
let tx = self
|
||||||
|
.transactions
|
||||||
|
.iter()
|
||||||
|
.position(|tx| tx.tx_id == tx_id + 1);
|
||||||
|
if let Some(idx) = tx {
|
||||||
|
let _ = self.transactions.remove(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tx(&mut self, tx_id: u64) -> Option<&QuicTransaction> {
|
||||||
|
self.transactions.iter().find(|&tx| tx.tx_id == tx_id + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_tx(&mut self, header: QuicHeader, data: QuicData) -> QuicTransaction {
|
||||||
|
let mut tx = QuicTransaction::new(header, data);
|
||||||
|
self.max_tx_id += 1;
|
||||||
|
tx.tx_id = self.max_tx_id;
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tx_iterator(
|
||||||
|
&mut self, min_tx_id: u64, state: &mut u64,
|
||||||
|
) -> Option<(&QuicTransaction, u64, bool)> {
|
||||||
|
let mut index = *state as usize;
|
||||||
|
let len = self.transactions.len();
|
||||||
|
|
||||||
|
while index < len {
|
||||||
|
let tx = &self.transactions[index];
|
||||||
|
if tx.tx_id < min_tx_id + 1 {
|
||||||
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*state = index as u64;
|
||||||
|
return Some((tx, tx.tx_id - 1, (len - index) > 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(&mut self, input: &[u8]) -> bool {
|
||||||
|
match QuicHeader::from_bytes(input, DEFAULT_DCID_LEN) {
|
||||||
|
Ok((rest, header)) => match QuicData::from_bytes(rest) {
|
||||||
|
Ok(data) => {
|
||||||
|
let transaction = self.new_tx(header, data);
|
||||||
|
self.transactions.push(transaction);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_e) => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_quic_state_new(
|
||||||
|
_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
|
||||||
|
) -> *mut std::os::raw::c_void {
|
||||||
|
let state = QuicState::new();
|
||||||
|
let boxed = Box::new(state);
|
||||||
|
return Box::into_raw(boxed) as *mut _;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_quic_state_free(state: *mut std::os::raw::c_void) {
|
||||||
|
// Just unbox...
|
||||||
|
std::mem::drop(unsafe { Box::from_raw(state as *mut QuicState) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
|
||||||
|
let state = cast_pointer!(state, QuicState);
|
||||||
|
state.free_tx(tx_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_probing_parser(
|
||||||
|
_flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
|
||||||
|
) -> AppProto {
|
||||||
|
let slice = build_slice!(input, input_len as usize);
|
||||||
|
|
||||||
|
if QuicHeader::from_bytes(slice, DEFAULT_DCID_LEN).is_ok() {
|
||||||
|
return ALPROTO_QUIC;
|
||||||
|
} else {
|
||||||
|
return ALPROTO_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_parse(
|
||||||
|
_flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
|
||||||
|
stream_slice: StreamSlice,
|
||||||
|
_data: *const std::os::raw::c_void,
|
||||||
|
) -> AppLayerResult {
|
||||||
|
let state = cast_pointer!(state, QuicState);
|
||||||
|
let buf = stream_slice.as_slice();
|
||||||
|
|
||||||
|
if state.parse(buf) {
|
||||||
|
return AppLayerResult::ok();
|
||||||
|
}
|
||||||
|
return AppLayerResult::err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_state_get_tx(
|
||||||
|
state: *mut std::os::raw::c_void, tx_id: u64,
|
||||||
|
) -> *mut std::os::raw::c_void {
|
||||||
|
let state = cast_pointer!(state, QuicState);
|
||||||
|
match state.get_tx(tx_id) {
|
||||||
|
Some(tx) => {
|
||||||
|
return tx as *const _ as *mut _;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
|
||||||
|
let state = cast_pointer!(state, QuicState);
|
||||||
|
return state.max_tx_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_quic_state_progress_completion_status(_direction: u8) -> std::os::raw::c_int {
|
||||||
|
// This parser uses 1 to signal transaction completion status.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_tx_get_alstate_progress(
|
||||||
|
tx: *mut std::os::raw::c_void, _direction: u8,
|
||||||
|
) -> std::os::raw::c_int {
|
||||||
|
let _tx = cast_pointer!(tx, QuicTransaction);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_state_get_events(
|
||||||
|
tx: *mut std::os::raw::c_void,
|
||||||
|
) -> *mut core::AppLayerDecoderEvents {
|
||||||
|
let tx = cast_pointer!(tx, QuicTransaction);
|
||||||
|
return tx.events;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_quic_state_get_event_info(
|
||||||
|
_event_name: *const std::os::raw::c_char, _event_id: *mut std::os::raw::c_int,
|
||||||
|
_event_type: *mut core::AppLayerEventType,
|
||||||
|
) -> std::os::raw::c_int {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn rs_quic_state_get_event_info_by_id(
|
||||||
|
_event_id: std::os::raw::c_int, _event_name: *mut *const std::os::raw::c_char,
|
||||||
|
_event_type: *mut core::AppLayerEventType,
|
||||||
|
) -> i8 {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_state_get_tx_iterator(
|
||||||
|
_ipproto: u8, _alproto: AppProto, state: *mut std::os::raw::c_void, min_tx_id: u64,
|
||||||
|
_max_tx_id: u64, istate: &mut u64,
|
||||||
|
) -> applayer::AppLayerGetTxIterTuple {
|
||||||
|
let state = cast_pointer!(state, QuicState);
|
||||||
|
match state.tx_iterator(min_tx_id, istate) {
|
||||||
|
Some((tx, out_tx_id, has_next)) => {
|
||||||
|
let c_tx = tx as *const _ as *mut _;
|
||||||
|
let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
|
||||||
|
return ires;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return applayer::AppLayerGetTxIterTuple::not_found();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export_tx_data_get!(rs_quic_get_tx_data, QuicTransaction);
|
||||||
|
|
||||||
|
// Parser name as a C style string.
|
||||||
|
const PARSER_NAME: &[u8] = b"quic\0";
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn rs_quic_register_parser() {
|
||||||
|
let default_port = CString::new("[443,80]").unwrap();
|
||||||
|
let parser = RustParser {
|
||||||
|
name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
|
||||||
|
default_port: default_port.as_ptr(),
|
||||||
|
ipproto: IPPROTO_UDP,
|
||||||
|
probe_ts: Some(rs_quic_probing_parser),
|
||||||
|
probe_tc: Some(rs_quic_probing_parser),
|
||||||
|
min_depth: 0,
|
||||||
|
max_depth: 16,
|
||||||
|
state_new: rs_quic_state_new,
|
||||||
|
state_free: rs_quic_state_free,
|
||||||
|
tx_free: rs_quic_state_tx_free,
|
||||||
|
parse_ts: rs_quic_parse,
|
||||||
|
parse_tc: rs_quic_parse,
|
||||||
|
get_tx_count: rs_quic_state_get_tx_count,
|
||||||
|
get_tx: rs_quic_state_get_tx,
|
||||||
|
tx_comp_st_ts: 1,
|
||||||
|
tx_comp_st_tc: 1,
|
||||||
|
tx_get_progress: rs_quic_tx_get_alstate_progress,
|
||||||
|
get_eventinfo: Some(rs_quic_state_get_event_info),
|
||||||
|
get_eventinfo_byid: Some(rs_quic_state_get_event_info_by_id),
|
||||||
|
localstorage_new: None,
|
||||||
|
localstorage_free: None,
|
||||||
|
get_files: None,
|
||||||
|
get_tx_iterator: Some(rs_quic_state_get_tx_iterator),
|
||||||
|
get_tx_data: rs_quic_get_tx_data,
|
||||||
|
apply_tx_config: None,
|
||||||
|
flags: 0,
|
||||||
|
truncate: None,
|
||||||
|
get_frame_id_by_name: None,
|
||||||
|
get_frame_name_by_id: None,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
let ip_proto_str = CString::new("udp").unwrap();
|
||||||
|
|
||||||
|
if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
|
||||||
|
let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
|
||||||
|
ALPROTO_QUIC = alproto;
|
||||||
|
if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
|
||||||
|
let _ = AppLayerRegisterParser(&parser, alproto);
|
||||||
|
}
|
||||||
|
SCLogDebug!("Rust quic parser registered.");
|
||||||
|
} else {
|
||||||
|
SCLogDebug!("Protocol detector and parser disabled for quic.");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*
|
||||||
|
* Quic Application Layer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "stream.h"
|
||||||
|
#include "conf.h"
|
||||||
|
|
||||||
|
#include "util-unittest.h"
|
||||||
|
|
||||||
|
#include "app-layer-detect-proto.h"
|
||||||
|
#include "app-layer-parser.h"
|
||||||
|
|
||||||
|
#include "app-layer-quic.h"
|
||||||
|
#include "rust.h"
|
||||||
|
|
||||||
|
void RegisterQuicParsers(void)
|
||||||
|
{
|
||||||
|
rs_quic_register_parser();
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __APP_LAYER_QUIC_H__
|
||||||
|
#define __APP_LAYER_QUIC_H__
|
||||||
|
|
||||||
|
void RegisterQuicParsers(void);
|
||||||
|
void QuicParserRegisterTests(void);
|
||||||
|
|
||||||
|
#endif /* __APP_LAYER_QUIC_H__ */
|
@ -0,0 +1,428 @@
|
|||||||
|
/* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Implements the quic.cyu.hash sticky buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "detect.h"
|
||||||
|
#include "detect-parse.h"
|
||||||
|
#include "detect-engine.h"
|
||||||
|
#include "detect-engine-mpm.h"
|
||||||
|
#include "detect-engine-prefilter.h"
|
||||||
|
#include "detect-engine-content-inspection.h"
|
||||||
|
#include "detect-quic-cyu-hash.h"
|
||||||
|
#include "rust.h"
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
static void DetectQuicCyuHashRegisterTests(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KEYWORD_NAME "quic.cyu.hash"
|
||||||
|
#define KEYWORD_DOC "quic-cyu.html#quic-cyu-hash"
|
||||||
|
#define BUFFER_NAME "quic.cyu.hash"
|
||||||
|
#define BUFFER_DESC "QUIC CYU Hash"
|
||||||
|
static int g_buffer_id = 0;
|
||||||
|
|
||||||
|
struct QuicHashGetDataArgs {
|
||||||
|
uint32_t local_id; /**< used as index into thread inspect array */
|
||||||
|
void *txv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int DetectQuicCyuHashSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
|
||||||
|
{
|
||||||
|
if (DetectBufferSetActiveList(s, g_buffer_id) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (DetectSignatureSetAppProto(s, ALPROTO_QUIC) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static InspectionBuffer *QuicHashGetData(DetectEngineThreadCtx *det_ctx,
|
||||||
|
const DetectEngineTransforms *transforms, Flow *f, struct QuicHashGetDataArgs *cbdata,
|
||||||
|
int list_id, bool first)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
|
||||||
|
InspectionBuffer *buffer =
|
||||||
|
InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (!first && buffer->inspect != NULL)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
const uint8_t *data;
|
||||||
|
uint32_t data_len;
|
||||||
|
if (rs_quic_tx_get_cyu_hash(cbdata->txv, (uint16_t)cbdata->local_id, &data, &data_len) == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
InspectionBufferSetupMulti(buffer, transforms, data, data_len);
|
||||||
|
|
||||||
|
SCReturnPtr(buffer, "InspectionBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DetectEngineInspectQuicHash(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
|
||||||
|
const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
|
||||||
|
void *alstate, void *txv, uint64_t tx_id)
|
||||||
|
{
|
||||||
|
uint32_t local_id = 0;
|
||||||
|
|
||||||
|
const DetectEngineTransforms *transforms = NULL;
|
||||||
|
if (!engine->mpm) {
|
||||||
|
transforms = engine->v2.transforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct QuicHashGetDataArgs cbdata = {
|
||||||
|
local_id,
|
||||||
|
txv,
|
||||||
|
};
|
||||||
|
InspectionBuffer *buffer =
|
||||||
|
QuicHashGetData(det_ctx, transforms, f, &cbdata, engine->sm_list, false);
|
||||||
|
if (buffer == NULL || buffer->inspect == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
det_ctx->buffer_offset = 0;
|
||||||
|
det_ctx->discontinue_matching = 0;
|
||||||
|
det_ctx->inspection_recursion_counter = 0;
|
||||||
|
|
||||||
|
const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f,
|
||||||
|
(uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset,
|
||||||
|
DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
|
||||||
|
if (match == 1) {
|
||||||
|
return DETECT_ENGINE_INSPECT_SIG_MATCH;
|
||||||
|
}
|
||||||
|
local_id++;
|
||||||
|
}
|
||||||
|
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct PrefilterMpmQuicHash {
|
||||||
|
int list_id;
|
||||||
|
const MpmCtx *mpm_ctx;
|
||||||
|
const DetectEngineTransforms *transforms;
|
||||||
|
} PrefilterMpmQuicHash;
|
||||||
|
|
||||||
|
/** \brief QuicHash Mpm prefilter callback
|
||||||
|
*
|
||||||
|
* \param det_ctx detection engine thread ctx
|
||||||
|
* \param p packet to inspect
|
||||||
|
* \param f flow to inspect
|
||||||
|
* \param txv tx to inspect
|
||||||
|
* \param pectx inspection context
|
||||||
|
*/
|
||||||
|
static void PrefilterTxQuicHash(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
|
||||||
|
Flow *f, void *txv, const uint64_t idx, const uint8_t flags)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
|
||||||
|
const PrefilterMpmQuicHash *ctx = (const PrefilterMpmQuicHash *)pectx;
|
||||||
|
const MpmCtx *mpm_ctx = ctx->mpm_ctx;
|
||||||
|
const int list_id = ctx->list_id;
|
||||||
|
|
||||||
|
uint32_t local_id = 0;
|
||||||
|
while (1) {
|
||||||
|
// loop until we get a NULL
|
||||||
|
|
||||||
|
struct QuicHashGetDataArgs cbdata = { local_id, txv };
|
||||||
|
InspectionBuffer *buffer =
|
||||||
|
QuicHashGetData(det_ctx, ctx->transforms, f, &cbdata, list_id, true);
|
||||||
|
if (buffer == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (buffer->inspect_len >= mpm_ctx->minlen) {
|
||||||
|
(void)mpm_table[mpm_ctx->mpm_type].Search(
|
||||||
|
mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrefilterMpmQuicHashFree(void *ptr)
|
||||||
|
{
|
||||||
|
SCFree(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int PrefilterMpmQuicHashRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
|
||||||
|
const DetectBufferMpmRegistery *mpm_reg, int list_id)
|
||||||
|
{
|
||||||
|
PrefilterMpmQuicHash *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, PrefilterTxQuicHash, mpm_reg->app_v2.alproto,
|
||||||
|
mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmQuicHashFree, mpm_reg->pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DetectQuicHashValidateCallback(const Signature *s, const char **sigerror)
|
||||||
|
{
|
||||||
|
const SigMatch *sm = s->init_data->smlists[g_buffer_id];
|
||||||
|
for (; sm != NULL; sm = sm->next) {
|
||||||
|
if (sm->type != DETECT_CONTENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const DetectContentData *cd = (DetectContentData *)sm->ctx;
|
||||||
|
|
||||||
|
if (cd->flags & DETECT_CONTENT_NOCASE) {
|
||||||
|
*sigerror = BUFFER_NAME " should not be used together with "
|
||||||
|
"nocase, since the rule is automatically "
|
||||||
|
"lowercased anyway which makes nocase redundant.";
|
||||||
|
SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cd->content_len != 32) {
|
||||||
|
*sigerror = "Invalid length of the specified" BUFFER_NAME " (should "
|
||||||
|
"be 32 characters long). This rule will therefore "
|
||||||
|
"never match.";
|
||||||
|
SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < cd->content_len; ++i) {
|
||||||
|
if (!isxdigit(cd->content[i])) {
|
||||||
|
*sigerror = "Invalid " BUFFER_NAME
|
||||||
|
" string (should be string of hexadecimal characters)."
|
||||||
|
"This rule will therefore never match.";
|
||||||
|
SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectQuicCyuHashRegister(void)
|
||||||
|
{
|
||||||
|
/* quic.cyu.hash sticky buffer */
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_HASH].name = KEYWORD_NAME;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_HASH].desc = "sticky buffer to match on the QUIC CYU hash";
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_HASH].url = "/rules/" KEYWORD_DOC;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_HASH].Setup = DetectQuicCyuHashSetup;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_HASH].flags |= SIGMATCH_NOOPT;
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_HASH].RegisterTests = DetectQuicCyuHashRegisterTests;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DetectAppLayerMpmRegister2(
|
||||||
|
BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterMpmQuicHashRegister, NULL, ALPROTO_QUIC, 1);
|
||||||
|
|
||||||
|
DetectAppLayerInspectEngineRegister2(
|
||||||
|
BUFFER_NAME, ALPROTO_QUIC, SIG_FLAG_TOSERVER, 0, DetectEngineInspectQuicHash, NULL);
|
||||||
|
|
||||||
|
DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC);
|
||||||
|
|
||||||
|
g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME);
|
||||||
|
|
||||||
|
DetectBufferTypeRegisterValidateCallback(BUFFER_NAME, DetectQuicHashValidateCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
#include "app-layer-parser.h"
|
||||||
|
#include "util-unittest.h"
|
||||||
|
#include "util-unittest-helper.h"
|
||||||
|
#include "flow-util.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \test DetectQuicCyuHashTest01 is a test for a valid quic packet, matching
|
||||||
|
* on the cyu hash
|
||||||
|
*
|
||||||
|
* \retval 1 on success
|
||||||
|
* \retval 0 on failure
|
||||||
|
*/
|
||||||
|
static int DetectQuicCyuHashTest01(void)
|
||||||
|
{
|
||||||
|
/* quic packet */
|
||||||
|
uint8_t buf[] = { 0xc3, 0x51, 0x30, 0x34, 0x36, 0x50, 0x76, 0xd8, 0x63, 0xb7, 0x54, 0xf7, 0xab,
|
||||||
|
0x32, 0x00, 0x00, 0x00, 0x01, 0x54, 0xfd, 0xf4, 0x79, 0x48, 0x76, 0xd0, 0x87, 0x58, 0x8d,
|
||||||
|
0x26, 0x8f, 0xa0, 0x01, 0x04, 0x00, 0x43, 0x48, 0x4c, 0x4f, 0x11, 0x00, 0x00, 0x00, 0x50,
|
||||||
|
0x41, 0x44, 0x00, 0xe4, 0x02, 0x00, 0x00, 0x53, 0x4e, 0x49, 0x00, 0xf7, 0x02, 0x00, 0x00,
|
||||||
|
0x56, 0x45, 0x52, 0x00, 0xfb, 0x02, 0x00, 0x00, 0x43, 0x43, 0x53, 0x00, 0x0b, 0x03, 0x00,
|
||||||
|
0x00, 0x55, 0x41, 0x49, 0x44, 0x2c, 0x03, 0x00, 0x00, 0x54, 0x43, 0x49, 0x44, 0x30, 0x03,
|
||||||
|
0x00, 0x00, 0x50, 0x44, 0x4d, 0x44, 0x34, 0x03, 0x00, 0x00, 0x53, 0x4d, 0x48, 0x4c, 0x38,
|
||||||
|
0x03, 0x00, 0x00, 0x49, 0x43, 0x53, 0x4c, 0x3c, 0x03, 0x00, 0x00, 0x4e, 0x4f, 0x4e, 0x50,
|
||||||
|
0x5c, 0x03, 0x00, 0x00, 0x4d, 0x49, 0x44, 0x53, 0x60, 0x03, 0x00, 0x00, 0x53, 0x43, 0x4c,
|
||||||
|
0x53, 0x64, 0x03, 0x00, 0x00, 0x43, 0x53, 0x43, 0x54, 0x64, 0x03, 0x00, 0x00, 0x43, 0x4f,
|
||||||
|
0x50, 0x54, 0x64, 0x03, 0x00, 0x00, 0x49, 0x52, 0x54, 0x54, 0x68, 0x03, 0x00, 0x00, 0x43,
|
||||||
|
0x46, 0x43, 0x57, 0x6c, 0x03, 0x00, 0x00, 0x53, 0x46, 0x43, 0x57, 0x70, 0x03, 0x00, 0x00,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x31, 0x2e, 0x67,
|
||||||
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x51, 0x30, 0x34, 0x36, 0x01, 0xe8,
|
||||||
|
0x81, 0x60, 0x92, 0x92, 0x1a, 0xe8, 0x7e, 0xed, 0x80, 0x86, 0xa2, 0x15, 0x82, 0x91, 0x43,
|
||||||
|
0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x37, 0x39, 0x2e, 0x30, 0x2e, 0x33, 0x39, 0x34, 0x35,
|
||||||
|
0x2e, 0x31, 0x31, 0x37, 0x20, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f,
|
||||||
|
0x36, 0x34, 0x00, 0x00, 0x00, 0x00, 0x58, 0x35, 0x30, 0x39, 0x01, 0x00, 0x00, 0x00, 0x1e,
|
||||||
|
0x00, 0x00, 0x00, 0x82, 0x88, 0x09, 0x00, 0xfa, 0x0f, 0xde, 0xb7, 0x2e, 0x7e, 0x6c, 0x78,
|
||||||
|
0xcc, 0x09, 0x65, 0xab, 0x06, 0x0c, 0x31, 0x05, 0xfa, 0xd9, 0xa2, 0x0b, 0xdd, 0x74, 0x5c,
|
||||||
|
0x28, 0xdf, 0x7b, 0x74, 0x23, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1d, 0x43,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00 };
|
||||||
|
|
||||||
|
Flow f;
|
||||||
|
void *quic_state = NULL;
|
||||||
|
Packet *p = NULL;
|
||||||
|
Signature *s = NULL;
|
||||||
|
ThreadVars tv;
|
||||||
|
DetectEngineThreadCtx *det_ctx = NULL;
|
||||||
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
||||||
|
|
||||||
|
memset(&tv, 0, sizeof(ThreadVars));
|
||||||
|
memset(&f, 0, sizeof(Flow));
|
||||||
|
|
||||||
|
p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP, "192.168.1.5", "192.168.1.1", 41424, 443);
|
||||||
|
|
||||||
|
FLOW_INITIALIZE(&f);
|
||||||
|
f.flags |= FLOW_IPV4;
|
||||||
|
f.proto = IPPROTO_UDP;
|
||||||
|
f.protomap = FlowGetProtoMapping(f.proto);
|
||||||
|
|
||||||
|
p->flow = &f;
|
||||||
|
p->flags |= PKT_HAS_FLOW;
|
||||||
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
||||||
|
f.alproto = ALPROTO_QUIC;
|
||||||
|
|
||||||
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||||
|
FAIL_IF_NULL(de_ctx);
|
||||||
|
de_ctx->mpm_matcher = mpm_default_matcher;
|
||||||
|
de_ctx->flags |= DE_QUIET;
|
||||||
|
|
||||||
|
s = DetectEngineAppendSig(de_ctx,
|
||||||
|
"alert quic any any -> any any "
|
||||||
|
"(msg:\"Test QUIC CYU hash\"; "
|
||||||
|
"quic.cyu.hash; content:\"910a5e3a4d51593bd59a44611544f209\"; "
|
||||||
|
"sid:1;)");
|
||||||
|
FAIL_IF_NULL(s);
|
||||||
|
|
||||||
|
SigGroupBuild(de_ctx);
|
||||||
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
||||||
|
|
||||||
|
FLOWLOCK_WRLOCK(&f);
|
||||||
|
int r = AppLayerParserParse(
|
||||||
|
NULL, alp_tctx, &f, ALPROTO_QUIC, STREAM_TOSERVER, buf, sizeof(buf));
|
||||||
|
if (r != 0) {
|
||||||
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
||||||
|
FLOWLOCK_UNLOCK(&f);
|
||||||
|
FAIL;
|
||||||
|
}
|
||||||
|
FLOWLOCK_UNLOCK(&f);
|
||||||
|
|
||||||
|
quic_state = f.alstate;
|
||||||
|
FAIL_IF_NULL(quic_state);
|
||||||
|
|
||||||
|
/* do detect */
|
||||||
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
||||||
|
|
||||||
|
if (!(PacketAlertCheck(p, 1))) {
|
||||||
|
printf("sig 1 didn't alert, but it should have: ");
|
||||||
|
FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alp_tctx != NULL)
|
||||||
|
AppLayerParserThreadCtxFree(alp_tctx);
|
||||||
|
if (det_ctx != NULL)
|
||||||
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
||||||
|
if (de_ctx != NULL)
|
||||||
|
SigGroupCleanup(de_ctx);
|
||||||
|
if (de_ctx != NULL)
|
||||||
|
DetectEngineCtxFree(de_ctx);
|
||||||
|
|
||||||
|
FLOW_DESTROY(&f);
|
||||||
|
UTHFreePacket(p);
|
||||||
|
PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DetectQuicCyuHashRegisterTests(void)
|
||||||
|
{
|
||||||
|
UtRegisterTest("DetectQuicCyuHashTest01", DetectQuicCyuHashTest01);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* UNITTESTS */
|
@ -0,0 +1,28 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DETECT_QUIC_CYU_HASH_H__
|
||||||
|
#define __DETECT_QUIC_CYU_HASH_H__
|
||||||
|
|
||||||
|
void DetectQuicCyuHashRegister(void);
|
||||||
|
|
||||||
|
#endif /* __DETECT_QUIC_CYU_HASH_H__ */
|
@ -0,0 +1,390 @@
|
|||||||
|
/* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Implements the quic.cyu.string sticky buffer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "detect.h"
|
||||||
|
#include "detect-parse.h"
|
||||||
|
#include "detect-engine.h"
|
||||||
|
#include "detect-engine-mpm.h"
|
||||||
|
#include "detect-engine-prefilter.h"
|
||||||
|
#include "detect-engine-content-inspection.h"
|
||||||
|
#include "detect-quic-cyu-string.h"
|
||||||
|
#include "rust.h"
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
static void DetectQuicCyuStringRegisterTests(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KEYWORD_NAME "quic.cyu.string"
|
||||||
|
#define KEYWORD_DOC "quic-cyu.html#quic-cyu-string"
|
||||||
|
#define BUFFER_NAME "quic.cyu.string"
|
||||||
|
#define BUFFER_DESC "QUIC CYU String"
|
||||||
|
static int g_buffer_id = 0;
|
||||||
|
|
||||||
|
struct QuicStringGetDataArgs {
|
||||||
|
uint32_t local_id; /**< used as index into thread inspect array */
|
||||||
|
void *txv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int DetectQuicCyuStringSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
|
||||||
|
{
|
||||||
|
if (DetectBufferSetActiveList(s, g_buffer_id) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (DetectSignatureSetAppProto(s, ALPROTO_QUIC) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static InspectionBuffer *QuicStringGetData(DetectEngineThreadCtx *det_ctx,
|
||||||
|
const DetectEngineTransforms *transforms, Flow *f, struct QuicStringGetDataArgs *cbdata,
|
||||||
|
int list_id, bool first)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
|
||||||
|
InspectionBuffer *buffer =
|
||||||
|
InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id);
|
||||||
|
if (buffer == NULL)
|
||||||
|
return NULL;
|
||||||
|
if (!first && buffer->inspect != NULL)
|
||||||
|
return buffer;
|
||||||
|
|
||||||
|
const uint8_t *data;
|
||||||
|
uint32_t data_len;
|
||||||
|
if (rs_quic_tx_get_cyu_string(cbdata->txv, cbdata->local_id, &data, &data_len) == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
InspectionBufferSetupMulti(buffer, transforms, data, data_len);
|
||||||
|
|
||||||
|
SCReturnPtr(buffer, "InspectionBuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DetectEngineInspectQuicString(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
|
||||||
|
const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
|
||||||
|
void *alstate, void *txv, uint64_t tx_id)
|
||||||
|
{
|
||||||
|
uint32_t local_id = 0;
|
||||||
|
|
||||||
|
const DetectEngineTransforms *transforms = NULL;
|
||||||
|
if (!engine->mpm) {
|
||||||
|
transforms = engine->v2.transforms;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct QuicStringGetDataArgs cbdata = {
|
||||||
|
local_id,
|
||||||
|
txv,
|
||||||
|
};
|
||||||
|
InspectionBuffer *buffer =
|
||||||
|
QuicStringGetData(det_ctx, transforms, f, &cbdata, engine->sm_list, false);
|
||||||
|
if (buffer == NULL || buffer->inspect == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
det_ctx->buffer_offset = 0;
|
||||||
|
det_ctx->discontinue_matching = 0;
|
||||||
|
det_ctx->inspection_recursion_counter = 0;
|
||||||
|
|
||||||
|
const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f,
|
||||||
|
(uint8_t *)buffer->inspect, buffer->inspect_len, buffer->inspect_offset,
|
||||||
|
DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
|
||||||
|
if (match == 1) {
|
||||||
|
return DETECT_ENGINE_INSPECT_SIG_MATCH;
|
||||||
|
}
|
||||||
|
local_id++;
|
||||||
|
}
|
||||||
|
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief QuicString Mpm prefilter callback
|
||||||
|
*
|
||||||
|
* \param det_ctx detection engine thread ctx
|
||||||
|
* \param p packet to inspect
|
||||||
|
* \param f flow to inspect
|
||||||
|
* \param txv tx to inspect
|
||||||
|
* \param pectx inspection context
|
||||||
|
*/
|
||||||
|
static void PrefilterTxQuicString(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
|
||||||
|
Flow *f, void *txv, const uint64_t idx, const uint8_t flags)
|
||||||
|
{
|
||||||
|
SCEnter();
|
||||||
|
|
||||||
|
const PrefilterMpmListId *ctx = (const PrefilterMpmListId *)pectx;
|
||||||
|
const MpmCtx *mpm_ctx = ctx->mpm_ctx;
|
||||||
|
const int list_id = ctx->list_id;
|
||||||
|
|
||||||
|
uint32_t local_id = 0;
|
||||||
|
while (1) {
|
||||||
|
// loop until we get a NULL
|
||||||
|
|
||||||
|
struct QuicStringGetDataArgs cbdata = { local_id, txv };
|
||||||
|
InspectionBuffer *buffer =
|
||||||
|
QuicStringGetData(det_ctx, ctx->transforms, f, &cbdata, list_id, true);
|
||||||
|
if (buffer == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (buffer->inspect_len >= mpm_ctx->minlen) {
|
||||||
|
(void)mpm_table[mpm_ctx->mpm_type].Search(
|
||||||
|
mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
local_id++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrefilterMpmListIdFree(void *ptr)
|
||||||
|
{
|
||||||
|
SCFree(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int PrefilterMpmListIdRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
|
||||||
|
const DetectBufferMpmRegistery *mpm_reg, int list_id)
|
||||||
|
{
|
||||||
|
PrefilterMpmListId *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, PrefilterTxQuicString, mpm_reg->app_v2.alproto,
|
||||||
|
mpm_reg->app_v2.tx_min_progress, pectx, PrefilterMpmListIdFree, mpm_reg->pname);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetectQuicCyuStringRegister(void)
|
||||||
|
{
|
||||||
|
/* quic.cyu.string sticky buffer */
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_STRING].name = KEYWORD_NAME;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_STRING].desc =
|
||||||
|
"sticky buffer to match on the QUIC CYU string";
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_STRING].url = "/rules/" KEYWORD_DOC;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_STRING].Setup = DetectQuicCyuStringSetup;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_STRING].flags |= SIGMATCH_NOOPT;
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_CYU_STRING].RegisterTests = DetectQuicCyuStringRegisterTests;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DetectAppLayerMpmRegister2(
|
||||||
|
BUFFER_NAME, SIG_FLAG_TOSERVER, 2, PrefilterMpmListIdRegister, NULL, ALPROTO_QUIC, 1);
|
||||||
|
|
||||||
|
DetectAppLayerInspectEngineRegister2(
|
||||||
|
BUFFER_NAME, ALPROTO_QUIC, SIG_FLAG_TOSERVER, 0, DetectEngineInspectQuicString, NULL);
|
||||||
|
|
||||||
|
DetectBufferTypeSetDescriptionByName(BUFFER_NAME, BUFFER_DESC);
|
||||||
|
|
||||||
|
g_buffer_id = DetectBufferTypeGetByName(BUFFER_NAME);
|
||||||
|
|
||||||
|
SCLogDebug("registering " BUFFER_NAME " rule option");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
#include "app-layer-parser.h"
|
||||||
|
#include "util-unittest.h"
|
||||||
|
#include "util-unittest-helper.h"
|
||||||
|
#include "flow-util.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \test DetectQuicCyuStringTest01 is a test for a valid quic packet, matching
|
||||||
|
* on the cyu string
|
||||||
|
*
|
||||||
|
* \retval 1 on success
|
||||||
|
* \retval 0 on failure
|
||||||
|
*/
|
||||||
|
static int DetectQuicCyuStringTest01(void)
|
||||||
|
{
|
||||||
|
/* quic packet */
|
||||||
|
uint8_t buf[] = { 0xc3, 0x51, 0x30, 0x34, 0x36, 0x50, 0x76, 0xd8, 0x63, 0xb7, 0x54, 0xf7, 0xab,
|
||||||
|
0x32, 0x00, 0x00, 0x00, 0x01, 0x54, 0xfd, 0xf4, 0x79, 0x48, 0x76, 0xd0, 0x87, 0x58, 0x8d,
|
||||||
|
0x26, 0x8f, 0xa0, 0x01, 0x04, 0x00, 0x43, 0x48, 0x4c, 0x4f, 0x11, 0x00, 0x00, 0x00, 0x50,
|
||||||
|
0x41, 0x44, 0x00, 0xe4, 0x02, 0x00, 0x00, 0x53, 0x4e, 0x49, 0x00, 0xf7, 0x02, 0x00, 0x00,
|
||||||
|
0x56, 0x45, 0x52, 0x00, 0xfb, 0x02, 0x00, 0x00, 0x43, 0x43, 0x53, 0x00, 0x0b, 0x03, 0x00,
|
||||||
|
0x00, 0x55, 0x41, 0x49, 0x44, 0x2c, 0x03, 0x00, 0x00, 0x54, 0x43, 0x49, 0x44, 0x30, 0x03,
|
||||||
|
0x00, 0x00, 0x50, 0x44, 0x4d, 0x44, 0x34, 0x03, 0x00, 0x00, 0x53, 0x4d, 0x48, 0x4c, 0x38,
|
||||||
|
0x03, 0x00, 0x00, 0x49, 0x43, 0x53, 0x4c, 0x3c, 0x03, 0x00, 0x00, 0x4e, 0x4f, 0x4e, 0x50,
|
||||||
|
0x5c, 0x03, 0x00, 0x00, 0x4d, 0x49, 0x44, 0x53, 0x60, 0x03, 0x00, 0x00, 0x53, 0x43, 0x4c,
|
||||||
|
0x53, 0x64, 0x03, 0x00, 0x00, 0x43, 0x53, 0x43, 0x54, 0x64, 0x03, 0x00, 0x00, 0x43, 0x4f,
|
||||||
|
0x50, 0x54, 0x64, 0x03, 0x00, 0x00, 0x49, 0x52, 0x54, 0x54, 0x68, 0x03, 0x00, 0x00, 0x43,
|
||||||
|
0x46, 0x43, 0x57, 0x6c, 0x03, 0x00, 0x00, 0x53, 0x46, 0x43, 0x57, 0x70, 0x03, 0x00, 0x00,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d,
|
||||||
|
0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x31, 0x2e, 0x67,
|
||||||
|
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x51, 0x30, 0x34, 0x36, 0x01, 0xe8,
|
||||||
|
0x81, 0x60, 0x92, 0x92, 0x1a, 0xe8, 0x7e, 0xed, 0x80, 0x86, 0xa2, 0x15, 0x82, 0x91, 0x43,
|
||||||
|
0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x37, 0x39, 0x2e, 0x30, 0x2e, 0x33, 0x39, 0x34, 0x35,
|
||||||
|
0x2e, 0x31, 0x31, 0x37, 0x20, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x78, 0x38, 0x36, 0x5f,
|
||||||
|
0x36, 0x34, 0x00, 0x00, 0x00, 0x00, 0x58, 0x35, 0x30, 0x39, 0x01, 0x00, 0x00, 0x00, 0x1e,
|
||||||
|
0x00, 0x00, 0x00, 0x82, 0x88, 0x09, 0x00, 0xfa, 0x0f, 0xde, 0xb7, 0x2e, 0x7e, 0x6c, 0x78,
|
||||||
|
0xcc, 0x09, 0x65, 0xab, 0x06, 0x0c, 0x31, 0x05, 0xfa, 0xd9, 0xa2, 0x0b, 0xdd, 0x74, 0x5c,
|
||||||
|
0x28, 0xdf, 0x7b, 0x74, 0x23, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1d, 0x43,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00 };
|
||||||
|
|
||||||
|
Flow f;
|
||||||
|
void *quic_state = NULL;
|
||||||
|
Packet *p = NULL;
|
||||||
|
Signature *s = NULL;
|
||||||
|
ThreadVars tv;
|
||||||
|
DetectEngineThreadCtx *det_ctx = NULL;
|
||||||
|
AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
|
||||||
|
|
||||||
|
memset(&tv, 0, sizeof(ThreadVars));
|
||||||
|
memset(&f, 0, sizeof(Flow));
|
||||||
|
|
||||||
|
p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP, "192.168.1.5", "192.168.1.1", 41424, 443);
|
||||||
|
|
||||||
|
FLOW_INITIALIZE(&f);
|
||||||
|
f.flags |= FLOW_IPV4;
|
||||||
|
f.proto = IPPROTO_UDP;
|
||||||
|
f.protomap = FlowGetProtoMapping(f.proto);
|
||||||
|
|
||||||
|
p->flow = &f;
|
||||||
|
p->flags |= PKT_HAS_FLOW;
|
||||||
|
p->flowflags |= FLOW_PKT_TOSERVER;
|
||||||
|
f.alproto = ALPROTO_QUIC;
|
||||||
|
|
||||||
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||||
|
FAIL_IF_NULL(de_ctx);
|
||||||
|
de_ctx->mpm_matcher = mpm_default_matcher;
|
||||||
|
de_ctx->flags |= DE_QUIET;
|
||||||
|
|
||||||
|
s = DetectEngineAppendSig(de_ctx, "alert quic any any -> any any "
|
||||||
|
"(msg:\"Test QUIC CYU string\"; "
|
||||||
|
"quic.cyu.string; "
|
||||||
|
"content:\"46,PAD-SNI-VER-CCS-UAID-TCID-PDMD-SMHL-ICSL-NONP-"
|
||||||
|
"MIDS-SCLS-CSCT-COPT-IRTT-CFCW-SFCW\"; "
|
||||||
|
"sid:1;)");
|
||||||
|
FAIL_IF_NULL(s);
|
||||||
|
|
||||||
|
SigGroupBuild(de_ctx);
|
||||||
|
DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
|
||||||
|
|
||||||
|
FLOWLOCK_WRLOCK(&f);
|
||||||
|
int r = AppLayerParserParse(
|
||||||
|
NULL, alp_tctx, &f, ALPROTO_QUIC, STREAM_TOSERVER, buf, sizeof(buf));
|
||||||
|
if (r != 0) {
|
||||||
|
printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
|
||||||
|
FLOWLOCK_UNLOCK(&f);
|
||||||
|
FAIL;
|
||||||
|
}
|
||||||
|
FLOWLOCK_UNLOCK(&f);
|
||||||
|
|
||||||
|
quic_state = f.alstate;
|
||||||
|
FAIL_IF_NULL(quic_state);
|
||||||
|
|
||||||
|
/* do detect */
|
||||||
|
SigMatchSignatures(&tv, de_ctx, det_ctx, p);
|
||||||
|
|
||||||
|
if (!(PacketAlertCheck(p, 1))) {
|
||||||
|
printf("sig 1 didn't alert, but it should have: ");
|
||||||
|
FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alp_tctx != NULL)
|
||||||
|
AppLayerParserThreadCtxFree(alp_tctx);
|
||||||
|
if (det_ctx != NULL)
|
||||||
|
DetectEngineThreadCtxDeinit(&tv, det_ctx);
|
||||||
|
if (de_ctx != NULL)
|
||||||
|
SigGroupCleanup(de_ctx);
|
||||||
|
if (de_ctx != NULL)
|
||||||
|
DetectEngineCtxFree(de_ctx);
|
||||||
|
|
||||||
|
FLOW_DESTROY(&f);
|
||||||
|
UTHFreePacket(p);
|
||||||
|
PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief this function registers unit tests for Quic Cyu String
|
||||||
|
*/
|
||||||
|
static void DetectQuicCyuStringRegisterTests(void)
|
||||||
|
{
|
||||||
|
UtRegisterTest("DetectQuicCyuStringTest01", DetectQuicCyuStringTest01);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* UNITTESTS */
|
@ -0,0 +1,28 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DETECT_QUIC_CYU_STRING_H__
|
||||||
|
#define __DETECT_QUIC_CYU_STRING_H__
|
||||||
|
|
||||||
|
void DetectQuicCyuStringRegister(void);
|
||||||
|
|
||||||
|
#endif /* __DETECT_QUIC_CYU_STRING_H__ */
|
@ -0,0 +1,260 @@
|
|||||||
|
/* Copyright (C) 2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Implements the quic.version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "conf.h"
|
||||||
|
#include "detect.h"
|
||||||
|
#include "detect-parse.h"
|
||||||
|
#include "detect-engine.h"
|
||||||
|
#include "detect-engine-content-inspection.h"
|
||||||
|
#include "detect-engine-uint.h"
|
||||||
|
#include "detect-quic-version.h"
|
||||||
|
#include "util-byte.h"
|
||||||
|
#include "util-unittest.h"
|
||||||
|
#include "rust.h"
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
static void DetectQuicVersionRegisterTests(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int quic_version_id = 0;
|
||||||
|
|
||||||
|
static int DetectQuicVersionMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags,
|
||||||
|
void *state, void *txv, const Signature *s, const SigMatchCtx *ctx);
|
||||||
|
static int DetectQuicVersionSetup(DetectEngineCtx *, Signature *, const char *);
|
||||||
|
void DetectQuicVersionFree(DetectEngineCtx *de_ctx, void *);
|
||||||
|
|
||||||
|
static int DetectEngineInspectQuicVersionGeneric(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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Registration function for quic.version: keyword
|
||||||
|
*/
|
||||||
|
void DetectQuicVersionRegister(void)
|
||||||
|
{
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].name = "quic.version";
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].desc = "match Quic version";
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].url = "/rules/quic-keywords.html#quic-version";
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].AppLayerTxMatch = DetectQuicVersionMatch;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].Setup = DetectQuicVersionSetup;
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].Free = DetectQuicVersionFree;
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
sigmatch_table[DETECT_AL_QUIC_VERSION].RegisterTests = DetectQuicVersionRegisterTests;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DetectAppLayerInspectEngineRegister2("quic.version", ALPROTO_QUIC, SIG_FLAG_TOSERVER, 1,
|
||||||
|
DetectEngineInspectQuicVersionGeneric, NULL);
|
||||||
|
|
||||||
|
quic_version_id = DetectBufferTypeGetByName("quic.version");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DetectEngineInspectQuicVersionGeneric(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)
|
||||||
|
{
|
||||||
|
return DetectEngineInspectGenericList(
|
||||||
|
de_ctx, det_ctx, s, engine->smd, f, flags, alstate, txv, tx_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \brief Function to match protocol version of an Quic Tx
|
||||||
|
*
|
||||||
|
* \param det_ctx Pointer to the pattern matcher thread.
|
||||||
|
* \param f Pointer to the current flow.
|
||||||
|
* \param flags Flags.
|
||||||
|
* \param state App layer state.
|
||||||
|
* \param txv Pointer to the transaction.
|
||||||
|
* \param s Pointer to the Signature.
|
||||||
|
* \param ctx Pointer to the sigmatch that we will cast into DetectQuicVersionData.
|
||||||
|
*
|
||||||
|
* \retval 0 no match.
|
||||||
|
* \retval 1 match.
|
||||||
|
*/
|
||||||
|
static int DetectQuicVersionMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags,
|
||||||
|
void *state, void *txv, const Signature *s, const SigMatchCtx *ctx)
|
||||||
|
{
|
||||||
|
const DetectU32Data *de = (const DetectU32Data *)ctx;
|
||||||
|
uint32_t version;
|
||||||
|
|
||||||
|
version = rs_quic_tx_get_version(txv);
|
||||||
|
|
||||||
|
return DetectU32Match(version, de);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \brief this function is used to add the parsed sigmatch into the current signature
|
||||||
|
*
|
||||||
|
* \param de_ctx pointer to the Detection Engine Context
|
||||||
|
* \param s pointer to the Current Signature
|
||||||
|
* \param rawstr pointer to the user provided options
|
||||||
|
*
|
||||||
|
* \retval 0 on Success
|
||||||
|
* \retval -1 on Failure
|
||||||
|
*/
|
||||||
|
static int DetectQuicVersionSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
|
||||||
|
{
|
||||||
|
SigMatch *sm = NULL;
|
||||||
|
DetectU32Data *de = NULL;
|
||||||
|
|
||||||
|
if (DetectSignatureSetAppProto(s, ALPROTO_QUIC) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
de = DetectU32Parse(rawstr);
|
||||||
|
if (de == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sm = SigMatchAlloc();
|
||||||
|
if (sm == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
sm->type = DETECT_AL_QUIC_VERSION;
|
||||||
|
sm->ctx = (SigMatchCtx *)de;
|
||||||
|
|
||||||
|
SigMatchAppendSMToList(s, sm, quic_version_id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (de != NULL)
|
||||||
|
SCFree(de);
|
||||||
|
if (sm != NULL)
|
||||||
|
SCFree(sm);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \internal
|
||||||
|
* \brief this function will free memory associated with DetectQuicVersionData
|
||||||
|
*
|
||||||
|
* \param de pointer to DetectQuicVersionData
|
||||||
|
*/
|
||||||
|
void DetectQuicVersionFree(DetectEngineCtx *de_ctx, void *de_ptr)
|
||||||
|
{
|
||||||
|
if (de_ptr != NULL)
|
||||||
|
SCFree(de_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef UNITTESTS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \test QuicVersionTestParse01 is a test for a valid value
|
||||||
|
*
|
||||||
|
* \retval 1 on success
|
||||||
|
* \retval 0 on failure
|
||||||
|
*/
|
||||||
|
static int QuicVersionTestParse01(void)
|
||||||
|
{
|
||||||
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||||
|
FAIL_IF_NULL(de_ctx);
|
||||||
|
|
||||||
|
Signature *sig = DetectEngineAppendSig(
|
||||||
|
de_ctx, "alert ip any any -> any any (quic.version:3; sid:1; rev:1;)");
|
||||||
|
FAIL_IF_NULL(sig);
|
||||||
|
|
||||||
|
sig = DetectEngineAppendSig(
|
||||||
|
de_ctx, "alert ip any any -> any any (quic.version:3; sid:2; rev:1;)");
|
||||||
|
FAIL_IF_NULL(sig);
|
||||||
|
|
||||||
|
DetectEngineCtxFree(de_ctx);
|
||||||
|
|
||||||
|
PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \test QuicVersionTestParse02 is a test for a valid value
|
||||||
|
*
|
||||||
|
* \retval 1 on success
|
||||||
|
* \retval 0 on failure
|
||||||
|
*/
|
||||||
|
static int QuicVersionTestParse02(void)
|
||||||
|
{
|
||||||
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||||
|
FAIL_IF_NULL(de_ctx);
|
||||||
|
|
||||||
|
Signature *sig = DetectEngineAppendSig(
|
||||||
|
de_ctx, "alert ip any any -> any any (quic.version:>3; sid:1; rev:1;)");
|
||||||
|
FAIL_IF_NULL(sig);
|
||||||
|
|
||||||
|
sig = DetectEngineAppendSig(
|
||||||
|
de_ctx, "alert ip any any -> any any (quic.version:<44; sid:2; rev:1;)");
|
||||||
|
FAIL_IF_NULL(sig);
|
||||||
|
|
||||||
|
DetectEngineCtxFree(de_ctx);
|
||||||
|
|
||||||
|
PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \test QuicVersionTestParse03 is a test for an invalid value
|
||||||
|
*
|
||||||
|
* \retval 1 on success
|
||||||
|
* \retval 0 on failure
|
||||||
|
*/
|
||||||
|
static int QuicVersionTestParse03(void)
|
||||||
|
{
|
||||||
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||||
|
FAIL_IF_NULL(de_ctx);
|
||||||
|
|
||||||
|
Signature *sig = DetectEngineAppendSig(
|
||||||
|
de_ctx, "alert ip any any -> any any (quic.version:; sid:1; rev:1;)");
|
||||||
|
FAIL_IF_NOT_NULL(sig);
|
||||||
|
|
||||||
|
DetectEngineCtxFree(de_ctx);
|
||||||
|
|
||||||
|
PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \test QuicVersionTestParse04 is a test for an invalid value
|
||||||
|
*
|
||||||
|
* \retval 1 on success
|
||||||
|
* \retval 0 on failure
|
||||||
|
*/
|
||||||
|
static int QuicVersionTestParse04(void)
|
||||||
|
{
|
||||||
|
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||||
|
FAIL_IF_NULL(de_ctx);
|
||||||
|
|
||||||
|
Signature *sig = DetectEngineAppendSig(
|
||||||
|
de_ctx, "alert ip any any -> any any (quic.version:<4294967296; sid:1; rev:1;)");
|
||||||
|
FAIL_IF_NOT_NULL(sig);
|
||||||
|
|
||||||
|
DetectEngineCtxFree(de_ctx);
|
||||||
|
|
||||||
|
PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief this function registers unit tests for QuicVersion
|
||||||
|
*/
|
||||||
|
void DetectQuicVersionRegisterTests(void)
|
||||||
|
{
|
||||||
|
UtRegisterTest("QuicVersionTestParse01", QuicVersionTestParse01);
|
||||||
|
UtRegisterTest("QuicVersionTestParse02", QuicVersionTestParse02);
|
||||||
|
UtRegisterTest("QuicVersionTestParse03", QuicVersionTestParse03);
|
||||||
|
UtRegisterTest("QuicVersionTestParse04", QuicVersionTestParse04);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* UNITTESTS */
|
@ -0,0 +1,28 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DETECT_QUIC_VERSION_H__
|
||||||
|
#define __DETECT_QUIC_VERSION_H__
|
||||||
|
|
||||||
|
void DetectQuicVersionRegister(void);
|
||||||
|
|
||||||
|
#endif /* __DETECT_QUIC_VERSION_H__ */
|
@ -0,0 +1,165 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*
|
||||||
|
* Implements JSON/eve logging for Quic app-layer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "suricata-common.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "detect.h"
|
||||||
|
#include "pkt-var.h"
|
||||||
|
#include "conf.h"
|
||||||
|
#include "threads.h"
|
||||||
|
#include "threadvars.h"
|
||||||
|
#include "tm-threads.h"
|
||||||
|
#include "util-unittest.h"
|
||||||
|
#include "util-buffer.h"
|
||||||
|
#include "util-debug.h"
|
||||||
|
#include "util-byte.h"
|
||||||
|
#include "output.h"
|
||||||
|
#include "output-json.h"
|
||||||
|
#include "app-layer.h"
|
||||||
|
#include "app-layer-parser.h"
|
||||||
|
#include "output-json-quic.h"
|
||||||
|
#include "rust.h"
|
||||||
|
|
||||||
|
typedef struct LogQuicFileCtx_ {
|
||||||
|
LogFileCtx *file_ctx;
|
||||||
|
OutputJsonCtx *eve_ctx;
|
||||||
|
} LogQuicFileCtx;
|
||||||
|
|
||||||
|
typedef struct JsonQuicLogThread_ {
|
||||||
|
LogQuicFileCtx *quiclog_ctx;
|
||||||
|
OutputJsonThreadCtx *ctx;
|
||||||
|
} JsonQuicLogThread;
|
||||||
|
|
||||||
|
static int JsonQuicLogger(ThreadVars *tv, void *thread_data, const Packet *p, Flow *f, void *state,
|
||||||
|
void *tx, uint64_t tx_id)
|
||||||
|
{
|
||||||
|
JsonQuicLogThread *thread = thread_data;
|
||||||
|
|
||||||
|
JsonBuilder *js =
|
||||||
|
CreateEveHeader(p, LOG_DIR_PACKET, "quic", NULL, thread->quiclog_ctx->eve_ctx);
|
||||||
|
if (unlikely(js == NULL)) {
|
||||||
|
return TM_ECODE_OK;
|
||||||
|
}
|
||||||
|
if (!rs_quic_to_json(tx, js)) {
|
||||||
|
jb_free(js);
|
||||||
|
return TM_ECODE_FAILED;
|
||||||
|
}
|
||||||
|
OutputJsonBuilderBuffer(js, thread->ctx);
|
||||||
|
|
||||||
|
jb_free(js);
|
||||||
|
return TM_ECODE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OutputQuicLogDeInitCtxSub(OutputCtx *output_ctx)
|
||||||
|
{
|
||||||
|
LogQuicFileCtx *quiclog_ctx = (LogQuicFileCtx *)output_ctx->data;
|
||||||
|
SCFree(quiclog_ctx);
|
||||||
|
SCFree(output_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static OutputInitResult OutputQuicLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
|
||||||
|
{
|
||||||
|
OutputInitResult result = { NULL, false };
|
||||||
|
OutputJsonCtx *ajt = parent_ctx->data;
|
||||||
|
|
||||||
|
LogQuicFileCtx *quiclog_ctx = SCCalloc(1, sizeof(*quiclog_ctx));
|
||||||
|
if (unlikely(quiclog_ctx == NULL)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
quiclog_ctx->file_ctx = ajt->file_ctx;
|
||||||
|
quiclog_ctx->eve_ctx = ajt;
|
||||||
|
|
||||||
|
OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
|
||||||
|
if (unlikely(output_ctx == NULL)) {
|
||||||
|
SCFree(quiclog_ctx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
output_ctx->data = quiclog_ctx;
|
||||||
|
output_ctx->DeInit = OutputQuicLogDeInitCtxSub;
|
||||||
|
|
||||||
|
AppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_QUIC);
|
||||||
|
|
||||||
|
result.ctx = output_ctx;
|
||||||
|
result.ok = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TmEcode JsonQuicLogThreadInit(ThreadVars *t, const void *initdata, void **data)
|
||||||
|
{
|
||||||
|
if (initdata == NULL) {
|
||||||
|
SCLogDebug("Error getting context for EveLogQuic. \"initdata\" is NULL.");
|
||||||
|
return TM_ECODE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonQuicLogThread *thread = SCCalloc(1, sizeof(*thread));
|
||||||
|
if (unlikely(thread == NULL)) {
|
||||||
|
return TM_ECODE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->quiclog_ctx = ((OutputCtx *)initdata)->data;
|
||||||
|
thread->ctx = CreateEveThreadCtx(t, thread->quiclog_ctx->eve_ctx);
|
||||||
|
if (thread->ctx == NULL) {
|
||||||
|
goto error_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data = (void *)thread;
|
||||||
|
return TM_ECODE_OK;
|
||||||
|
|
||||||
|
error_exit:
|
||||||
|
SCFree(thread);
|
||||||
|
return TM_ECODE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TmEcode JsonQuicLogThreadDeinit(ThreadVars *t, void *data)
|
||||||
|
{
|
||||||
|
JsonQuicLogThread *thread = (JsonQuicLogThread *)data;
|
||||||
|
if (thread == NULL) {
|
||||||
|
return TM_ECODE_OK;
|
||||||
|
}
|
||||||
|
FreeEveThreadCtx(thread->ctx);
|
||||||
|
SCFree(thread);
|
||||||
|
return TM_ECODE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JsonQuicAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js)
|
||||||
|
{
|
||||||
|
void *state = FlowGetAppState(f);
|
||||||
|
if (state) {
|
||||||
|
void *tx = AppLayerParserGetTx(f->proto, ALPROTO_QUIC, state, tx_id);
|
||||||
|
if (tx) {
|
||||||
|
return rs_quic_to_json(tx, js);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonQuicLogRegister(void)
|
||||||
|
{
|
||||||
|
/* Register as an eve sub-module. */
|
||||||
|
OutputRegisterTxSubModule(LOGGER_JSON_QUIC, "eve-log", "JsonQuicLog", "eve-log.quic",
|
||||||
|
OutputQuicLogInitSub, ALPROTO_QUIC, JsonQuicLogger, JsonQuicLogThreadInit,
|
||||||
|
JsonQuicLogThreadDeinit, NULL);
|
||||||
|
|
||||||
|
SCLogDebug("quic json logger registered.");
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
/* Copyright (C) 2021 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __OUTPUT_JSON_QUIC_H__
|
||||||
|
#define __OUTPUT_JSON_QUIC_H__
|
||||||
|
|
||||||
|
bool JsonQuicAddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *js);
|
||||||
|
void JsonQuicLogRegister(void);
|
||||||
|
|
||||||
|
#endif /* __OUTPUT_JSON_QUIC_H__ */
|
Loading…
Reference in New Issue