http2: initial support

pull/5282/head
Philippe Antoine 5 years ago committed by Victor Julien
parent 999af4f62a
commit 1422b18a99

@ -0,0 +1,15 @@
# HTTP2 app layer event rules
#
# SID's fall in the 2290000+ range. See https://redmine.openinfosecfoundation.org/projects/suricata/wiki/AppLayer
#
# These sigs fire at most once per connection.
#
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid frame header"; flow:established; app-layer-event:http2.invalid_frame_header; classtype:protocol-command-decode; sid:2290000; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid client magic"; flow:established; app-layer-event:http2.invalid_client_magic; classtype:protocol-command-decode; sid:2290001; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid frame data"; flow:established; app-layer-event:http2.invalid_frame_data; classtype:protocol-command-decode; sid:2290002; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid header"; flow:established; app-layer-event:http2.invalid_header; classtype:protocol-command-decode; sid:2290003; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid frame length"; flow:established; app-layer-event:http2.invalid_frame_length; classtype:protocol-command-decode; sid:2290004; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 header frame with extra data"; flow:established; app-layer-event:http2.extra_header_data; classtype:protocol-command-decode; sid:2290005; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 too long frame data"; flow:established; app-layer-event:http2.long_frame_data; classtype:protocol-command-decode; sid:2290006; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 stream identifier reuse"; flow:established; app-layer-event:http2.stream_id_reuse; classtype:protocol-command-decode; sid:2290007; rev:1;)

@ -21,6 +21,11 @@ use std::os::raw::{c_void};
use crate::log::*;
use crate::core::*;
// Defined in util-file.h
extern {
pub fn FileFlowToFlags(flow: *const Flow, flags: u8) -> u16;
}
pub struct File;
#[repr(C)]
#[derive(Debug)]

