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 all commits
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
4 changes: 3 additions & 1 deletion rules/http-events.rules
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,6 @@ alert http any any -> any any (msg:"SURICATA HTTP compression bomb"; flow:establ

alert http any any -> any any (msg:"SURICATA HTTP too many warnings"; flow:established; app-layer-event:http.too_many_warnings; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221050; rev:1;)

# next sid 2221051
alert http any any -> any any (msg:"SURICATA HTTP invalid Range header value"; flow:established; app-layer-event:http.range_invalid; flowint:http.anomaly.count,+,1; classtype:protocol-command-decode; sid:2221051; rev:1;)

# next sid 2221052
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
22 changes: 18 additions & 4 deletions rust/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,22 @@ pub type AppLayerDecoderEventsSetEventRawFunc =
pub type AppLayerDecoderEventsFreeEventsFunc =
extern "C" fn (events: *mut *mut AppLayerDecoderEvents);

pub enum SuricataStreamingBufferConfig {}

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: &SuricataStreamingBufferConfig,
sbcfg: &StreamingBufferConfig,
track_id: u32,
name: *const u8, name_len: u16,
data: *const u8, data_len: u32,
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 All @@ -158,7 +172,7 @@ pub struct SuricataContext {
#[allow(non_snake_case)]
#[repr(C)]
pub struct SuricataFileContext {
pub files_sbcfg: &'static SuricataStreamingBufferConfig,
pub files_sbcfg: &'static StreamingBufferConfig,
}

extern {
Expand Down
4 changes: 2 additions & 2 deletions rust/src/http2/decompression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl HTTP2DecoderHalf {
}

pub fn decompress<'a>(
&'a mut self, input: &'a [u8], output: &'a mut Vec<u8>,
&mut self, input: &'a [u8], output: &'a mut Vec<u8>,
) -> io::Result<&'a [u8]> {
match self.decoder {
HTTP2Decompresser::GZIP(ref mut gzip_decoder) => {
Expand Down Expand Up @@ -249,7 +249,7 @@ impl HTTP2Decoder {
}

pub fn decompress<'a>(
&'a mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: u8,
&mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: u8,
) -> io::Result<&'a [u8]> {
if dir == STREAM_TOCLIENT {
return self.decoder_tc.decompress(input, output);
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