diff --git a/rust/src/applayer.rs b/rust/src/applayer.rs index 8eacadcc0089..9e10a642611c 100644 --- a/rust/src/applayer.rs +++ b/rust/src/applayer.rs @@ -298,7 +298,8 @@ extern { } // Defined in app-layer-parser.h -pub const APP_LAYER_PARSER_EOF : u8 = 0b0; +pub const APP_LAYER_PARSER_EOF_TS : u8 = 0b0101; +pub const APP_LAYER_PARSER_EOF_TC : u8 = 0b0110; pub const APP_LAYER_PARSER_NO_INSPECTION : u8 = 0b1; pub const APP_LAYER_PARSER_NO_REASSEMBLY : u8 = 0b10; pub const APP_LAYER_PARSER_NO_INSPECTION_PAYLOAD : u8 = 0b100; diff --git a/rust/src/applayertemplate/template.rs b/rust/src/applayertemplate/template.rs index 25576b51c49d..c82e49bf05b2 100644 --- a/rust/src/applayertemplate/template.rs +++ b/rust/src/applayertemplate/template.rs @@ -316,7 +316,7 @@ pub extern "C" fn rs_template_parse_request( _flags: u8, ) -> AppLayerResult { let eof = unsafe { - if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF) > 0 { + if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TS) > 0 { true } else { false @@ -352,7 +352,7 @@ pub extern "C" fn rs_template_parse_response( _flags: u8, ) -> AppLayerResult { let _eof = unsafe { - if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF) > 0 { + if AppLayerParserStateIssetFlag(pstate, APP_LAYER_PARSER_EOF_TC) > 0 { true } else { false diff --git a/rust/src/core.rs b/rust/src/core.rs index 07e49be071cf..727d4605cb79 100644 --- a/rust/src/core.rs +++ b/rust/src/core.rs @@ -77,6 +77,8 @@ pub type SCLogMessageFunc = pub type DetectEngineStateFreeFunc = extern "C" fn(state: *mut DetectEngineState); +pub type AppLayerParserTriggerRawStreamReassemblyFunc = + extern "C" fn (flow: *const Flow, direction: i32); pub type AppLayerDecoderEventsSetEventRawFunc = extern "C" fn (events: *mut *mut AppLayerDecoderEvents, event: u8); @@ -129,6 +131,7 @@ pub struct SuricataContext { DetectEngineStateFree: DetectEngineStateFreeFunc, AppLayerDecoderEventsSetEventRaw: AppLayerDecoderEventsSetEventRawFunc, AppLayerDecoderEventsFreeEvents: AppLayerDecoderEventsFreeEventsFunc, + pub AppLayerParserTriggerRawStreamReassembly: AppLayerParserTriggerRawStreamReassemblyFunc, pub FileOpenFile: SCFileOpenFileWithId, pub FileCloseFile: SCFileCloseFileById, @@ -176,6 +179,15 @@ pub fn sc_detect_engine_state_free(state: *mut DetectEngineState) } } +/// AppLayerParserTriggerRawStreamReassembly wrapper +pub fn sc_app_layer_parser_trigger_raw_stream_reassembly(flow: *const Flow, direction: i32) { + unsafe { + if let Some(c) = SC { + (c.AppLayerParserTriggerRawStreamReassembly)(flow, direction); + } + } +} + /// AppLayerDecoderEventsSetEventRaw wrapper. pub fn sc_app_layer_decoder_events_set_event_raw( events: *mut *mut AppLayerDecoderEvents, event: u8) diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 70786abe02ee..e5c26ed7d9e0 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -16,8 +16,8 @@ */ use std::mem::transmute; -use crate::applayer::{AppLayerResult, AppLayerTxData}; -use crate::core::{self, sc_detect_engine_state_free}; +use crate::applayer::*; +use crate::core::{self, *}; use crate::dcerpc::parser; use nom::error::ErrorKind; use nom::number::Endianness; @@ -350,6 +350,7 @@ pub struct DCERPCState { pub tc_ssn_gap: bool, pub ts_ssn_trunc: bool, /// true if Truncated in this direction pub tc_ssn_trunc: bool, + pub flow: Option<*const core::Flow>, } impl DCERPCState { @@ -376,6 +377,7 @@ impl DCERPCState { tc_ssn_gap: false, ts_ssn_trunc: false, tc_ssn_trunc: false, + flow: None, }; } @@ -618,6 +620,9 @@ impl DCERPCState { tx.req_lost = true; } tx.req_done = true; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, dir.into()); + } } } else if self.tc_ssn_gap && dir == core::STREAM_TOCLIENT { for tx in &mut self.transactions { @@ -633,6 +638,9 @@ impl DCERPCState { } tx.req_done = true; tx.resp_done = true; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, dir.into()); + } } } } @@ -745,6 +753,9 @@ impl DCERPCState { let mut tx = self.create_tx(call_id); tx.req_cmd = self.get_hdr_type().unwrap_or(0); tx.req_done = true; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, core::STREAM_TOSERVER.into()); + } tx.frag_cnt_ts = 1; self.transactions.push(tx); // Bytes parsed with `parse_dcerpc_bind` + (bytes parsed per bindctxitem [44] * number @@ -827,6 +838,9 @@ impl DCERPCState { ); tx.req_done = true; tx.frag_cnt_ts = 1; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, core::STREAM_TOSERVER.into()); + } } DCERPC_TYPE_RESPONSE => { retval = evaluate_stub_params( @@ -839,6 +853,9 @@ impl DCERPCState { ); tx.resp_done = true; tx.frag_cnt_tc = 1; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, core::STREAM_TOCLIENT.into()); + } } _ => { SCLogDebug!("Unrecognized packet type"); @@ -1069,6 +1086,9 @@ impl DCERPCState { }; tx.resp_done = true; tx.frag_cnt_tc = 1; + if let Some(flow) = self.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, core::STREAM_TOCLIENT.into()); + } self.handle_bind_cache(current_call_id, false); } DCERPC_TYPE_REQUEST => { @@ -1163,7 +1183,7 @@ pub extern "C" fn rs_parse_dcerpc_response_gap( #[no_mangle] pub extern "C" fn rs_dcerpc_parse_request( - _flow: *mut core::Flow, state: &mut DCERPCState, _pstate: *mut std::os::raw::c_void, + flow: *mut core::Flow, state: &mut DCERPCState, _pstate: *mut std::os::raw::c_void, input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, flags: u8, ) -> AppLayerResult { SCLogDebug!("Handling request: input {:p} input_len {} flags {:x} EOF {}", @@ -1177,6 +1197,7 @@ pub extern "C" fn rs_dcerpc_parse_request( } if input_len > 0 && input != std::ptr::null_mut() { let buf = build_slice!(input, input_len as usize); + state.flow = Some(flow); return state.handle_input_data(buf, core::STREAM_TOSERVER); } AppLayerResult::err() @@ -1184,7 +1205,7 @@ pub extern "C" fn rs_dcerpc_parse_request( #[no_mangle] pub extern "C" fn rs_dcerpc_parse_response( - _flow: *mut core::Flow, state: &mut DCERPCState, _pstate: *mut std::os::raw::c_void, + flow: *mut core::Flow, state: &mut DCERPCState, _pstate: *mut std::os::raw::c_void, input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, flags: u8, ) -> AppLayerResult { if flags & core::STREAM_EOF != 0 && input_len == 0 { @@ -1197,6 +1218,7 @@ pub extern "C" fn rs_dcerpc_parse_response( if input_len > 0 { if input != std::ptr::null_mut() { let buf = build_slice!(input, input_len as usize); + state.flow = Some(flow); return state.handle_input_data(buf, core::STREAM_TOCLIENT); } } @@ -1229,12 +1251,18 @@ pub extern "C" fn rs_dcerpc_state_trunc(state: *mut std::os::raw::c_void, direct dce_state.ts_ssn_trunc = true; for tx in &mut dce_state.transactions { tx.req_done = true; + if let Some(flow) = dce_state.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, core::STREAM_TOSERVER.into()); + } } SCLogDebug!("dce_state.ts_ssn_trunc = true; txs {}", dce_state.transactions.len()); } else if direction & core::STREAM_TOCLIENT != 0 { dce_state.tc_ssn_trunc = true; for tx in &mut dce_state.transactions { tx.resp_done = true; + if let Some(flow) = dce_state.flow { + sc_app_layer_parser_trigger_raw_stream_reassembly(flow, core::STREAM_TOCLIENT.into()); + } } SCLogDebug!("dce_state.tc_ssn_trunc = true; txs {}", dce_state.transactions.len()); } @@ -1323,6 +1351,46 @@ pub unsafe extern "C" fn rs_dcerpc_get_stub_data( *endianness = tx.get_endianness(); } +/// Probe input to see if it looks like DCERPC. +fn probe(input: &[u8]) -> (bool, bool) { + match parser::parse_dcerpc_header(input) { + Ok((_, hdr)) => { + let is_request = hdr.hdrtype == 0x00; + let is_dcerpc = hdr.rpc_vers == 0x05 && hdr.rpc_vers_minor == 0x00; + return (is_dcerpc, is_request); + }, + Err(_) => (false, false), + } +} + +#[no_mangle] +pub extern "C" fn rs_dcerpc_probe_tcp(direction: u8, input: *const u8, + len: u32, rdir: *mut u8) -> i32 +{ + SCLogDebug!("Probing packet for DCERPC"); + if len == 0 { + return core::ALPROTO_UNKNOWN; + } + let slice: &[u8] = unsafe { + std::slice::from_raw_parts(input as *mut u8, len as usize) + }; + //is_incomplete is checked by caller + let (is_dcerpc, is_request, ) = probe(slice); + if is_dcerpc { + let dir = if is_request { + core::STREAM_TOSERVER + } else { + core::STREAM_TOCLIENT + }; + if direction & (core::STREAM_TOSERVER|core::STREAM_TOCLIENT) != dir { + unsafe { *rdir = dir }; + } + return 1; + } + return 0; + +} + #[cfg(test)] mod tests { use crate::applayer::AppLayerResult; diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 959b02483ec4..1ed151859b62 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -275,7 +275,7 @@ pub extern "C" fn rs_dcerpc_udp_get_tx( let dce_state = cast_pointer!(state, DCERPCUDPState); match dce_state.get_tx(tx_id) { Some(tx) => { - return unsafe{transmute(tx)}; + return unsafe{&mut *(tx as *mut DCERPCTransaction)}; }, None => { return std::ptr::null_mut(); @@ -289,6 +289,50 @@ pub extern "C" fn rs_dcerpc_udp_get_tx_cnt(vtx: *mut std::os::raw::c_void) -> u6 dce_state.tx_id } +/// Probe input to see if it looks like DCERPC. +fn probe(input: &[u8]) -> (bool, bool) { + match parser::parse_dcerpc_udp_header(input) { + Ok((_, hdr)) => { + let is_request = hdr.pkt_type == 0x00; + let is_dcerpc = hdr.rpc_vers == 0x04 && + (hdr.flags2 & 0xfc == 0) && + (hdr.drep[0] & 0xee == 0) && + (hdr.drep[1] <= 3); + return (is_dcerpc, is_request); + }, + Err(_) => (false, false), + } +} + +#[no_mangle] +pub extern "C" fn rs_dcerpc_probe_udp(direction: u8, input: *const u8, + len: u32, rdir: *mut u8) -> i32 +{ + SCLogDebug!("Probing the packet for DCERPC/UDP"); + if len == 0 { + return core::ALPROTO_UNKNOWN; + } + let slice: &[u8] = unsafe { + std::slice::from_raw_parts(input as *mut u8, len as usize) + }; + //is_incomplete is checked by caller + let (is_dcerpc, is_request) = probe(slice); + if is_dcerpc { + let dir = if is_request { + core::STREAM_TOSERVER + } else { + core::STREAM_TOCLIENT + }; + if direction & (core::STREAM_TOSERVER|core::STREAM_TOCLIENT) != dir { + unsafe { *rdir = dir }; + } + return 1; + } + return 0; + +} + + #[cfg(test)] mod tests { use crate::applayer::AppLayerResult; diff --git a/src/app-layer-dcerpc-udp.c b/src/app-layer-dcerpc-udp.c index d38378ae070e..b784865ba447 100644 --- a/src/app-layer-dcerpc-udp.c +++ b/src/app-layer-dcerpc-udp.c @@ -102,11 +102,27 @@ static int RustDCERPCUDPGetAlstateProgress(void *tx, uint8_t direction) return rs_dcerpc_get_alstate_progress(tx, direction); } +static uint16_t DCERPCUDPProbe( + Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir) +{ + SCLogDebug("DCERPCUDPProbe"); + + const int r = rs_dcerpc_probe_udp(direction, input, len, rdir); + switch (r) { + case 1: + return ALPROTO_DCERPC; + case 0: + return ALPROTO_UNKNOWN; + case -1: + default: + return ALPROTO_FAILED; + } +} + static int DCERPCUDPRegisterPatternsForProtocolDetection(void) { - if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_UDP, ALPROTO_DCERPC, - "|04 00|", 2, 0, STREAM_TOSERVER) < 0) - { + if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_UDP, ALPROTO_DCERPC, "|04 00|", 2, 0, + STREAM_TOSERVER, DCERPCUDPProbe, 0, 0) < 0) { return -1; } diff --git a/src/app-layer-dcerpc.c b/src/app-layer-dcerpc.c index 40cd753bf0a2..82700b1cdda2 100644 --- a/src/app-layer-dcerpc.c +++ b/src/app-layer-dcerpc.c @@ -126,16 +126,31 @@ static int DCERPCGetAlstateProgress(void *tx, uint8_t direction) return rs_dcerpc_get_alstate_progress(tx, direction); } +static uint16_t DCERPCTCPProbe( + Flow *f, uint8_t direction, const uint8_t *input, uint32_t len, uint8_t *rdir) +{ + SCLogDebug("DCERPCTCPProbe"); + + const int r = rs_dcerpc_probe_tcp(direction, input, len, rdir); + switch (r) { + case 1: + return ALPROTO_DCERPC; + case 0: + return ALPROTO_UNKNOWN; + case -1: + default: + return ALPROTO_FAILED; + } +} + static int DCERPCRegisterPatternsForProtocolDetection(void) { - if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC, - "|05 00|", 2, 0, STREAM_TOSERVER) < 0) - { + if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_DCERPC, "|05 00|", 2, 0, + STREAM_TOSERVER, DCERPCTCPProbe, 0, 0) < 0) { return -1; } - if (AppLayerProtoDetectPMRegisterPatternCS(IPPROTO_TCP, ALPROTO_DCERPC, - "|05 00|", 2, 0, STREAM_TOCLIENT) < 0) - { + if (AppLayerProtoDetectPMRegisterPatternCSwPP(IPPROTO_TCP, ALPROTO_DCERPC, "|05 00|", 2, 0, + STREAM_TOCLIENT, DCERPCTCPProbe, 0, 0) < 0) { return -1; } diff --git a/src/rust-context.h b/src/rust-context.h index 8efd2678a3d4..46223f6b06d6 100644 --- a/src/rust-context.h +++ b/src/rust-context.h @@ -32,6 +32,7 @@ typedef struct SuricataContext_ { void (*AppLayerDecoderEventsSetEventRaw)(AppLayerDecoderEvents **, uint8_t); void (*AppLayerDecoderEventsFreeEvents)(AppLayerDecoderEvents **); + void (*AppLayerParserTriggerRawStreamReassembly)(Flow *, int direction); int (*FileOpenFileWithId)(FileContainer *, const StreamingBufferConfig *, uint32_t track_id, const uint8_t *name, uint16_t name_len, diff --git a/src/suricata.c b/src/suricata.c index 15a84e2f9f25..5728da50251a 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -2678,6 +2678,8 @@ int InitGlobal(void) { suricata_context.DetectEngineStateFree = DetectEngineStateFree; suricata_context.AppLayerDecoderEventsSetEventRaw = AppLayerDecoderEventsSetEventRaw; suricata_context.AppLayerDecoderEventsFreeEvents = AppLayerDecoderEventsFreeEvents; + suricata_context.AppLayerParserTriggerRawStreamReassembly = + AppLayerParserTriggerRawStreamReassembly; suricata_context.FileOpenFileWithId = FileOpenFileWithId; suricata_context.FileCloseFileById = FileCloseFileById;