@ -52,7 +52,7 @@ impl FileChunk {
#[derive(Debug)]
pub struct FileTransferTracker {
file_size: u64,
tracked: u64,
pub tracked: u64,
cur_ooo: u64, // how many bytes do we have queued from ooo chunks
track_id: u32,
chunk_left: u32,

@ -0,0 +1,542 @@
/* Copyright (C) 2020 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::http2::{HTTP2FrameTypeData, HTTP2Transaction};
use super::parser;
use crate::core::STREAM_TOSERVER;
use std::ffi::CStr;
use std::mem::transmute;
use std::str::FromStr;
fn http2_tx_has_frametype(
tx: &mut HTTP2Transaction, direction: u8, value: u8,
) -> std::os::raw::c_int {
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
if tx.frames_ts[i].header.ftype as u8 == value {
return 1;
}
}
} else {
for i in 0..tx.frames_tc.len() {
if tx.frames_tc[i].header.ftype as u8 == value {
return 1;
}
}
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_http2_tx_has_frametype(
tx: *mut std::os::raw::c_void, direction: u8, value: u8,
) -> std::os::raw::c_int {
let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_tx_has_frametype(tx, direction, value);
}
#[no_mangle]
pub unsafe extern "C" fn rs_http2_parse_frametype(
str: *const std::os::raw::c_char,
) -> std::os::raw::c_int {
let ft_name: &CStr = CStr::from_ptr(str); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Ok(x) = parser::HTTP2FrameType::from_str(s) {
return x as i32;
}
}
return -1;
}
fn http2_tx_has_errorcode(
tx: &mut HTTP2Transaction, direction: u8, code: u32,
) -> std::os::raw::c_int {
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match tx.frames_ts[i].data {
HTTP2FrameTypeData::GOAWAY(goaway) => {
if goaway.errorcode == code {
return 1;
}
}
HTTP2FrameTypeData::RSTSTREAM(rst) => {
if rst.errorcode == code {
return 1;
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match tx.frames_tc[i].data {
HTTP2FrameTypeData::GOAWAY(goaway) => {
if goaway.errorcode as u32 == code {
return 1;
}
}
HTTP2FrameTypeData::RSTSTREAM(rst) => {
if rst.errorcode as u32 == code {
return 1;
}
}
_ => {}
}
}
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_http2_tx_has_errorcode(
tx: *mut std::os::raw::c_void, direction: u8, code: u32,
) -> std::os::raw::c_int {
let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_tx_has_errorcode(tx, direction, code);
}
#[no_mangle]
pub unsafe extern "C" fn rs_http2_parse_errorcode(
str: *const std::os::raw::c_char,
) -> std::os::raw::c_int {
let ft_name: &CStr = CStr::from_ptr(str); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Ok(x) = parser::HTTP2ErrorCode::from_str(s) {
return x as i32;
}
}
return -1;
}
fn http2_tx_get_next_priority(
tx: &mut HTTP2Transaction, direction: u8, nb: u32,
) -> std::os::raw::c_int {
let mut pos = 0 as u32;
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match &tx.frames_ts[i].data {
HTTP2FrameTypeData::PRIORITY(prio) => {
if pos == nb {
return prio.weight as i32;
} else {
pos = pos + 1;
}
}
HTTP2FrameTypeData::HEADERS(hd) => {
if let Some(prio) = hd.priority {
if pos == nb {
return prio.weight as i32;
} else {
pos = pos + 1;
}
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match &tx.frames_tc[i].data {
HTTP2FrameTypeData::PRIORITY(prio) => {
if pos == nb {
return prio.weight as i32;
} else {
pos = pos + 1;
}
}
HTTP2FrameTypeData::HEADERS(hd) => {
if let Some(prio) = hd.priority {
if pos == nb {
return prio.weight as i32;
} else {
pos = pos + 1;
}
}
}
_ => {}
}
}
}
return -1;
}
#[no_mangle]
pub extern "C" fn rs_http2_tx_get_next_priority(
tx: *mut std::os::raw::c_void, direction: u8, nb: u32,
) -> std::os::raw::c_int {
let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_tx_get_next_priority(tx, direction, nb);
}
fn http2_tx_get_next_window(
tx: &mut HTTP2Transaction, direction: u8, nb: u32,
) -> std::os::raw::c_int {
let mut pos = 0 as u32;
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match tx.frames_ts[i].data {
HTTP2FrameTypeData::WINDOWUPDATE(wu) => {
if pos == nb {
return wu.sizeinc as i32;
} else {
pos = pos + 1;
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match tx.frames_tc[i].data {
HTTP2FrameTypeData::WINDOWUPDATE(wu) => {
if pos == nb {
return wu.sizeinc as i32;
} else {
pos = pos + 1;
}
}
_ => {}
}
}
}
return -1;
}
#[no_mangle]
pub extern "C" fn rs_http2_tx_get_next_window(
tx: *mut std::os::raw::c_void, direction: u8, nb: u32,
) -> std::os::raw::c_int {
let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_tx_get_next_window(tx, direction, nb);
}
#[no_mangle]
pub unsafe extern "C" fn rs_http2_parse_settingsid(
str: *const std::os::raw::c_char,
) -> std::os::raw::c_int {
let ft_name: &CStr = CStr::from_ptr(str); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Ok(x) = parser::HTTP2SettingsId::from_str(s) {
return x as i32;
}
}
return -1;
}
#[no_mangle]
pub unsafe extern "C" fn rs_http2_detect_settingsctx_parse(
str: *const std::os::raw::c_char,
) -> *mut std::os::raw::c_void {
let ft_name: &CStr = CStr::from_ptr(str); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Ok((_, ctx)) = parser::http2_parse_settingsctx(s) {
let boxed = Box::new(ctx);
return transmute(boxed); //unsafe
}
}
return std::ptr::null_mut();
}
#[no_mangle]
pub unsafe extern "C" fn rs_http2_detect_settingsctx_free(ctx: *mut std::os::raw::c_void) {
// Just unbox...
let _ctx: Box<parser::DetectHTTP2settingsSigCtx> = transmute(ctx);
}
fn http2_detect_settings_match(
set: &Vec<parser::HTTP2FrameSettings>, ctx: &parser::DetectHTTP2settingsSigCtx,
) -> std::os::raw::c_int {
for i in 0..set.len() {
if set[i].id == ctx.id {
match &ctx.value {
None => {
return 1;
}
Some(x) => match x.mode {
parser::DetectUintMode::DetectUintModeEqual => {
if set[i].value == x.value {
return 1;
}
}
parser::DetectUintMode::DetectUintModeLt => {
if set[i].value <= x.value {
return 1;
}
}
parser::DetectUintMode::DetectUintModeGt => {
if set[i].value >= x.value {
return 1;
}
}
parser::DetectUintMode::DetectUintModeRange => {
if set[i].value <= x.value && set[i].value >= x.valrange {
return 1;
}
}
},
}
}
}
return 0;
}
fn http2_detect_settingsctx_match(
ctx: &mut parser::DetectHTTP2settingsSigCtx, tx: &mut HTTP2Transaction, direction: u8,
) -> std::os::raw::c_int {
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match &tx.frames_ts[i].data {
HTTP2FrameTypeData::SETTINGS(set) => {
if http2_detect_settings_match(&set, ctx) != 0 {
return 1;
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match &tx.frames_tc[i].data {
HTTP2FrameTypeData::SETTINGS(set) => {
if http2_detect_settings_match(&set, ctx) != 0 {
return 1;
}
}
_ => {}
}
}
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_http2_detect_settingsctx_match(
ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
) -> std::os::raw::c_int {
let ctx = cast_pointer!(ctx, parser::DetectHTTP2settingsSigCtx);
let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_detect_settingsctx_match(ctx, tx, direction);
}
#[no_mangle]
pub unsafe extern "C" fn rs_detect_u64_parse(
str: *const std::os::raw::c_char,
) -> *mut std::os::raw::c_void {
let ft_name: &CStr = CStr::from_ptr(str); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Ok((_, ctx)) = parser::detect_parse_u64(s) {
let boxed = Box::new(ctx);
return transmute(boxed); //unsafe
}
}
return std::ptr::null_mut();
}
#[no_mangle]
pub unsafe extern "C" fn rs_detect_u64_free(ctx: *mut std::os::raw::c_void) {
// Just unbox...
let _ctx: Box<parser::DetectU64Data> = transmute(ctx);
}
fn http2_detect_sizeupdate_match(
hd: &parser::HTTP2FrameHeaders, ctx: &parser::DetectU64Data,
) -> std::os::raw::c_int {
for i in 0..hd.blocks.len() {
match ctx.mode {
parser::DetectUintMode::DetectUintModeEqual => {
if hd.blocks[i].sizeupdate == ctx.value
&& hd.blocks[i].error
== parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
{
return 1;
}
}
parser::DetectUintMode::DetectUintModeLt => {
if hd.blocks[i].sizeupdate <= ctx.value
&& hd.blocks[i].error
== parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
{
return 1;
}
}
parser::DetectUintMode::DetectUintModeGt => {
if hd.blocks[i].sizeupdate >= ctx.value
&& hd.blocks[i].error
== parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
{
return 1;
}
}
parser::DetectUintMode::DetectUintModeRange => {
if hd.blocks[i].sizeupdate <= ctx.value
&& hd.blocks[i].sizeupdate >= ctx.valrange
&& hd.blocks[i].error
== parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
{
return 1;
}
}
}
}
return 0;
}
fn http2_detect_sizeupdatectx_match(
ctx: &mut parser::DetectU64Data, tx: &mut HTTP2Transaction, direction: u8,
) -> std::os::raw::c_int {
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match &tx.frames_ts[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if http2_detect_sizeupdate_match(&hd, ctx) != 0 {
return 1;
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match &tx.frames_tc[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if http2_detect_sizeupdate_match(&hd, ctx) != 0 {
return 1;
}
}
_ => {}
}
}
}
return 0;
}
#[no_mangle]
pub extern "C" fn rs_http2_detect_sizeupdatectx_match(
ctx: *const std::os::raw::c_void, tx: *mut std::os::raw::c_void, direction: u8,
) -> std::os::raw::c_int {
let ctx = cast_pointer!(ctx, parser::DetectU64Data);
let tx = cast_pointer!(tx, HTTP2Transaction);
return http2_detect_sizeupdatectx_match(ctx, tx, direction);
}
//TODOask better syntax between rs_http2_tx_get_header_name in argument
// and rs_http2_detect_sizeupdatectx_match explicitly casting
#[no_mangle]
pub unsafe extern "C" fn rs_http2_tx_get_header_name(
tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
let mut pos = 0 as u32;
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match &tx.frames_ts[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if nb < pos + hd.blocks.len() as u32 {
let value = &hd.blocks[(nb - pos) as usize].name;
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
return 1;
} else {
pos = pos + hd.blocks.len() as u32;
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match &tx.frames_tc[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if nb < pos + hd.blocks.len() as u32 {
let value = &hd.blocks[(nb - pos) as usize].name;
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
return 1;
} else {
pos = pos + hd.blocks.len() as u32;
}
}
_ => {}
}
}
}
return 0;
}
fn http2_escape_header(hd: &parser::HTTP2FrameHeaders, i: u32) -> Vec<u8> {
//minimum size + 2 for escapes
let normalsize = hd.blocks[i as usize].value.len() + 2 + hd.blocks[i as usize].name.len() + 2;
let mut vec = Vec::with_capacity(normalsize);
for j in 0..hd.blocks[i as usize].name.len() {
vec.push(hd.blocks[i as usize].name[j]);
if hd.blocks[i as usize].name[j] == ':' as u8 {
vec.push(':' as u8);
}
}
vec.push(':' as u8);
vec.push(' ' as u8);
for j in 0..hd.blocks[i as usize].value.len() {
vec.push(hd.blocks[i as usize].value[j]);
if hd.blocks[i as usize].value[j] == ':' as u8 {
vec.push(':' as u8);
}
}
return vec;
}
#[no_mangle]
pub unsafe extern "C" fn rs_http2_tx_get_header(
tx: &mut HTTP2Transaction, direction: u8, nb: u32, buffer: *mut *const u8, buffer_len: *mut u32,
) -> u8 {
let mut pos = 0 as u32;
if direction & STREAM_TOSERVER != 0 {
for i in 0..tx.frames_ts.len() {
match &tx.frames_ts[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if nb < pos + hd.blocks.len() as u32 {
tx.escaped_tmp = http2_escape_header(&hd, nb - pos);
let value = &tx.escaped_tmp;
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
return 1;
} else {
pos = pos + hd.blocks.len() as u32;
}
}
_ => {}
}
}
} else {
for i in 0..tx.frames_tc.len() {
match &tx.frames_tc[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if nb < pos + hd.blocks.len() as u32 {
tx.escaped_tmp = http2_escape_header(&hd, nb - pos);
let value = &tx.escaped_tmp;
*buffer = value.as_ptr(); //unsafe
*buffer_len = value.len() as u32;
return 1;
} else {
pos = pos + hd.blocks.len() as u32;
}
}
_ => {}
}
}
}
return 0;
}

@ -0,0 +1,51 @@
/* Copyright (C) 2020 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::core::*;
use crate::filecontainer::*;
/// Wrapper around Suricata's internal file container logic.
#[derive(Debug)]
pub struct HTTP2Files {
pub files_ts: FileContainer,
pub files_tc: FileContainer,
pub flags_ts: u16,
pub flags_tc: u16,
}
impl HTTP2Files {
pub fn new() -> HTTP2Files {
HTTP2Files {
files_ts: FileContainer::default(),
files_tc: FileContainer::default(),
flags_ts: 0,
flags_tc: 0,
}
}
pub fn free(&mut self) {
self.files_ts.free();
self.files_tc.free();
}
pub fn get(&mut self, direction: u8) -> (&mut FileContainer, u16) {
if direction == STREAM_TOSERVER {
(&mut self.files_ts, self.flags_ts)
} else {
(&mut self.files_tc, self.flags_tc)
}
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,504 @@
/* Copyright (C) 2020 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;
use nom::Err;
use nom::IResult;
fn http2_huffman_table_len5(n: u32) -> Option<u8> {
match n {
0 => Some(48),
1 => Some(49),
2 => Some(50),
3 => Some(97),
4 => Some(99),
5 => Some(101),
6 => Some(105),
7 => Some(111),
8 => Some(115),
9 => Some(116),
_ => None,
}
}
named!(http2_decode_huffman_len5<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(5u32), http2_huffman_table_len5) )
);
fn http2_huffman_table_len6(n: u32) -> Option<u8> {
match n {
0x14 => Some(32),
0x15 => Some(37),
0x16 => Some(45),
0x17 => Some(46),
0x18 => Some(47),
0x19 => Some(51),
0x1a => Some(52),
0x1b => Some(53),
0x1c => Some(54),
0x1d => Some(55),
0x1e => Some(56),
0x1f => Some(57),
0x20 => Some(61),
0x21 => Some(65),
0x22 => Some(95),
0x23 => Some(98),
0x24 => Some(100),
0x25 => Some(102),
0x26 => Some(103),
0x27 => Some(104),
0x28 => Some(108),
0x29 => Some(109),
0x2a => Some(110),
0x2b => Some(112),
0x2c => Some(114),
0x2d => Some(117),
_ => None,
}
}
named!(http2_decode_huffman_len6<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(6u32), http2_huffman_table_len6))
);
fn http2_huffman_table_len7(n: u32) -> Option<u8> {
match n {
0x5c => Some(58),
0x5d => Some(66),
0x5e => Some(67),
0x5f => Some(68),
0x60 => Some(69),
0x61 => Some(70),
0x62 => Some(71),
0x63 => Some(72),
0x64 => Some(73),
0x65 => Some(74),
0x66 => Some(75),
0x67 => Some(76),
0x68 => Some(77),
0x69 => Some(78),
0x6a => Some(79),
0x6b => Some(80),
0x6c => Some(81),
0x6d => Some(82),
0x6e => Some(83),
0x6f => Some(84),
0x70 => Some(85),
0x71 => Some(86),
0x72 => Some(87),
0x73 => Some(89),
0x74 => Some(106),
0x75 => Some(107),
0x76 => Some(113),
0x77 => Some(118),
0x78 => Some(119),
0x79 => Some(120),
0x7a => Some(121),
0x7b => Some(122),
_ => None,
}
}
named!(http2_decode_huffman_len7<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(7u32), http2_huffman_table_len7))
);
fn http2_huffman_table_len8(n: u32) -> Option<u8> {
match n {
0xf8 => Some(38),
0xf9 => Some(42),
0xfa => Some(44),
0xfb => Some(59),
0xfc => Some(88),
0xfd => Some(90),
_ => None,
}
}
named!(http2_decode_huffman_len8<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(8u32), http2_huffman_table_len8))
);
fn http2_huffman_table_len10(n: u32) -> Option<u8> {
match n {
0x3f8 => Some(33),
0x3f9 => Some(34),
0x3fa => Some(40),
0x3fb => Some(41),
0x3fc => Some(63),
_ => None,
}
}
named!(http2_decode_huffman_len10<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(10u32), http2_huffman_table_len10))
);
fn http2_huffman_table_len11(n: u32) -> Option<u8> {
match n {
0x7fa => Some(39),
0x7fb => Some(43),
0x7fc => Some(124),
_ => None,
}
}
named!(http2_decode_huffman_len11<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(11u32), http2_huffman_table_len11))
);
fn http2_huffman_table_len12(n: u32) -> Option<u8> {
match n {
0xffa => Some(35),
0xffb => Some(62),
_ => None,
}
}
named!(http2_decode_huffman_len12<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(12u32), http2_huffman_table_len12))
);
fn http2_huffman_table_len13(n: u32) -> Option<u8> {
match n {
0x1ff8 => Some(0),
0x1ff9 => Some(36),
0x1ffa => Some(64),
0x1ffb => Some(91),
0x1ffc => Some(93),
0x1ffd => Some(126),
_ => None,
}
}
named!(http2_decode_huffman_len13<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(13u32), http2_huffman_table_len13))
);
fn http2_huffman_table_len14(n: u32) -> Option<u8> {
match n {
0x3ffc => Some(94),
0x3ffd => Some(125),
_ => None,
}
}
named!(http2_decode_huffman_len14<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(14u32), http2_huffman_table_len14))
);
fn http2_huffman_table_len15(n: u32) -> Option<u8> {
match n {
0x7ffc => Some(60),
0x7ffd => Some(96),
0x7ffe => Some(123),
_ => None,
}
}
named!(http2_decode_huffman_len15<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(15u32), http2_huffman_table_len15))
);
fn http2_huffman_table_len19(n: u32) -> Option<u8> {
match n {
0x7fff0 => Some(92),
0x7fff1 => Some(195),
0x7fff2 => Some(208),
_ => None,
}
}
named!(http2_decode_huffman_len19<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(19u32), http2_huffman_table_len19))
);
fn http2_huffman_table_len20(n: u32) -> Option<u8> {
match n {
0xfffe6 => Some(128),
0xfffe7 => Some(130),
0xfffe8 => Some(131),
0xfffe9 => Some(162),
0xfffea => Some(184),
0xfffeb => Some(194),
0xfffec => Some(224),
0xfffed => Some(226),
_ => None,
}
}
named!(http2_decode_huffman_len20<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(20u32), http2_huffman_table_len20))
);
fn http2_huffman_table_len21(n: u32) -> Option<u8> {
match n {
0x1fffdc => Some(153),
0x1fffdd => Some(161),
0x1fffde => Some(167),
0x1fffdf => Some(172),
0x1fffe0 => Some(176),
0x1fffe1 => Some(177),
0x1fffe2 => Some(179),
0x1fffe3 => Some(209),
0x1fffe4 => Some(216),
0x1fffe5 => Some(217),
0x1fffe6 => Some(227),
0x1fffe7 => Some(229),
0x1fffe8 => Some(230),
_ => None,
}
}
named!(http2_decode_huffman_len21<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(21u32), http2_huffman_table_len21))
);
fn http2_huffman_table_len22(n: u32) -> Option<u8> {
match n {
0x3fffd2 => Some(129),
0x3fffd3 => Some(132),
0x3fffd4 => Some(133),
0x3fffd5 => Some(134),
0x3fffd6 => Some(136),
0x3fffd7 => Some(146),
0x3fffd8 => Some(154),
0x3fffd9 => Some(156),
0x3fffda => Some(160),
0x3fffdb => Some(163),
0x3fffdc => Some(164),
0x3fffdd => Some(169),
0x3fffde => Some(170),
0x3fffdf => Some(173),
0x3fffe0 => Some(178),
0x3fffe1 => Some(181),
0x3fffe2 => Some(185),
0x3fffe3 => Some(186),
0x3fffe4 => Some(187),
0x3fffe5 => Some(189),
0x3fffe6 => Some(190),
0x3fffe7 => Some(196),
0x3fffe8 => Some(198),
0x3fffe9 => Some(228),
0x3fffea => Some(232),
0x3fffeb => Some(233),
_ => None,
}
}
named!(http2_decode_huffman_len22<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(22u32), http2_huffman_table_len22))
);
fn http2_huffman_table_len23(n: u32) -> Option<u8> {
match n {
0x7fffd8 => Some(1),
0x7fffd9 => Some(135),
0x7fffda => Some(137),
0x7fffdb => Some(138),
0x7fffdc => Some(139),
0x7fffdd => Some(140),
0x7fffde => Some(141),
0x7fffdf => Some(143),
0x7fffe0 => Some(147),
0x7fffe1 => Some(149),
0x7fffe2 => Some(150),
0x7fffe3 => Some(151),
0x7fffe4 => Some(152),
0x7fffe5 => Some(155),
0x7fffe6 => Some(157),
0x7fffe7 => Some(158),
0x7fffe8 => Some(165),
0x7fffe9 => Some(166),
0x7fffea => Some(168),
0x7fffeb => Some(174),
0x7fffec => Some(175),
0x7fffed => Some(180),
0x7fffee => Some(182),
0x7fffef => Some(183),
0x7ffff0 => Some(188),
0x7ffff1 => Some(191),
0x7ffff2 => Some(197),
0x7ffff3 => Some(231),
0x7ffff4 => Some(239),
_ => None,
}
}
named!(http2_decode_huffman_len23<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(23u32), http2_huffman_table_len23))
);
fn http2_huffman_table_len24(n: u32) -> Option<u8> {
match n {
0xffffea => Some(9),
0xffffeb => Some(142),
0xffffec => Some(144),
0xffffed => Some(145),
0xffffee => Some(148),
0xffffef => Some(159),
0xfffff0 => Some(171),
0xfffff1 => Some(206),
0xfffff2 => Some(215),
0xfffff3 => Some(225),
0xfffff4 => Some(236),
0xfffff5 => Some(237),
_ => None,
}
}
named!(http2_decode_huffman_len24<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(24u32), http2_huffman_table_len24))
);
fn http2_huffman_table_len25(n: u32) -> Option<u8> {
match n {
0x1ffffec => Some(199),
0x1ffffed => Some(207),
0x1ffffee => Some(234),
0x1ffffef => Some(235),
_ => None,
}
}
named!(http2_decode_huffman_len25<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(25u32), http2_huffman_table_len25))
);
fn http2_huffman_table_len26(n: u32) -> Option<u8> {
match n {
0x3ffffe0 => Some(192),
0x3ffffe1 => Some(193),
0x3ffffe2 => Some(200),
0x3ffffe3 => Some(201),
0x3ffffe4 => Some(202),
0x3ffffe5 => Some(205),
0x3ffffe6 => Some(210),
0x3ffffe7 => Some(213),
0x3ffffe8 => Some(218),
0x3ffffe9 => Some(219),
0x3ffffea => Some(238),
0x3ffffeb => Some(240),
0x3ffffec => Some(242),
0x3ffffed => Some(243),
0x3ffffee => Some(255),
_ => None,
}
}
named!(http2_decode_huffman_len26<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(26u32), http2_huffman_table_len26))
);
fn http2_huffman_table_len27(n: u32) -> Option<u8> {
match n {
0x7ffffde => Some(203),
0x7ffffdf => Some(204),
0x7ffffe0 => Some(211),
0x7ffffe1 => Some(212),
0x7ffffe2 => Some(214),
0x7ffffe3 => Some(221),
0x7ffffe4 => Some(222),
0x7ffffe5 => Some(223),
0x7ffffe6 => Some(241),
0x7ffffe7 => Some(244),
0x7ffffe8 => Some(245),
0x7ffffe9 => Some(246),
0x7ffffea => Some(247),
0x7ffffeb => Some(248),
0x7ffffec => Some(250),
0x7ffffed => Some(251),
0x7ffffee => Some(252),
0x7ffffef => Some(253),
0x7fffff0 => Some(254),
_ => None,
}
}
named!(http2_decode_huffman_len27<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(27u32), http2_huffman_table_len27))
);
fn http2_huffman_table_len28(n: u32) -> Option<u8> {
match n {
0xfffffe2 => Some(2),
0xfffffe3 => Some(3),
0xfffffe4 => Some(4),
0xfffffe5 => Some(5),
0xfffffe6 => Some(6),
0xfffffe7 => Some(7),
0xfffffe8 => Some(8),
0xfffffe9 => Some(11),
0xfffffea => Some(12),
0xfffffeb => Some(14),
0xfffffec => Some(15),
0xfffffed => Some(16),
0xfffffee => Some(17),
0xfffffef => Some(18),
0xffffff0 => Some(19),
0xffffff1 => Some(20),
0xffffff2 => Some(21),
0xffffff3 => Some(23),
0xffffff4 => Some(24),
0xffffff5 => Some(25),
0xffffff6 => Some(26),
0xffffff7 => Some(27),
0xffffff8 => Some(28),
0xffffff9 => Some(29),
0xffffffa => Some(30),
0xffffffb => Some(31),
0xffffffc => Some(127),
0xffffffd => Some(220),
0xffffffe => Some(249),
_ => None,
}
}
named!(http2_decode_huffman_len28<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(28u32), http2_huffman_table_len28))
);
fn http2_huffman_table_len30(n: u32) -> Option<u8> {
match n {
0x3ffffffc => Some(10),
0x3ffffffd => Some(13),
0x3ffffffe => Some(22),
// 0x3fffffff => Some(256),
_ => None,
}
}
named!(http2_decode_huffman_len30<(&[u8], usize), u8>,
complete!( map_opt!(take_bits!(30u32), http2_huffman_table_len30))
);
//hack to end many0 even if some bits are remaining
fn http2_decode_huffman_end(input: (&[u8], usize)) -> IResult<(&[u8], usize), u8> {
return Err(Err::Error((input, ErrorKind::Eof)));
}
//we could profile and optimize performance here
named!(pub http2_decode_huffman<(&[u8], usize), u8>,
alt!(http2_decode_huffman_len5 | http2_decode_huffman_len6 | http2_decode_huffman_len7 |
http2_decode_huffman_len8 | http2_decode_huffman_len10 | http2_decode_huffman_len11 |
http2_decode_huffman_len12 | http2_decode_huffman_len13 | http2_decode_huffman_len14 |
http2_decode_huffman_len15 | http2_decode_huffman_len19 | http2_decode_huffman_len20 |
http2_decode_huffman_len21 | http2_decode_huffman_len22 | http2_decode_huffman_len23 |
http2_decode_huffman_len24 | http2_decode_huffman_len25 | http2_decode_huffman_len26 |
http2_decode_huffman_len27 | http2_decode_huffman_len28 | http2_decode_huffman_len30 |
http2_decode_huffman_end)
);

@ -0,0 +1,184 @@
/* Copyright (C) 2020 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::http2::{HTTP2Frame, HTTP2FrameTypeData, HTTP2Transaction};
use super::parser;
use crate::jsonbuilder::{JsonBuilder, JsonError};
use std;
fn log_http2_headers(
blocks: &Vec<parser::HTTP2FrameHeaderBlock>, js: &mut JsonBuilder,
) -> Result<(), JsonError> {
for j in 0..blocks.len() {
js.start_object()?;
match blocks[j].error {
parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSuccess => {
js.set_string_from_bytes("name", &blocks[j].name)?;
js.set_string_from_bytes("value", &blocks[j].value)?;
}
parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate => {
js.set_uint("table_size_update", blocks[j].sizeupdate)?;
}
_ => {
js.set_string("error", &blocks[j].error.to_string())?;
}
}
js.close()?;
}
return Ok(());
}
fn log_http2_frames(frames: &Vec<HTTP2Frame>, js: &mut JsonBuilder) -> Result<bool, JsonError> {
let mut has_settings = false;
for i in 0..frames.len() {
if let HTTP2FrameTypeData::SETTINGS(set) = &frames[i].data {
if !has_settings {
js.open_array("settings")?;
has_settings = true;
}
for j in 0..set.len() {
js.start_object()?;
js.set_string("settings_id", &set[j].id.to_string())?;
js.set_uint("settings_value", set[j].value as u64)?;
js.close()?;
}
}
}
if has_settings {
js.close()?;
}
let mut has_headers = false;
for i in 0..frames.len() {
match &frames[i].data {
HTTP2FrameTypeData::HEADERS(hd) => {
if !has_headers {
js.open_array("headers")?;
has_headers = true;
}
log_http2_headers(&hd.blocks, js)?;
}
HTTP2FrameTypeData::PUSHPROMISE(hd) => {
if !has_headers {
js.open_array("headers")?;
has_headers = true;
}
log_http2_headers(&hd.blocks, js)?;
}
HTTP2FrameTypeData::CONTINUATION(hd) => {
if !has_headers {
js.open_array("headers")?;
has_headers = true;
}
log_http2_headers(&hd.blocks, js)?;
}
_ => {}
}
}
if has_headers {
js.close()?;
}
let mut has_error_code = false;
let mut has_priority = false;
let mut has_multiple = false;
for i in 0..frames.len() {
match &frames[i].data {
HTTP2FrameTypeData::GOAWAY(goaway) => {
if !has_error_code {
let errcode: Option<parser::HTTP2ErrorCode> =
num::FromPrimitive::from_u32(goaway.errorcode);
match errcode {
Some(errstr) => {
js.set_string("error_code", &errstr.to_string())?;
}
None => {
//use uint32
js.set_string("error_code", &goaway.errorcode.to_string())?;
}
}
has_error_code = true;
} else if !has_multiple {
js.set_string("has_multiple", "error_code")?;
has_multiple = true;
}
}
HTTP2FrameTypeData::RSTSTREAM(rst) => {
if !has_error_code {
let errcode: Option<parser::HTTP2ErrorCode> =
num::FromPrimitive::from_u32(rst.errorcode);
match errcode {
Some(errstr) => {
js.set_string("error_code", &errstr.to_string())?;
}
None => {
//use uint32
js.set_string("error_code", &rst.errorcode.to_string())?;
}
}
has_error_code = true;
} else if !has_multiple {
js.set_string("has_multiple", "error_code")?;
has_multiple = true;
}
}
HTTP2FrameTypeData::PRIORITY(priority) => {
if !has_priority {
js.set_uint("priority", priority.weight as u64)?;
has_priority = true;
} else if !has_multiple {
js.set_string("has_multiple", "priority")?;
has_multiple = true;
}
}
HTTP2FrameTypeData::HEADERS(hd) => {
if let Some(ref priority) = hd.priority {
if !has_priority {
js.set_uint("priority", priority.weight as u64)?;
has_priority = true;
} else if !has_multiple {
js.set_string("has_multiple", "priority")?;
has_multiple = true;
}
}
}
_ => {}
}
}
return Ok(has_settings || has_headers || has_error_code || has_priority);
}
fn log_http2(tx: &HTTP2Transaction, js: &mut JsonBuilder) -> Result<bool, JsonError> {
js.set_uint("stream_id", tx.stream_id as u64)?;
js.open_object("request")?;
let has_request = log_http2_frames(&tx.frames_ts, js)?;
js.close()?;
js.open_object("response")?;
let has_response = log_http2_frames(&tx.frames_tc, js)?;
js.close()?;
return Ok(has_request || has_response);
}
#[no_mangle]
pub extern "C" fn rs_http2_log_json(tx: *mut std::os::raw::c_void, js: &mut JsonBuilder) -> bool {
let tx = cast_pointer!(tx, HTTP2Transaction);
if let Ok(x) = log_http2(tx, js) {
return x;
}
return false;
}

@ -0,0 +1,23 @@
/* Copyright (C) 2020 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.
*/
pub mod detect;
pub mod files;
pub mod http2;
mod huffman;
pub mod logger;
mod parser;

File diff suppressed because it is too large Load Diff

@ -81,3 +81,4 @@ pub mod rdp;
pub mod x509;
pub mod asn1;
pub mod ssh;
pub mod http2;

@ -35,6 +35,7 @@ app-layer-htp-file.c app-layer-htp-file.h \
app-layer-htp-libhtp.c app-layer-htp-libhtp.h \
app-layer-htp-mem.c app-layer-htp-mem.h \
app-layer-htp-xff.c app-layer-htp-xff.h \
app-layer-http2.c app-layer-http2.h \
app-layer-modbus.c app-layer-modbus.h \
app-layer-parser.c app-layer-parser.h \
app-layer-protos.c app-layer-protos.h \
@ -208,6 +209,7 @@ detect-http-stat-code.c detect-http-stat-code.h \
detect-http-stat-msg.c detect-http-stat-msg.h \
detect-http-ua.c detect-http-ua.h \
detect-http-uri.c detect-http-uri.h \
detect-http2.c detect-http2.h \
detect-icmp-id.c detect-icmp-id.h \
detect-icmp-seq.c detect-icmp-seq.h \
detect-icmpv6hdr.c detect-icmpv6hdr.h \
@ -372,6 +374,7 @@ output-json-flow.c output-json-flow.h \
output-json-ftp.c output-json-ftp.h \
output-json-netflow.c output-json-netflow.h \
output-json-http.c output-json-http.h \
output-json-http2.c output-json-http2.h \
output-json-sip.c output-json-sip.h \
output-json-smtp.c output-json-smtp.h \
output-json-ssh.c output-json-ssh.h \

@ -687,6 +687,26 @@ static void PacketToDataProtoHTTP(const Packet *p, const PacketAlert *pa, idmef_
}
/**
* \brief Handle ALPROTO_HTTP2 JSON information
* \param p Packet where to extract data
* \param pa Packet alert information
* \param alert IDMEF alert
* \return void
*/
static void PacketToDataProtoHTTP2(const Packet *p, const PacketAlert *pa, idmef_alert_t *alert)
{
void *http2_state = FlowGetAppState(f);
if (http2_state) {
void *tx_ptr = rs_http2_state_get_tx(http2_state, pa->tx_id);
json_t *js = rs_http2_log_json(tx_ptr);
if (unlikely(js == NULL))
return;
JsonToAdditionalData(NULL, js, alert);
json_decref(js);
}
}
/**
* \brief Handle ALPROTO_TLS JSON information
* \param p Packet where to extract data
@ -811,6 +831,9 @@ static int PacketToData(const Packet *p, const PacketAlert *pa, idmef_alert_t *a
case ALPROTO_HTTP:
PacketToDataProtoHTTP(p, pa, alert);
break;
case ALPROTO_HTTP2:
PacketToDataProtoHTTP(p, pa, alert);
break;
case ALPROTO_TLS:
PacketToDataProtoTLS(p, pa, alert);
break;

@ -0,0 +1,71 @@
/* Copyright (C) 2020 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
*
* \author Philippe Antoine <p.antoine@catenacyber.fr>
*
* Parser for HTTP2, RFC 7540
*/
#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-http2.h"
#include "rust.h"
static int HTTP2RegisterPatternsForProtocolDetection(void)
{
/* Using the 24 bytes pattern makes AppLayerTest09 fail/leak
* The complete pattern is "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
*/
if (AppLayerProtoDetectPMRegisterPatternCI(IPPROTO_TCP, ALPROTO_HTTP2,
"PRI * HTTP/2.0\r\n",
16, 0, STREAM_TOSERVER) < 0)
{
return -1;
}
return 0;
}
static StreamingBufferConfig sbcfg = STREAMING_BUFFER_CONFIG_INITIALIZER;
static SuricataFileContext sfc = { &sbcfg };
void RegisterHTTP2Parsers(void)
{
const char *proto_name = "http2";
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_HTTP2, proto_name);
if (HTTP2RegisterPatternsForProtocolDetection() < 0)
return;
rs_http2_init(&sfc);
rs_http2_register_parser();
}
#ifdef UNITTESTS
//TODOask HTTP2ParserRegisterTests();
#endif
}

