Skip to content

Commit

Permalink
http2: follow range requests
Browse files Browse the repository at this point in the history
Move the content-range parsing code to rust
  • Loading branch information
catenacyber committed Sep 7, 2021
1 parent 928b29c commit 2758109
Show file tree
Hide file tree
Showing 16 changed files with 436 additions and 111 deletions.
1 change: 1 addition & 0 deletions rules/http2-events.rules
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ alert http2 any any -> any any (msg:"SURICATA HTTP2 too long frame data"; flow:e
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;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid HTTP1 settings during upgrade"; flow:established; app-layer-event:http2.invalid_http1_settings; classtype:protocol-command-decode; sid:2290008; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 failed decompression"; flow:established; app-layer-event:http2.failed_decompression; classtype:protocol-command-decode; sid:2290009; rev:1;)
alert http2 any any -> any any (msg:"SURICATA HTTP2 invalid range header"; flow:established; app-layer-event:http2.invalid_range; classtype:protocol-command-decode; sid:2290010; rev:1;)
2 changes: 2 additions & 0 deletions rust/cbindgen.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ exclude = [
"CLuaState",
"DetectEngineState",
"Flow",
"StreamingBufferConfig",
"HttpRangeContainerBlock",
"FileContainer",
"JsonT",
"IKEState",
Expand Down
14 changes: 14 additions & 0 deletions rust/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ pub type AppLayerDecoderEventsFreeEventsFunc =

pub enum StreamingBufferConfig {}

// Opaque flow type (defined in C)
pub enum HttpRangeContainerBlock {}

pub type SCHttpRangeFreeBlock = extern "C" fn (
c: *mut HttpRangeContainerBlock);
pub type SCHTPFileCloseHandleRange = extern "C" fn (
fc: *mut FileContainer,
flags: u16,
c: *mut HttpRangeContainerBlock,
data: *const u8,
data_len: u32);
pub type SCFileOpenFileWithId = extern "C" fn (
file_container: &FileContainer,
sbcfg: &StreamingBufferConfig,
Expand Down Expand Up @@ -144,6 +155,9 @@ pub struct SuricataContext {
AppLayerDecoderEventsFreeEvents: AppLayerDecoderEventsFreeEventsFunc,
pub AppLayerParserTriggerRawStreamReassembly: AppLayerParserTriggerRawStreamReassemblyFunc,

pub HttpRangeFreeBlock: SCHttpRangeFreeBlock,
pub HTPFileCloseHandleRange: SCHTPFileCloseHandleRange,

pub FileOpenFile: SCFileOpenFileWithId,
pub FileCloseFile: SCFileCloseFileById,
pub FileAppendData: SCFileAppendDataById,
Expand Down
38 changes: 38 additions & 0 deletions rust/src/http2/detect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,44 @@ fn http2_frames_get_header_firstvalue<'a>(
return Err(());
}

// same as http2_frames_get_header_value but returns a new Vec
// instead of using the transation to store the result slice
pub fn http2_frames_get_header_value_vec(
tx: &HTTP2Transaction, direction: u8, name: &str,
) -> Result<Vec<u8>, ()> {
let mut found = 0;
let mut vec = Vec::new();
let frames = if direction == STREAM_TOSERVER {
&tx.frames_ts
} else {
&tx.frames_tc
};
for i in 0..frames.len() {
if let Some(blocks) = http2_header_blocks(&frames[i]) {
for block in blocks.iter() {
if block.name == name.as_bytes().to_vec() {
if found == 0 {
vec.extend_from_slice(&block.value);
found = 1;
} else if found == 1 {
vec.extend_from_slice(&[b',', b' ']);
vec.extend_from_slice(&block.value);
found = 2;
} else {
vec.extend_from_slice(&[b',', b' ']);
vec.extend_from_slice(&block.value);
}
}
}
}
}
if found == 0 {
return Err(());
} else {
return Ok(vec);
}
}

fn http2_frames_get_header_value<'a>(
tx: &'a mut HTTP2Transaction, direction: u8, name: &str,
) -> Result<&'a [u8], ()> {
Expand Down
65 changes: 61 additions & 4 deletions rust/src/http2/http2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
*/

use super::decompression;
use super::detect;
use super::parser;
use super::range;

use crate::applayer::{self, *};
use crate::core::{
self, AppProto, Flow, SuricataFileContext, ALPROTO_FAILED, ALPROTO_UNKNOWN, IPPROTO_TCP,
STREAM_TOCLIENT, STREAM_TOSERVER,
self, AppProto, Flow, HttpRangeContainerBlock, SuricataFileContext, ALPROTO_FAILED,
ALPROTO_UNKNOWN, IPPROTO_TCP, SC, STREAM_TOCLIENT, STREAM_TOSERVER,
};
use crate::filecontainer::*;
use crate::filetracker::*;
Expand Down Expand Up @@ -129,11 +132,12 @@ pub struct HTTP2Transaction {
pub frames_ts: Vec<HTTP2Frame>,

decoder: decompression::HTTP2Decoder,
pub file_range: *mut HttpRangeContainerBlock,

de_state: Option<*mut core::DetectEngineState>,
events: *mut core::AppLayerDecoderEvents,
tx_data: AppLayerTxData,
ft_tc: FileTransferTracker,
pub ft_tc: FileTransferTracker,
ft_ts: FileTransferTracker,

//temporary escaped header for detection
Expand All @@ -151,6 +155,7 @@ impl HTTP2Transaction {
frames_tc: Vec::new(),
frames_ts: Vec::new(),
decoder: decompression::HTTP2Decoder::new(),
file_range: std::ptr::null_mut(),
de_state: None,
events: std::ptr::null_mut(),
tx_data: AppLayerTxData::new(),
Expand All @@ -167,6 +172,27 @@ impl HTTP2Transaction {
if let Some(state) = self.de_state {
core::sc_detect_engine_state_free(state);
}
if self.file_range != std::ptr::null_mut() {
match unsafe { SC } {
None => panic!("BUG no suricata_config"),
Some(c) => {
//TODO get a file container instead of NULL
(c.HTPFileCloseHandleRange)(
std::ptr::null_mut(),
0,
self.file_range,
std::ptr::null_mut(),
0,
);
(c.HttpRangeFreeBlock)(self.file_range);
}
}
}
}

pub fn set_event(&mut self, event: HTTP2Event) {
let ev = event as u8;
core::sc_app_layer_decoder_events_set_event_raw(&mut self.events, ev);
}

fn handle_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: u8) {
Expand All @@ -179,7 +205,7 @@ impl HTTP2Transaction {

fn decompress<'a>(
&'a mut self, input: &'a [u8], dir: u8, sfcm: &'static SuricataFileContext, over: bool,
files: &mut FileContainer, flags: u16,
files: &mut FileContainer, flags: u16, flow: *const Flow,
) -> io::Result<()> {
let mut output = Vec::with_capacity(decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE);
let decompressed = self.decoder.decompress(input, &mut output, dir)?;
Expand All @@ -190,6 +216,31 @@ impl HTTP2Transaction {
// we are now sure that new_chunk will open a file
// even if it may close it right afterwards
self.tx_data.incr_files_opened();
if let Ok(value) = detect::http2_frames_get_header_value_vec(
self,
STREAM_TOCLIENT,
"content-range",
) {
match range::http2_parse_content_range(&value) {
Ok((_, v)) => {
range::http2_range_open(self, &v, flow, sfcm, flags, decompressed);
if over {
range::http2_range_close(self, files, flags, &[])
}
}
_ => {
self.set_event(HTTP2Event::InvalidRange);
}
}
}
} else {
if self.file_range != std::ptr::null_mut() {
if over {
range::http2_range_close(self, files, flags, decompressed)
} else {
range::http2_range_append(self.file_range, decompressed)
}
}
}
self.ft_tc.new_chunk(
sfcm,
Expand Down Expand Up @@ -321,6 +372,7 @@ pub enum HTTP2Event {
StreamIdReuse,
InvalidHTTP1Settings,
FailedDecompression,
InvalidRange,
}

pub struct HTTP2DynTable {
Expand Down Expand Up @@ -350,6 +402,7 @@ pub struct HTTP2State {
transactions: Vec<HTTP2Transaction>,
progress: HTTP2ConnectionState,
pub files: Files,
flow: *const Flow,
}

impl HTTP2State {
Expand All @@ -366,6 +419,7 @@ impl HTTP2State {
transactions: Vec::new(),
progress: HTTP2ConnectionState::Http2StateInit,
files: Files::default(),
flow: std::ptr::null_mut(),
}
}

Expand Down Expand Up @@ -825,6 +879,7 @@ impl HTTP2State {
over,
files,
flags,
self.flow,
) {
Err(_e) => {
self.set_event(HTTP2Event::FailedDecompression);
Expand Down Expand Up @@ -1025,6 +1080,7 @@ pub unsafe extern "C" fn rs_http2_parse_ts(

state.files.flags_ts = FileFlowToFlags(flow, STREAM_TOSERVER);
state.files.flags_ts = state.files.flags_ts | FILE_USE_DETECT;
state.flow = flow;
return state.parse_ts(buf);
}

Expand All @@ -1037,6 +1093,7 @@ pub unsafe extern "C" fn rs_http2_parse_tc(
let buf = build_slice!(input, input_len as usize);
state.files.flags_tc = FileFlowToFlags(flow, STREAM_TOCLIENT);
state.files.flags_tc = state.files.flags_tc | FILE_USE_DETECT;
state.flow = flow;
return state.parse_tc(buf);
}

Expand Down
1 change: 1 addition & 0 deletions rust/src/http2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ pub mod http2;
mod huffman;
pub mod logger;
mod parser;
mod range;
Loading

0 comments on commit 2758109

Please sign in to comment.