Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/http range/v29 #6355

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: address or remove comment if its not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be much easier when we have file per tx

Right now, I should do something like state.files.get(STREAM_TOCLIENT) but I do not have an easy reference to state in HTTP2Transaction::free

So, what should we do ?

(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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally we're not adding this if we can just pass it around as a function argument

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right

}

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