@ -0,0 +1,29 @@
/* Copyright (C) 2020 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
*
* \author Philippe Antoine <p.antoine@catenacyber.fr>
*/
#ifndef __APP_LAYER_HTTP2_H__
#define __APP_LAYER_HTTP2_H__
void RegisterHTTP2Parsers(void);
#endif /* __APP_LAYER_HTTP2_H__ */

@ -70,6 +70,7 @@
#include "app-layer-template.h"
#include "app-layer-template-rust.h"
#include "app-layer-rdp.h"
#include "app-layer-http2.h"
#include "conf.h"
#include "util-spm.h"
@ -1580,6 +1581,7 @@ void AppLayerParserRegisterProtocolParsers(void)
RegisterMQTTParsers();
RegisterTemplateParsers();
RegisterRdpParsers();
RegisterHTTP2Parsers();
/** IMAP */
AppLayerProtoDetectRegisterProtocol(ALPROTO_IMAP, "imap");

@ -117,6 +117,9 @@ const char *AppProtoToString(AppProto alproto)
case ALPROTO_RDP:
proto_name = "rdp";
break;
case ALPROTO_HTTP2:
proto_name = "http2";
break;
case ALPROTO_FAILED:
proto_name = "failed";
break;
@ -161,6 +164,7 @@ AppProto StringToAppProto(const char *proto_name)
if (strcmp(proto_name,"template")==0) return ALPROTO_TEMPLATE;
if (strcmp(proto_name,"template-rust")==0) return ALPROTO_TEMPLATE_RUST;
if (strcmp(proto_name,"rdp")==0) return ALPROTO_RDP;
if (strcmp(proto_name,"http2")==0) return ALPROTO_HTTP2;
if (strcmp(proto_name,"failed")==0) return ALPROTO_FAILED;
return ALPROTO_UNKNOWN;

@ -56,6 +56,7 @@ enum AppProtoEnum {
ALPROTO_TEMPLATE,
ALPROTO_TEMPLATE_RUST,
ALPROTO_RDP,
ALPROTO_HTTP2,
/* used by the probing parser when alproto detection fails
* permanently for that particular stream */

@ -161,6 +161,7 @@
#include "detect-http-stat-msg.h"
#include "detect-http-request-line.h"
#include "detect-http-response-line.h"
#include "detect-http2.h"
#include "detect-byte-extract.h"
#include "detect-file-data.h"
#include "detect-pkt-data.h"
@ -473,6 +474,7 @@ void SigTableSetup(void)
DetectHttpStatMsgRegister();
DetectHttpStatCodeRegister();
DetectHttp2Register();
DetectDnsQueryRegister();
DetectDnsOpcodeRegister();

@ -172,6 +172,15 @@ enum DetectKeywordId {
DETECT_PKT_DATA,
DETECT_AL_APP_LAYER_EVENT,
DETECT_HTTP2_FRAMETYPE,
DETECT_HTTP2_ERRORCODE,
DETECT_HTTP2_PRIORITY,
DETECT_HTTP2_WINDOW,
DETECT_HTTP2_SIZEUPDATE,
DETECT_HTTP2_SETTINGS,
DETECT_HTTP2_HEADERNAME,
DETECT_HTTP2_HEADER,
DETECT_DCE_IFACE,
DETECT_DCE_OPNUM,
DETECT_DCE_STUB_DATA,

@ -103,6 +103,12 @@ void DetectFiledataRegister(void)
DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOCLIENT, 2,
PrefilterMpmFiledataRegister, NULL,
ALPROTO_SMB, 0);
DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOSERVER, 2,
PrefilterMpmFiledataRegister, NULL,
ALPROTO_HTTP2, HTTP2StateDataClient);
DetectAppLayerMpmRegister2("file_data", SIG_FLAG_TOCLIENT, 2,
PrefilterMpmFiledataRegister, NULL,
ALPROTO_HTTP2, HTTP2StateDataServer);
DetectAppLayerInspectEngineRegister2("file_data",
ALPROTO_HTTP, SIG_FLAG_TOCLIENT, HTP_RESPONSE_BODY,
@ -118,6 +124,12 @@ void DetectFiledataRegister(void)
DetectAppLayerInspectEngineRegister2("file_data",
ALPROTO_SMB, SIG_FLAG_TOCLIENT, 0,
DetectEngineInspectFiledata, NULL);
DetectAppLayerInspectEngineRegister2("file_data",
ALPROTO_HTTP2, SIG_FLAG_TOSERVER, HTTP2StateDataClient,
DetectEngineInspectFiledata, NULL);
DetectAppLayerInspectEngineRegister2("file_data",
ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, HTTP2StateDataServer,
DetectEngineInspectFiledata, NULL);
DetectBufferTypeSetDescriptionByName("file_data",
"http response body, smb files or smtp attachments data");
@ -167,7 +179,8 @@ static int DetectFiledataSetup (DetectEngineCtx *de_ctx, Signature *s, const cha
if (!DetectProtoContainsProto(&s->proto, IPPROTO_TCP) ||
(s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP &&
s->alproto != ALPROTO_SMTP && s->alproto != ALPROTO_SMB)) {
s->alproto != ALPROTO_SMTP && s->alproto != ALPROTO_SMB &&
s->alproto != ALPROTO_HTTP2)) {
SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
return -1;
}

@ -127,9 +127,9 @@ void DetectFilemagicRegister(void)
g_file_match_list_id = DetectBufferTypeRegister("files");
AppProto protos_ts[] = {
ALPROTO_HTTP, ALPROTO_SMTP, ALPROTO_FTP, ALPROTO_SMB, ALPROTO_NFS, 0 };
ALPROTO_HTTP, ALPROTO_SMTP, ALPROTO_FTP, ALPROTO_SMB, ALPROTO_NFS, ALPROTO_HTTP2, 0 };
AppProto protos_tc[] = {
ALPROTO_HTTP, ALPROTO_FTP, ALPROTO_SMB, ALPROTO_NFS, 0 };
ALPROTO_HTTP, ALPROTO_FTP, ALPROTO_SMB, ALPROTO_NFS, ALPROTO_HTTP2, 0 };
for (int i = 0; protos_ts[i] != 0; i++) {
DetectAppLayerInspectEngineRegister2("file.magic", protos_ts[i],

@ -0,0 +1,984 @@
/* Copyright (C) 2020 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
*
* \author Philippe Antoine <p.antoine@catenacyber.fr>
*
*/
#include "suricata-common.h"
#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-uint.h"
#include "detect-engine-mpm.h"
#include "detect-engine-prefilter.h"
#include "detect-engine-content-inspection.h"
#include "detect-http2.h"
#include "util-byte.h"
#include "rust.h"
#ifdef UNITTESTS
void DetectHTTP2frameTypeRegisterTests (void);
void DetectHTTP2errorCodeRegisterTests (void);
void DetectHTTP2priorityRegisterTests (void);
void DetectHTTP2windowRegisterTests (void);
void DetectHTTP2settingsRegisterTests (void);
void DetectHTTP2sizeUpdateRegisterTests (void);
#endif
/* prototypes */
static int DetectHTTP2frametypeMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectHTTP2frametypeSetup (DetectEngineCtx *, Signature *, const char *);
void DetectHTTP2frametypeFree (DetectEngineCtx *, void *);
static int DetectHTTP2errorcodeMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectHTTP2errorcodeSetup (DetectEngineCtx *, Signature *, const char *);
void DetectHTTP2errorcodeFree (DetectEngineCtx *, void *);
static int DetectHTTP2priorityMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectHTTP2prioritySetup (DetectEngineCtx *, Signature *, const char *);
void DetectHTTP2priorityFree (DetectEngineCtx *, void *);
static int DetectHTTP2windowMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectHTTP2windowSetup (DetectEngineCtx *, Signature *, const char *);
void DetectHTTP2windowFree (DetectEngineCtx *, void *);
static int DetectHTTP2sizeUpdateMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectHTTP2sizeUpdateSetup (DetectEngineCtx *, Signature *, const char *);
void DetectHTTP2sizeUpdateFree (DetectEngineCtx *, void *);
static int DetectHTTP2settingsMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx);
static int DetectHTTP2settingsSetup (DetectEngineCtx *, Signature *, const char *);
void DetectHTTP2settingsFree (DetectEngineCtx *, void *);
static int DetectHTTP2headerNameSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg);
static int PrefilterMpmHttp2HeaderNameRegister(DetectEngineCtx *de_ctx,
SigGroupHead *sgh, MpmCtx *mpm_ctx,
const DetectBufferMpmRegistery *mpm_reg, int list_id);
static int DetectEngineInspectHttp2HeaderName(
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);
static int DetectHTTP2headerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg);
static int PrefilterMpmHttp2HeaderRegister(DetectEngineCtx *de_ctx,
SigGroupHead *sgh, MpmCtx *mpm_ctx,
const DetectBufferMpmRegistery *mpm_reg, int list_id);
static int DetectEngineInspectHttp2Header(
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);
static bool DetectHttp2HeaderValidateCallback(const Signature *s, const char **sigerror);
#ifdef UNITTESTS
void DetectHTTP2RegisterTests (void);
#endif
static int g_http2_match_buffer_id = 0;
static int g_http2_header_name_buffer_id = 0;
static int g_http2_header_buffer_id = 0;
static int DetectEngineInspectHTTP2(ThreadVars *tv, DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd,
Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
{
return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
f, flags, alstate, txv, tx_id);
}
/**
* \brief Registration function for HTTP2 keywords
*/
void DetectHttp2Register(void)
{
sigmatch_table[DETECT_HTTP2_FRAMETYPE].name = "http2.frametype";
sigmatch_table[DETECT_HTTP2_FRAMETYPE].desc = "match on HTTP2 frame type field";
sigmatch_table[DETECT_HTTP2_FRAMETYPE].url = "/rules/http2-keywords.html#frametype";
sigmatch_table[DETECT_HTTP2_FRAMETYPE].Match = NULL;
sigmatch_table[DETECT_HTTP2_FRAMETYPE].AppLayerTxMatch = DetectHTTP2frametypeMatch;
sigmatch_table[DETECT_HTTP2_FRAMETYPE].Setup = DetectHTTP2frametypeSetup;
sigmatch_table[DETECT_HTTP2_FRAMETYPE].Free = DetectHTTP2frametypeFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_FRAMETYPE].RegisterTests = DetectHTTP2frameTypeRegisterTests;
#endif
sigmatch_table[DETECT_HTTP2_ERRORCODE].name = "http2.errorcode";
sigmatch_table[DETECT_HTTP2_ERRORCODE].desc = "match on HTTP2 error code field";
sigmatch_table[DETECT_HTTP2_ERRORCODE].url = "/rules/http2-keywords.html#errorcode";
sigmatch_table[DETECT_HTTP2_ERRORCODE].Match = NULL;
sigmatch_table[DETECT_HTTP2_ERRORCODE].AppLayerTxMatch = DetectHTTP2errorcodeMatch;
sigmatch_table[DETECT_HTTP2_ERRORCODE].Setup = DetectHTTP2errorcodeSetup;
sigmatch_table[DETECT_HTTP2_ERRORCODE].Free = DetectHTTP2errorcodeFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_ERRORCODE].RegisterTests = DetectHTTP2errorCodeRegisterTests;
#endif
sigmatch_table[DETECT_HTTP2_PRIORITY].name = "http2.priority";
sigmatch_table[DETECT_HTTP2_PRIORITY].desc = "match on HTTP2 priority weight field";
sigmatch_table[DETECT_HTTP2_PRIORITY].url = "/rules/http2-keywords.html#priority";
sigmatch_table[DETECT_HTTP2_PRIORITY].Match = NULL;
sigmatch_table[DETECT_HTTP2_PRIORITY].AppLayerTxMatch = DetectHTTP2priorityMatch;
sigmatch_table[DETECT_HTTP2_PRIORITY].Setup = DetectHTTP2prioritySetup;
sigmatch_table[DETECT_HTTP2_PRIORITY].Free = DetectHTTP2priorityFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_PRIORITY].RegisterTests = DetectHTTP2priorityRegisterTests;
#endif
sigmatch_table[DETECT_HTTP2_WINDOW].name = "http2.window";
sigmatch_table[DETECT_HTTP2_WINDOW].desc = "match on HTTP2 window update size increment field";
sigmatch_table[DETECT_HTTP2_WINDOW].url = "/rules/http2-keywords.html#window";
sigmatch_table[DETECT_HTTP2_WINDOW].Match = NULL;
sigmatch_table[DETECT_HTTP2_WINDOW].AppLayerTxMatch = DetectHTTP2windowMatch;
sigmatch_table[DETECT_HTTP2_WINDOW].Setup = DetectHTTP2windowSetup;
sigmatch_table[DETECT_HTTP2_WINDOW].Free = DetectHTTP2windowFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_WINDOW].RegisterTests = DetectHTTP2windowRegisterTests;
#endif
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].name = "http2.size_update";
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].desc = "match on HTTP2 dynamic headers table size update";
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].url = "/rules/http2-keywords.html#sizeupdate";
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].Match = NULL;
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].AppLayerTxMatch = DetectHTTP2sizeUpdateMatch;
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].Setup = DetectHTTP2sizeUpdateSetup;
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].Free = DetectHTTP2sizeUpdateFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_SIZEUPDATE].RegisterTests = DetectHTTP2sizeUpdateRegisterTests;
#endif
sigmatch_table[DETECT_HTTP2_SETTINGS].name = "http2.settings";
sigmatch_table[DETECT_HTTP2_SETTINGS].desc = "match on HTTP2 settings identifier and value fields";
sigmatch_table[DETECT_HTTP2_SETTINGS].url = "/rules/http2-keywords.html#settings";
sigmatch_table[DETECT_HTTP2_SETTINGS].Match = NULL;
sigmatch_table[DETECT_HTTP2_SETTINGS].AppLayerTxMatch = DetectHTTP2settingsMatch;
sigmatch_table[DETECT_HTTP2_SETTINGS].Setup = DetectHTTP2settingsSetup;
sigmatch_table[DETECT_HTTP2_SETTINGS].Free = DetectHTTP2settingsFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_HTTP2_SETTINGS].RegisterTests = DetectHTTP2settingsRegisterTests;
#endif
sigmatch_table[DETECT_HTTP2_HEADERNAME].name = "http2.header_name";
sigmatch_table[DETECT_HTTP2_HEADERNAME].desc = "sticky buffer to match on one HTTP2 header name";
sigmatch_table[DETECT_HTTP2_HEADERNAME].url = "/rules/http2-keywords.html#header_name";
sigmatch_table[DETECT_HTTP2_HEADERNAME].Setup = DetectHTTP2headerNameSetup;
sigmatch_table[DETECT_HTTP2_HEADERNAME].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
DetectAppLayerMpmRegister2("http2_header_name", SIG_FLAG_TOCLIENT, 2,
PrefilterMpmHttp2HeaderNameRegister, NULL,
ALPROTO_HTTP2, HTTP2StateOpen);
DetectAppLayerInspectEngineRegister2("http2_header_name",
ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, HTTP2StateOpen,
DetectEngineInspectHttp2HeaderName, NULL);
DetectAppLayerMpmRegister2("http2_header_name", SIG_FLAG_TOSERVER, 2,
PrefilterMpmHttp2HeaderNameRegister, NULL,
ALPROTO_HTTP2, HTTP2StateOpen);
DetectAppLayerInspectEngineRegister2("http2_header_name",
ALPROTO_HTTP2, SIG_FLAG_TOSERVER, HTTP2StateOpen,
DetectEngineInspectHttp2HeaderName, NULL);
DetectBufferTypeSetDescriptionByName("http2_header_name",
"HTTP2 header name");
g_http2_header_name_buffer_id = DetectBufferTypeGetByName("http2_header_name");
sigmatch_table[DETECT_HTTP2_HEADER].name = "http2.header";
sigmatch_table[DETECT_HTTP2_HEADER].desc = "sticky buffer to match on one HTTP2 header name and value";
sigmatch_table[DETECT_HTTP2_HEADER].url = "/rules/http2-keywords.html#header";
sigmatch_table[DETECT_HTTP2_HEADER].Setup = DetectHTTP2headerSetup;
sigmatch_table[DETECT_HTTP2_HEADER].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
DetectAppLayerMpmRegister2("http2_header", SIG_FLAG_TOCLIENT, 2,
PrefilterMpmHttp2HeaderRegister, NULL,
ALPROTO_HTTP2, HTTP2StateOpen);
DetectAppLayerInspectEngineRegister2("http2_header",
ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, HTTP2StateOpen,
DetectEngineInspectHttp2Header, NULL);
DetectAppLayerMpmRegister2("http2_header", SIG_FLAG_TOSERVER, 2,
PrefilterMpmHttp2HeaderRegister, NULL,
ALPROTO_HTTP2, HTTP2StateOpen);
DetectAppLayerInspectEngineRegister2("http2_header",
ALPROTO_HTTP2, SIG_FLAG_TOSERVER, HTTP2StateOpen,
DetectEngineInspectHttp2Header, NULL);
DetectBufferTypeSetDescriptionByName("http2_header",
"HTTP2 header name and value");
DetectBufferTypeRegisterValidateCallback("http2_header", DetectHttp2HeaderValidateCallback);
g_http2_header_buffer_id = DetectBufferTypeGetByName("http2_header");
DetectAppLayerInspectEngineRegister("http2",
ALPROTO_HTTP2, SIG_FLAG_TOSERVER, 0,
DetectEngineInspectHTTP2);
DetectAppLayerInspectEngineRegister("http2",
ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, 0,
DetectEngineInspectHTTP2);
g_http2_match_buffer_id = DetectBufferTypeRegister("http2");
DetectUintRegister();
return;
}
/**
* \brief This function is used to match HTTP2 frame type rule option on a transaction with those passed via http2.frametype:
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectHTTP2frametypeMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
uint8_t *detect = (uint8_t *)ctx;
return rs_http2_tx_has_frametype(txv, flags, *detect);
}
static int DetectHTTP2FuncParseFrameType(const char *str, uint8_t *ft)
{
// first parse numeric value
if (ByteExtractStringUint8(ft, 10, strlen(str), str) >= 0) {
return 1;
}
// it it failed so far, parse string value from enumeration
int r = rs_http2_parse_frametype(str);
if (r >= 0) {
*ft = r;
return 1;
}
return 0;
}
/**
* \brief this function is used to attach the parsed http2.frametype data into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided http2.frametype options
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectHTTP2frametypeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
uint8_t frame_type;
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
if (!DetectHTTP2FuncParseFrameType(str, &frame_type)) {
SCLogError(SC_ERR_INVALID_SIGNATURE,
"Invalid argument \"%s\" supplied to http2.frametype keyword.", str);
return -1;
}
uint8_t *http2ft = SCCalloc(1, sizeof(uint8_t));
if (http2ft == NULL)
return -1;
*http2ft = frame_type;
SigMatch *sm = SigMatchAlloc();
if (sm == NULL) {
DetectHTTP2frametypeFree(NULL, http2ft);
return -1;
}
sm->type = DETECT_HTTP2_FRAMETYPE;
sm->ctx = (SigMatchCtx *)http2ft;
SigMatchAppendSMToList(s, sm, g_http2_match_buffer_id);
return 0;
}
/**
* \brief this function will free memory associated with uint8_t
*
* \param ptr pointer to uint8_t
*/
void DetectHTTP2frametypeFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCFree(ptr);
}
/**
* \brief This function is used to match HTTP2 error code rule option on a transaction with those passed via http2.errorcode:
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectHTTP2errorcodeMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
uint32_t *detect = (uint32_t *)ctx;
return rs_http2_tx_has_errorcode(txv, flags, *detect);
//TODOask handle negation rules
}
static int DetectHTTP2FuncParseErrorCode(const char *str, uint32_t *ec)
{
// first parse numeric value
if (ByteExtractStringUint32(ec, 10, strlen(str), str) >= 0) {
return 1;
}
// it it failed so far, parse string value from enumeration
int r = rs_http2_parse_errorcode(str);
if (r >= 0) {
*ec = r;
return 1;
}
return 0;
}
/**
* \brief this function is used to attach the parsed http2.errorcode data into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided http2.errorcode options
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectHTTP2errorcodeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
uint32_t error_code;
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
if (!DetectHTTP2FuncParseErrorCode(str, &error_code)) {
SCLogError(SC_ERR_INVALID_SIGNATURE,
"Invalid argument \"%s\" supplied to http2.errorcode keyword.", str);
return -1;
}
uint32_t *http2ec = SCCalloc(1, sizeof(uint32_t));
if (http2ec == NULL)
return -1;
*http2ec = error_code;
SigMatch *sm = SigMatchAlloc();
if (sm == NULL) {
DetectHTTP2errorcodeFree(NULL, http2ec);
return -1;
}
sm->type = DETECT_HTTP2_ERRORCODE;
sm->ctx = (SigMatchCtx *)http2ec;
SigMatchAppendSMToList(s, sm, g_http2_match_buffer_id);
return 0;
}
/**
* \brief this function will free memory associated with uint32_t
*
* \param ptr pointer to uint32_t
*/
void DetectHTTP2errorcodeFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCFree(ptr);
}
/**
* \brief This function is used to match HTTP2 error code rule option on a transaction with those passed via http2.priority:
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectHTTP2priorityMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
uint32_t nb = 0;
int value = rs_http2_tx_get_next_priority(txv, flags, nb);
const DetectU8Data *du8 = (const DetectU8Data *)ctx;
while (value >= 0) {
if (DetectU8Match(value, du8)) {
return 1;
}
nb++;
value = rs_http2_tx_get_next_priority(txv, flags, nb);
}
return 0;
}
/**
* \brief this function is used to attach the parsed http2.priority data into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided http2.priority options
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectHTTP2prioritySetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
DetectU8Data *prio = DetectU8Parse(str);
if (prio == NULL)
return -1;
SigMatch *sm = SigMatchAlloc();
if (sm == NULL) {
SCFree(prio);
return -1;
}
sm->type = DETECT_HTTP2_PRIORITY;
sm->ctx = (SigMatchCtx *)prio;
SigMatchAppendSMToList(s, sm, g_http2_match_buffer_id);
return 0;
}
/**
* \brief this function will free memory associated with uint32_t
*
* \param ptr pointer to DetectU8Data
*/
void DetectHTTP2priorityFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCFree(ptr);
}
/**
* \brief This function is used to match HTTP2 window rule option on a transaction with those passed via http2.window:
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectHTTP2windowMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
uint32_t nb = 0;
int value = rs_http2_tx_get_next_window(txv, flags, nb);
const DetectU32Data *du32 = (const DetectU32Data *)ctx;
while (value >= 0) {
if (DetectU32Match(value, du32)) {
return 1;
}
nb++;
value = rs_http2_tx_get_next_window(txv, flags, nb);
}
return 0;
}
/**
* \brief this function is used to attach the parsed http2.window data into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided http2.window options
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectHTTP2windowSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
DetectU32Data *wu = DetectU32Parse(str);
if (wu == NULL)
return -1;
SigMatch *sm = SigMatchAlloc();
if (sm == NULL) {
SCFree(wu);
return -1;
}
sm->type = DETECT_HTTP2_WINDOW;
sm->ctx = (SigMatchCtx *)wu;
SigMatchAppendSMToList(s, sm, g_http2_match_buffer_id);
return 0;
}
/**
* \brief this function will free memory associated with uint32_t
*
* \param ptr pointer to DetectU8Data
*/
void DetectHTTP2windowFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCFree(ptr);
}
/**
* \brief This function is used to match HTTP2 size update rule option on a transaction with those passed via http2.size_update:
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectHTTP2sizeUpdateMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
return rs_http2_detect_sizeupdatectx_match(ctx, txv, flags);
}
/**
* \brief this function is used to attach the parsed http2.size_update data into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided http2.size_update options
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectHTTP2sizeUpdateSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
void *su = rs_detect_u64_parse(str);
if (su == NULL)
return -1;
SigMatch *sm = SigMatchAlloc();
if (sm == NULL) {
DetectHTTP2settingsFree(NULL, su);
return -1;
}
sm->type = DETECT_HTTP2_SIZEUPDATE;
sm->ctx = (SigMatchCtx *)su;
SigMatchAppendSMToList(s, sm, g_http2_match_buffer_id);
return 0;
}
/**
* \brief this function will free memory associated with uint32_t
*
* \param ptr pointer to DetectU8Data
*/
void DetectHTTP2sizeUpdateFree(DetectEngineCtx *de_ctx, void *ptr)
{
rs_detect_u64_free(ptr);
}
/**
* \brief This function is used to match HTTP2 error code rule option on a transaction with those passed via http2.settings:
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectHTTP2settingsMatch(DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
const SigMatchCtx *ctx)
{
return rs_http2_detect_settingsctx_match(ctx, txv, flags);
}
/**
* \brief this function is used to attach the parsed http2.settings data into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided http2.settings options
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectHTTP2settingsSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
void *http2set = rs_http2_detect_settingsctx_parse(str);
if (http2set == NULL)
return -1;
SigMatch *sm = SigMatchAlloc();
if (sm == NULL) {
DetectHTTP2settingsFree(NULL, http2set);
return -1;
}
sm->type = DETECT_HTTP2_SETTINGS;
sm->ctx = (SigMatchCtx *)http2set;
SigMatchAppendSMToList(s, sm, g_http2_match_buffer_id);
return 0;
}
/**
* \brief this function will free memory associated with rust signature context
*
* \param ptr pointer to rust signature context
*/
void DetectHTTP2settingsFree(DetectEngineCtx *de_ctx, void *ptr)
{
rs_http2_detect_settingsctx_free(ptr);
}
static int DetectHTTP2headerNameSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
{
if (DetectBufferSetActiveList(s, g_http2_header_name_buffer_id) < 0)
return -1;
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
return 0;
}
static void PrefilterMpmHttp2HNameFree(void *ptr)
{
SCFree(ptr);
}
static InspectionBuffer *GetHttp2HNameData(DetectEngineThreadCtx *det_ctx,
const uint8_t flags, const DetectEngineTransforms *transforms,
Flow *_f, const struct MpmListIdDataArgs *cbdata,
int list_id, bool first)
{
SCEnter();
InspectionBufferMultipleForList *fb = InspectionBufferGetMulti(det_ctx, list_id);
InspectionBuffer *buffer = InspectionBufferMultipleForListGet(fb, cbdata->local_id);
if (buffer == NULL)
return NULL;
if (!first && buffer->inspect != NULL)
return buffer;
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (rs_http2_tx_get_header_name(cbdata->txv, flags, (uint32_t)cbdata->local_id, &b, &b_len) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;
InspectionBufferSetup(buffer, b, b_len);
InspectionBufferApplyTransforms(buffer, transforms);
SCReturnPtr(buffer, "InspectionBuffer");
}
static void PrefilterTxHttp2HName(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;
int local_id = 0;
while(1) {
// loop until we get a NULL
struct MpmListIdDataArgs cbdata = { local_id, txv };
InspectionBuffer *buffer = GetHttp2HNameData(det_ctx, flags, 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 int PrefilterMpmHttp2HeaderNameRegister(DetectEngineCtx *de_ctx,
SigGroupHead *sgh, MpmCtx *mpm_ctx,
const DetectBufferMpmRegistery *mpm_reg, int list_id)
{
//TODOask use PrefilterMpmListId elsewhere
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, PrefilterTxHttp2HName,
mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
pectx, PrefilterMpmHttp2HNameFree, mpm_reg->name);
}
static int DetectEngineInspectHttp2HeaderName(
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)
{
int local_id = 0;
const DetectEngineTransforms *transforms = NULL;
if (!engine->mpm) {
transforms = engine->v2.transforms;
}
while (1) {
//TODOask use MpmListIdDataArgs elsewhere
struct MpmListIdDataArgs cbdata = { local_id, txv, };
InspectionBuffer *buffer = GetHttp2HNameData(det_ctx, flags,
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;
}
static int DetectHTTP2headerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
{
if (DetectBufferSetActiveList(s, g_http2_header_buffer_id) < 0)
return -1;
if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
return -1;
return 0;
}
static void PrefilterMpmHttp2HeaderFree(void *ptr)
{
SCFree(ptr);
}
static InspectionBuffer *GetHttp2HeaderData(DetectEngineThreadCtx *det_ctx,
const uint8_t flags, const DetectEngineTransforms *transforms,
Flow *_f, const struct MpmListIdDataArgs *cbdata,
int list_id, bool first)
{
SCEnter();
InspectionBufferMultipleForList *fb = InspectionBufferGetMulti(det_ctx, list_id);
InspectionBuffer *buffer = InspectionBufferMultipleForListGet(fb, cbdata->local_id);
if (buffer == NULL)
return NULL;
if (!first && buffer->inspect != NULL)
return buffer;
uint32_t b_len = 0;
const uint8_t *b = NULL;
if (rs_http2_tx_get_header(cbdata->txv, flags, (uint32_t)cbdata->local_id, &b, &b_len) != 1)
return NULL;
if (b == NULL || b_len == 0)
return NULL;
InspectionBufferSetup(buffer, b, b_len);
InspectionBufferApplyTransforms(buffer, transforms);
SCReturnPtr(buffer, "InspectionBuffer");
}
static void PrefilterTxHttp2Header(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;
int local_id = 0;
while(1) {
// loop until we get a NULL
struct MpmListIdDataArgs cbdata = { local_id, txv };
InspectionBuffer *buffer = GetHttp2HeaderData(det_ctx, flags, 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 int PrefilterMpmHttp2HeaderRegister(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, PrefilterTxHttp2Header,
mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
pectx, PrefilterMpmHttp2HeaderFree, mpm_reg->name);
}
static int DetectEngineInspectHttp2Header(
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)
{
int local_id = 0;
const DetectEngineTransforms *transforms = NULL;
if (!engine->mpm) {
transforms = engine->v2.transforms;
}
while (1) {
struct MpmListIdDataArgs cbdata = { local_id, txv, };
InspectionBuffer *buffer = GetHttp2HeaderData(det_ctx, flags,
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;
}
static bool DetectHttp2HeaderValidateCallback(const Signature *s, const char **sigerror)
{
const SigMatch *sm = s->init_data->smlists[g_http2_header_buffer_id];
for ( ; sm != NULL; sm = sm->next) {
if (sm->type != DETECT_CONTENT)
continue;
const DetectContentData *cd = (DetectContentData *)sm->ctx;
bool escaped = false;
bool namevaluesep = false;
for (size_t i = 0; i < cd->content_len; ++i) {
if (escaped) {
if (cd->content[i] == ' ') {
if (namevaluesep) {
*sigerror = "Invalid http2.header string : "
"': ' is a special sequence for separation between name and value "
" and thus can only be present once";
SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror);
return false;
}
namevaluesep = true;
} else if (cd->content[i] != ':') {
*sigerror = "Invalid http2.header string : "
"':' is an escaping character for itself, "
"or space for the separation between name and value";
SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror);
return false;
}
escaped = false;
} else if(cd->content[i] == ':') {
escaped = true;
}
}
if (escaped) {
*sigerror = "Invalid http2.header string : "
"':' is an escaping character for itself, "
"or space for the separation between name and value";
SCLogWarning(SC_WARN_POOR_RULE, "rule %u: %s", s->id, *sigerror);
return false;
}
}
return true;
}
#ifdef UNITTESTS
#include "tests/detect-http2.c"
#endif

@ -0,0 +1,29 @@
/* Copyright (C) 2020 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
*
* \author Philippe Antoine <p.antoine@catenacyber.fr>
*/
#ifndef _DETECT_HTTP2_H
#define _DETECT_HTTP2_H
void DetectHttp2Register(void);
#endif /* _DETECT_HTTP2_H */

@ -162,6 +162,24 @@ static void AlertJsonSsh(const Flow *f, JsonBuilder *js)
return;
}
static void AlertJsonHttp2(const Flow *f, const uint64_t tx_id, JsonBuilder *js)
{
void *h2_state = FlowGetAppState(f);
if (h2_state) {
void *tx_ptr = rs_http2_state_get_tx(h2_state, tx_id);
JsonBuilderMark mark = { 0, 0, 0 };
jb_get_mark(js, &mark);
jb_open_object(js, "http2");
if (rs_http2_log_json(tx_ptr, js)) {
jb_close(js);
} else {
jb_restore_mark(js, &mark);
}
}
return;
}
static void AlertJsonDnp3(const Flow *f, const uint64_t tx_id, JsonBuilder *js)
{
DNP3State *dnp3_state = (DNP3State *)FlowGetAppState(f);
@ -504,6 +522,9 @@ static void AlertAddAppLayer(const Packet *p, JsonBuilder *jb,
case ALPROTO_DNP3:
AlertJsonDnp3(p->flow, tx_id, jb);
break;
case ALPROTO_HTTP2:
AlertJsonHttp2(p->flow, tx_id, jb);
break;
case ALPROTO_DNS:
AlertJsonDns(p->flow, tx_id, jb);
break;

@ -62,6 +62,7 @@
#include "output-json-email-common.h"
#include "output-json-nfs.h"
#include "output-json-smb.h"
#include "output-json-http2.h"
#include "app-layer-htp.h"
#include "app-layer-htp-xff.h"
@ -172,6 +173,15 @@ JsonBuilder *JsonBuildFileInfoRecord(const Packet *p, const File *ff,
jb_restore_mark(js, &mark);
}
break;
case ALPROTO_HTTP2:
jb_get_mark(js, &mark);
jb_open_object(js, "http2");
if (EveHTTP2AddMetadata(p->flow, ff->txid, js)) {
jb_close(js);
} else {
jb_restore_mark(js, &mark);
}
break;
}
jb_set_string(js, "app_proto", AppProtoToString(p->flow->alproto));

@ -0,0 +1,251 @@
/* Copyright (C) 2020 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
*
* \author Philippe Antoine <p.antoine@catenacyber.fr>
*
* Implements HTTP2 JSON logging portion of the engine.
*/
#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-print.h"
#include "util-unittest.h"
#include "util-debug.h"
#include "app-layer-parser.h"
#include "output.h"
#include "app-layer-http2.h"
#include "app-layer.h"
#include "util-privs.h"
#include "util-buffer.h"
#include "util-logopenfile.h"
#include "util-crypt.h"
#include "output-json.h"
#include "output-json-http2.h"
#include "rust.h"
#define MODULE_NAME "LogHttp2Log"
typedef struct OutputHttp2Ctx_ {
LogFileCtx *file_ctx;
OutputJsonCommonSettings cfg;
} OutputHttp2Ctx;
typedef struct JsonHttp2LogThread_ {
OutputHttp2Ctx *http2log_ctx;
MemBuffer *buffer;
} JsonHttp2LogThread;
bool EveHTTP2AddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *jb)
{
void *state = FlowGetAppState(f);
if (state) {
void *tx = AppLayerParserGetTx(f->proto, ALPROTO_HTTP2, state, tx_id);
if (tx) {
return rs_http2_log_json(tx, jb);
}
}
return false;
}
static int JsonHttp2Logger(ThreadVars *tv, void *thread_data, const Packet *p,
Flow *f, void *state, void *txptr, uint64_t tx_id)
{
JsonHttp2LogThread *aft = (JsonHttp2LogThread *)thread_data;
OutputHttp2Ctx *http2_ctx = aft->http2log_ctx;
if (unlikely(state == NULL)) {
return 0;
}
JsonBuilder *js = CreateEveHeaderWithTxId(p, LOG_DIR_FLOW, "http2", NULL, tx_id);
if (unlikely(js == NULL))
return 0;
EveAddCommonOptions(&http2_ctx->cfg, p, f, js);
/* reset */
MemBufferReset(aft->buffer);
jb_open_object(js, "http2");
if (!rs_http2_log_json(txptr, js)) {
goto end;
}
jb_close(js);
OutputJsonBuilderBuffer(js, http2_ctx->file_ctx, &aft->buffer);
end:
jb_free(js);
return 0;
}
static TmEcode JsonHttp2LogThreadInit(ThreadVars *t, const void *initdata, void **data)
{
JsonHttp2LogThread *aft = SCMalloc(sizeof(JsonHttp2LogThread));
if (unlikely(aft == NULL))
return TM_ECODE_FAILED;
memset(aft, 0, sizeof(JsonHttp2LogThread));
if(initdata == NULL)
{
SCLogDebug("Error getting context for EveLogHTTP2. \"initdata\" argument NULL");
SCFree(aft);
return TM_ECODE_FAILED;
}
/* Use the Ouptut Context (file pointer and mutex) */
aft->http2log_ctx = ((OutputCtx *)initdata)->data;
aft->buffer = MemBufferCreateNew(JSON_OUTPUT_BUFFER_SIZE);
if (aft->buffer == NULL) {
SCFree(aft);
return TM_ECODE_FAILED;
}
*data = (void *)aft;
return TM_ECODE_OK;
}
static TmEcode JsonHttp2LogThreadDeinit(ThreadVars *t, void *data)
{
JsonHttp2LogThread *aft = (JsonHttp2LogThread *)data;
if (aft == NULL) {
return TM_ECODE_OK;
}
MemBufferFree(aft->buffer);
/* clear memory */
memset(aft, 0, sizeof(JsonHttp2LogThread));
SCFree(aft);
return TM_ECODE_OK;
}
static void OutputHttp2LogDeinit(OutputCtx *output_ctx)
{
OutputHttp2Ctx *http2_ctx = output_ctx->data;
LogFileCtx *logfile_ctx = http2_ctx->file_ctx;
LogFileFreeCtx(logfile_ctx);
SCFree(http2_ctx);
SCFree(output_ctx);
}
#define DEFAULT_LOG_FILENAME "http2.json"
static OutputInitResult OutputHttp2LogInit(ConfNode *conf)
{
OutputInitResult result = { NULL, false };
LogFileCtx *file_ctx = LogFileNewCtx();
if(file_ctx == NULL) {
SCLogError(SC_ERR_HTTP2_LOG_GENERIC, "couldn't create new file_ctx");
return result;
}
if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
LogFileFreeCtx(file_ctx);
return result;
}
OutputHttp2Ctx *http2_ctx = SCMalloc(sizeof(OutputHttp2Ctx));
if (unlikely(http2_ctx == NULL)) {
LogFileFreeCtx(file_ctx);
return result;
}
OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
if (unlikely(output_ctx == NULL)) {
LogFileFreeCtx(file_ctx);
SCFree(http2_ctx);
return result;
}
http2_ctx->file_ctx = file_ctx;
output_ctx->data = http2_ctx;
output_ctx->DeInit = OutputHttp2LogDeinit;
AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2);
result.ctx = output_ctx;
result.ok = true;
return result;
}
static void OutputHttp2LogDeinitSub(OutputCtx *output_ctx)
{
OutputHttp2Ctx *http2_ctx = output_ctx->data;
SCFree(http2_ctx);
SCFree(output_ctx);
}
static OutputInitResult OutputHttp2LogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
{
OutputInitResult result = { NULL, false };
OutputJsonCtx *ojc = parent_ctx->data;
OutputHttp2Ctx *http2_ctx = SCMalloc(sizeof(OutputHttp2Ctx));
if (unlikely(http2_ctx == NULL))
return result;
OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
if (unlikely(output_ctx == NULL)) {
SCFree(http2_ctx);
return result;
}
http2_ctx->file_ctx = ojc->file_ctx;
http2_ctx->cfg = ojc->cfg;
output_ctx->data = http2_ctx;
output_ctx->DeInit = OutputHttp2LogDeinitSub;
AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2);
result.ctx = output_ctx;
result.ok = true;
return result;
}
void JsonHttp2LogRegister (void)
{
/* register as separate module */
OutputRegisterTxModuleWithProgress(LOGGER_JSON_HTTP2,
MODULE_NAME, "http2-json-log",
OutputHttp2LogInit, ALPROTO_HTTP2, JsonHttp2Logger,
HTTP2StateClosed, HTTP2StateClosed,
JsonHttp2LogThreadInit, JsonHttp2LogThreadDeinit, NULL);
/* also register as child of eve-log */
OutputRegisterTxSubModuleWithProgress(LOGGER_JSON_HTTP2,
"eve-log", MODULE_NAME, "eve-log.http2",
OutputHttp2LogInitSub, ALPROTO_HTTP2, JsonHttp2Logger,
HTTP2StateClosed, HTTP2StateClosed,
JsonHttp2LogThreadInit, JsonHttp2LogThreadDeinit, NULL);
}

@ -0,0 +1,30 @@
/* Copyright (C) 2020 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
*
* \author Philippe Antoine <p.antoine@catenacyber.fr>
*/
#ifndef __OUTPUT_JSON_HTTP2_H__
#define __OUTPUT_JSON_HTTP2_H__
void JsonHttp2LogRegister(void);
bool EveHTTP2AddMetadata(const Flow *f, uint64_t tx_id, JsonBuilder *jb);
#endif /* __OUTPUT_JSON_HTTP2_H__ */

@ -79,6 +79,7 @@
#include "output-json-template.h"
#include "output-json-template-rust.h"
#include "output-json-rdp.h"
#include "output-json-http2.h"
#include "output-lua.h"
#include "output-json-dnp3.h"
#include "output-json-metadata.h"
@ -1094,6 +1095,7 @@ void OutputRegisterLoggers(void)
/* http log */
LogHttpLogRegister();
JsonHttpLogRegister();
JsonHttp2LogRegister();
/* tls log */
LogTlsLogRegister();
JsonTlsLogRegister();

@ -468,6 +468,7 @@ typedef enum {
LOGGER_JSON_TEMPLATE,
LOGGER_JSON_RDP,
LOGGER_JSON_DCERPC,
LOGGER_JSON_HTTP2,
LOGGER_ALERT_DEBUG,
LOGGER_ALERT_FAST,

@ -0,0 +1,161 @@
/* Copyright (C) 2020 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "../suricata-common.h"
#include "../detect-engine.h"
#include "../detect-http2.h"
#include "../util-unittest.h"
/**
* \test signature with a valid http2.frametype value.
*/
static int DetectHTTP2frameTypeParseTest01 (void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
Signature *sig = DetectEngineAppendSig(de_ctx,
"alert http2 any any -> any any (http2.frametype:GOAWAY; sid:1; rev:1;)");
FAIL_IF_NULL(sig);
DetectEngineCtxFree(de_ctx);
PASS;
}
/**
* \brief this function registers unit tests for DetectHTTP2frameType
*/
void DetectHTTP2frameTypeRegisterTests(void)
{
UtRegisterTest("DetectHTTP2frameTypeParseTest01", DetectHTTP2frameTypeParseTest01);
}
/**
* \test signature with a valid http2.errorcode value.
*/
static int DetectHTTP2errorCodeParseTest01 (void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
Signature *sig = DetectEngineAppendSig(de_ctx,
"alert http2 any any -> any any (http2.errorcode:NO_ERROR; sid:1; rev:1;)");
FAIL_IF_NULL(sig);
DetectEngineCtxFree(de_ctx);
PASS;
}
void DetectHTTP2errorCodeRegisterTests(void)
{
UtRegisterTest("DetectHTTP2errorCodeParseTest01", DetectHTTP2errorCodeParseTest01);
}
/**
* \test signature with a valid http2.priority value.
*/
static int DetectHTTP2priorityParseTest01 (void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
Signature *sig = DetectEngineAppendSig(de_ctx,
"alert http2 any any -> any any (http2.priority:>100; sid:1; rev:1;)");
FAIL_IF_NULL(sig);
DetectEngineCtxFree(de_ctx);
PASS;
}
void DetectHTTP2priorityRegisterTests(void)
{
UtRegisterTest("DetectHTTP2priorityParseTest01", DetectHTTP2priorityParseTest01);
}
/**
* \test signature with a valid http2.window value.
*/
static int DetectHTTP2windowParseTest01 (void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
Signature *sig = DetectEngineAppendSig(de_ctx,
"alert http2 any any -> any any (http2.window:<42; sid:1; rev:1;)");
FAIL_IF_NULL(sig);
DetectEngineCtxFree(de_ctx);
PASS;
}
void DetectHTTP2windowRegisterTests(void)
{
UtRegisterTest("DetectHTTP2windowParseTest01", DetectHTTP2windowParseTest01);
}
/**
* \test signature with a valid http2.settings value.
*/
static int DetectHTTP2settingsParseTest01 (void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
Signature *sig = DetectEngineAppendSig(de_ctx,
"alert http2 any any -> any any (http2.settings:SETTINGS_MAX_HEADER_LIST_SIZE >1024; sid:1; rev:1;)");
FAIL_IF_NULL(sig);
DetectEngineCtxFree(de_ctx);
PASS;
}
void DetectHTTP2settingsRegisterTests(void)
{
UtRegisterTest("DetectHTTP2settingsParseTest01", DetectHTTP2settingsParseTest01);
}
/**
* \test signature with a valid http2.size_update value.
*/
static int DetectHTTP2sizeUpdateParseTest01 (void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
FAIL_IF_NULL(de_ctx);
Signature *sig = DetectEngineAppendSig(de_ctx,
"alert http2 any any -> any any (http2.size_update:>4096; sid:1; rev:1;)");
FAIL_IF_NULL(sig);
DetectEngineCtxFree(de_ctx);
PASS;
}
void DetectHTTP2sizeUpdateRegisterTests(void)
{
UtRegisterTest("DetectHTTP2sizeUpdateParseTest01", DetectHTTP2sizeUpdateParseTest01);
}

@ -108,6 +108,7 @@ const char * SCErrorToString(SCError err)
CASE_CODE (SC_ERR_DEBUG_LOG_GENERIC);
CASE_CODE (SC_ERR_UNIFIED_LOG_GENERIC);
CASE_CODE (SC_ERR_HTTP_LOG_GENERIC);
CASE_CODE (SC_ERR_HTTP2_LOG_GENERIC);
CASE_CODE (SC_ERR_FTP_LOG_GENERIC);
CASE_CODE (SC_ERR_UNIFIED_ALERT_GENERIC);
CASE_CODE (SC_ERR_UNIFIED2_ALERT_GENERIC);

@ -363,6 +363,7 @@ typedef enum {
SC_WARN_HASSH_DISABLED,
SC_WARN_FILESTORE_CONFIG,
SC_WARN_PATH_READ_ERROR,
SC_ERR_HTTP2_LOG_GENERIC,
SC_ERR_MAX
} SCError;

@ -1322,6 +1322,7 @@ const char * PacketProfileLoggertIdToString(LoggerId id)
CASE_CODE (LOGGER_JSON_TEMPLATE);
CASE_CODE (LOGGER_JSON_RDP);
CASE_CODE (LOGGER_JSON_DCERPC);
CASE_CODE (LOGGER_JSON_HTTP2);
CASE_CODE (LOGGER_TLS_STORE);
CASE_CODE (LOGGER_TLS);
CASE_CODE (LOGGER_FILE_STORE);

@ -281,6 +281,7 @@ outputs:
- ssh
- mqtt:
# passwords: yes # enable output of passwords
- http2
- stats:
totals: yes # stats for all threads merged together
threads: no # per thread stats
@ -756,6 +757,8 @@ app-layer:
ssh:
enabled: yes
#hassh: yes
http2:
enabled: yes
smtp:
enabled: yes
raw-extraction: no

Loading…
Cancel
Save