diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index 7bc8d4da47b..7617e56d3c7 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -80,6 +80,7 @@ pub const DCERPC_TYPE_SHUTDOWN: u8 = 17; pub const DCERPC_TYPE_CO_CANCEL: u8 = 18; pub const DCERPC_TYPE_ORPHANED: u8 = 19; pub const DCERPC_TYPE_RTS: u8 = 20; +pub const DCERPC_TYPE_UNKNOWN: u8 = 99; pub fn dcerpc_type_string(t: u8) -> String { match t { @@ -104,6 +105,7 @@ pub fn dcerpc_type_string(t: u8) -> String { DCERPC_TYPE_CO_CANCEL => "CO_CANCEL", DCERPC_TYPE_ORPHANED => "ORPHANED", DCERPC_TYPE_RTS => "RTS", + DCERPC_TYPE_UNKNOWN => "UNKNOWN", _ => { return (t).to_string(); } @@ -111,50 +113,98 @@ pub fn dcerpc_type_string(t: u8) -> String { .to_string() } +pub fn get_resp_type_for_req(t: u8) -> u8 { + match t { + DCERPC_TYPE_REQUEST => DCERPC_TYPE_RESPONSE, + DCERPC_TYPE_BIND => DCERPC_TYPE_BINDACK, + DCERPC_TYPE_ALTER_CONTEXT => DCERPC_TYPE_ALTER_CONTEXT_RESP, + _ => DCERPC_TYPE_UNKNOWN, + } +} + +pub fn get_req_type_for_resp(t: u8) -> u8 { + match t { + DCERPC_TYPE_RESPONSE => DCERPC_TYPE_REQUEST, + DCERPC_TYPE_BINDACK => DCERPC_TYPE_BIND, + DCERPC_TYPE_ALTER_CONTEXT_RESP => DCERPC_TYPE_ALTER_CONTEXT, + _ => DCERPC_TYPE_UNKNOWN, + } +} + #[derive(Debug)] -pub struct DCERPCRequest { +pub struct DCERPCTransaction { + pub id: u32, // internal transaction ID pub ctxid: u16, pub opnum: u16, pub first_request_seen: u8, - pub stub_data_buffer: Vec, - pub stub_data_buffer_len: u16, - pub stub_data_buffer_reset: bool, - pub cmd: u8, + pub call_id: u32, // ID to match any request-response pair + pub frag_cnt_ts: u16, + pub frag_cnt_tc: u16, + pub endianness: u8, + pub stub_data_buffer_ts: Vec, + pub stub_data_buffer_tc: Vec, + pub stub_data_buffer_len_ts: u16, + pub stub_data_buffer_len_tc: u16, + pub stub_data_buffer_reset_ts: bool, + pub stub_data_buffer_reset_tc: bool, + pub req_done: bool, + pub resp_done: bool, + pub req_cmd: u8, + pub resp_cmd: u8, + pub tx_data: AppLayerTxData, + pub de_state: Option<*mut core::DetectEngineState>, } -impl DCERPCRequest { - pub fn new() -> DCERPCRequest { - return DCERPCRequest { +impl DCERPCTransaction { + pub fn new() -> DCERPCTransaction { + return DCERPCTransaction { + id: 0, ctxid: 0, opnum: 0, first_request_seen: 0, - stub_data_buffer: Vec::new(), - stub_data_buffer_len: 0, - stub_data_buffer_reset: false, - cmd: DCERPC_TYPE_REQUEST, + call_id: 0, + frag_cnt_ts: 0, + frag_cnt_tc: 0, + endianness: 0, + stub_data_buffer_ts: Vec::new(), + stub_data_buffer_tc: Vec::new(), + stub_data_buffer_len_ts: 0, // TODO maybe retrieve length from buffer and avoid this param + stub_data_buffer_len_tc: 0, + stub_data_buffer_reset_ts: false, + stub_data_buffer_reset_tc: false, + req_done: false, + resp_done: false, + req_cmd: DCERPC_TYPE_REQUEST, + resp_cmd: DCERPC_TYPE_RESPONSE, + tx_data: AppLayerTxData::new(), + de_state: None, }; } -} -#[derive(Debug)] -pub struct DCERPCResponse { - pub stub_data_buffer: Vec, - pub stub_data_buffer_len: u16, - pub stub_data_buffer_reset: bool, - pub cmd: u8, -} + pub fn get_req_ctxid(&self) -> u16 { + self.ctxid + } -impl DCERPCResponse { - pub fn new() -> DCERPCResponse { - return DCERPCResponse { - stub_data_buffer: Vec::new(), - stub_data_buffer_len: 0, - stub_data_buffer_reset: false, - cmd: DCERPC_TYPE_RESPONSE, - }; + pub fn get_first_req_seen(&self) -> u8 { + self.first_request_seen + } + + pub fn get_req_opnum(&self) -> u16 { + self.opnum + } + + pub fn get_endianness(&self) -> u8 { + self.endianness } } +#[derive(Debug)] +pub struct DCERPCRequest { + pub ctxid: u16, + pub opnum: u16, + pub first_request_seen: u8, +} + #[derive(Debug, Clone)] pub struct DCERPCUuidEntry { pub ctxid: u16, @@ -237,19 +287,18 @@ pub struct DCERPCState { pub header: Option, pub bind: Option, pub bindack: Option, - pub request: Option, - pub response: Option, + pub transactions: Vec, pub buffer_ts: Vec, pub buffer_tc: Vec, pub pad: u8, pub padleft: u16, pub bytes_consumed: u16, - pub tx_id: u16, + pub tx_id: u32, pub query_completed: bool, pub data_needed_for_dir: u8, pub prev_dir: u8, - pub de_state: Option<*mut core::DetectEngineState>, - pub tx_data: AppLayerTxData, + pub prev_tx_call_id: u32, + pub clear_bind_cache: bool, } impl DCERPCState { @@ -258,8 +307,7 @@ impl DCERPCState { header: None, bind: None, bindack: None, - request: None, - response: None, + transactions: Vec::new(), buffer_ts: Vec::new(), buffer_tc: Vec::new(), pad: 0, @@ -269,11 +317,21 @@ impl DCERPCState { query_completed: false, data_needed_for_dir: core::STREAM_TOSERVER, prev_dir: core::STREAM_TOSERVER, - de_state: None, - tx_data: AppLayerTxData::new(), + prev_tx_call_id: 0, + clear_bind_cache: false, }; } + fn create_tx(&mut self, call_id: u32) -> DCERPCTransaction { + let mut tx = DCERPCTransaction::new(); + let endianness = self.get_hdr_drep_0() & 0x10; + tx.id = self.tx_id; + tx.call_id = call_id; + tx.endianness = endianness; + self.tx_id += 1; + tx + } + fn get_hdr_drep_0(&self) -> u8 { if let Some(ref hdr) = &self.header { return hdr.packed_drep[0]; @@ -325,28 +383,10 @@ impl DCERPCState { None } - pub fn get_req_ctxid(&self) -> Option { - debug_validate_bug_on!(self.request.is_none()); - if let Some(ref req) = self.request { - return Some(req.ctxid); - } - // Shouldn't happen - None - } - - pub fn get_first_req_seen(&self) -> Option { - debug_validate_bug_on!(self.request.is_none()); - if let Some(ref req) = self.request { - return Some(req.first_request_seen); - } - // Shouldn't happen - None - } - - pub fn get_req_opnum(&self) -> Option { - debug_validate_bug_on!(self.request.is_none()); - if let Some(ref req) = self.request { - return Some(req.opnum); + pub fn get_hdr_call_id(&self) -> Option { + debug_validate_bug_on!(self.header.is_none()); + if let Some(ref hdr) = self.header { + return Some(hdr.call_id); } // Shouldn't happen None @@ -398,6 +438,78 @@ impl DCERPCState { } } + /// Get transaction as per the given transaction ID. Transaction ID with + /// which the lookup is supposed to be done as per the calls from AppLayer + /// parser in C. This requires an internal transaction ID to be maintained. + /// + /// Arguments: + /// * `tx_id`: + /// type: unsigned 32 bit integer + /// description: internal transaction ID to track transactions + /// + /// Return value: + /// Option mutable reference to DCERPCTransaction + pub fn get_tx(&mut self, tx_id: u32) -> Option<&mut DCERPCTransaction> { + for tx in &mut self.transactions { + let found = tx.id == tx_id; + if found { + return Some(tx); + } + } + None + } + + /// Find the transaction as per call ID defined in header. If the tx is not + /// found, create one. + /// + /// Arguments: + /// * `call_id`: + /// type: unsigned 32 bit integer + /// description: call_id param derived from TCP Header + /// * `dir`: + /// type: unsigned 8 bit integer + /// description: direction of the flow + /// + /// Return value: + /// Option mutable reference to DCERPCTransaction + pub fn get_tx_by_call_id(&mut self, call_id: u32, dir: u8) -> Option<&mut DCERPCTransaction> { + let cmd = self.get_hdr_type().unwrap_or(0); + for tx in &mut self.transactions { + let found = tx.call_id == call_id; + if found { + match dir { + core::STREAM_TOSERVER => { + let resp_cmd = get_resp_type_for_req(cmd); + if resp_cmd != tx.resp_cmd { + continue; + } + } + _ => { + let req_cmd = get_req_type_for_resp(cmd); + if req_cmd != tx.req_cmd { + continue; + } + } + } + return Some(tx); + } + } + None + } + + pub fn handle_bind_cache(&mut self, call_id: u32, is_response: bool) { + if self.clear_bind_cache == true { + self.bind = None; + self.bindack = None; + } + if self.prev_tx_call_id == call_id && is_response == true { + self.clear_bind_cache = true; + } else { + self.clear_bind_cache = false; + } + self.prev_tx_call_id = call_id; + } + /// Makes a call to the nom parser for parsing DCERPC Header. /// /// Arguments: @@ -491,6 +603,12 @@ impl DCERPCState { } idx = retval + idx; } + let call_id = self.get_hdr_call_id().unwrap_or(0); + let mut tx = self.create_tx(call_id); + tx.req_cmd = self.get_hdr_type().unwrap_or(0); + tx.req_done = true; + tx.frag_cnt_ts = 1; + self.transactions.push(tx); // Bytes parsed with `parse_dcerpc_bind` + (bytes parsed per bindctxitem [44] * number // of bindctxitems) (input.len() - leftover_bytes.len()) as i32 + retval * numctxitems as i32 @@ -543,38 +661,48 @@ impl DCERPCState { } } - pub fn handle_stub_data(&mut self, input: &[u8], input_len: u16) -> u16 { - let mut retval: u16 = 0; + pub fn handle_stub_data(&mut self, input: &[u8], input_len: u16, dir: u8) -> u16 { + let retval; let hdrpfcflags = self.get_hdr_pfcflags().unwrap_or(0); let padleft = self.padleft; + let call_id = self.get_hdr_call_id().unwrap_or(0); + let hdrtype = self.get_hdr_type(); + let tx; + if let Some(transaction) = self.get_tx_by_call_id(call_id, dir) { + tx = transaction; + } else { + SCLogDebug!("No transaction found matching the call ID: {:?}", call_id); + return 0; + } + // Update the stub params based on the packet type - match self.get_hdr_type() { + match hdrtype { Some(x) => match x { DCERPC_TYPE_REQUEST => { - if let Some(ref mut req) = self.request { - retval = evaluate_stub_params( - input, - input_len, - hdrpfcflags, - padleft, - &mut req.stub_data_buffer, - &mut req.stub_data_buffer_len, - &mut req.stub_data_buffer_reset, - ); - } + retval = evaluate_stub_params( + input, + input_len, + hdrpfcflags, + padleft, + &mut tx.stub_data_buffer_ts, + &mut tx.stub_data_buffer_len_ts, + &mut tx.stub_data_buffer_reset_ts, + ); + tx.req_done = true; + tx.frag_cnt_ts = 1; } DCERPC_TYPE_RESPONSE => { - if let Some(ref mut resp) = self.response { - retval = evaluate_stub_params( - input, - input_len, - hdrpfcflags, - padleft, - &mut resp.stub_data_buffer, - &mut resp.stub_data_buffer_len, - &mut resp.stub_data_buffer_reset, - ); - } + retval = evaluate_stub_params( + input, + input_len, + hdrpfcflags, + padleft, + &mut tx.stub_data_buffer_tc, + &mut tx.stub_data_buffer_len_tc, + &mut tx.stub_data_buffer_reset_tc, + ); + tx.resp_done = true; + tx.frag_cnt_tc = 1; } _ => { SCLogDebug!("Unrecognized packet type"); @@ -616,7 +744,7 @@ impl DCERPCState { let mut input_left = input.len() as u16 - bytes_consumed; let mut parsed = bytes_consumed; while input_left > 0 && parsed < fraglen { - let retval = self.handle_stub_data(&input[parsed as usize..], input_left); + let retval = self.handle_stub_data(&input[parsed as usize..], input_left, dir); if retval > 0 && retval <= input_left { parsed += retval; input_left -= retval; @@ -639,9 +767,26 @@ impl DCERPCState { pub fn process_request_pdu(&mut self, input: &[u8]) -> i32 { let endianness = self.get_endianness(); match parser::parse_dcerpc_request(input, endianness) { - Ok((leftover_input, mut request)) => { - request.cmd = self.get_hdr_type().unwrap_or(0); - self.request = Some(request); + Ok((leftover_input, request)) => { + let call_id = self.get_hdr_call_id().unwrap_or(0); + let hdr_type = self.get_hdr_type().unwrap_or(0); + let mut transaction = self.get_tx_by_call_id(call_id, core::STREAM_TOSERVER); + match transaction { + Some(ref mut tx) => { + tx.req_cmd = hdr_type; + tx.ctxid = request.ctxid; + tx.opnum = request.opnum; + tx.first_request_seen = request.first_request_seen; + } + None => { + let mut tx = self.create_tx(call_id); + tx.req_cmd = hdr_type; + tx.ctxid = request.ctxid; + tx.opnum = request.opnum; + tx.first_request_seen = request.first_request_seen; + self.transactions.push(tx); + } + } let parsed = self.handle_common_stub( &input, (input.len() - leftover_input.len()) as u16, @@ -726,9 +871,10 @@ impl DCERPCState { } else { self.query_completed = true; } - // TODO buffer extra data when input length is more than fraglen parsed = self.bytes_consumed as i32; + let current_call_id = self.get_hdr_call_id().unwrap_or(0); + match self.get_hdr_type() { Some(x) => match x { DCERPC_TYPE_BIND | DCERPC_TYPE_ALTER_CONTEXT => { @@ -736,23 +882,47 @@ impl DCERPCState { if retval == -1 { return AppLayerResult::err(); } + self.handle_bind_cache(current_call_id, false); } DCERPC_TYPE_BINDACK | DCERPC_TYPE_ALTER_CONTEXT_RESP => { retval = self.process_bindack_pdu(&buffer[parsed as usize..]); if retval == -1 { return AppLayerResult::err(); } + let tx = if let Some(mut tx) = self.get_tx_by_call_id(current_call_id, core::STREAM_TOCLIENT) { + tx.resp_cmd = x; + tx + } else { + let mut tx = self.create_tx(current_call_id); + tx.resp_cmd = x; + self.transactions.push(tx); + self.transactions.last_mut().unwrap() + }; + tx.resp_done = true; + tx.frag_cnt_tc = 1; + self.handle_bind_cache(current_call_id, false); } DCERPC_TYPE_REQUEST => { retval = self.process_request_pdu(&buffer[parsed as usize..]); if retval == -1 { return AppLayerResult::err(); } + // In case the response came first, the transaction would complete later when + // the corresponding request also comes through + self.handle_bind_cache(current_call_id, false); } DCERPC_TYPE_RESPONSE => { - let mut response = DCERPCResponse::new(); - response.cmd = self.get_hdr_type().unwrap_or(DCERPC_TYPE_RESPONSE); - self.response = Some(response); + let transaction = self.get_tx_by_call_id(current_call_id, core::STREAM_TOCLIENT); + match transaction { + Some(mut tx) => { + tx.resp_cmd = x; + } + None => { + let mut tx = self.create_tx(current_call_id); + tx.resp_cmd = x; + self.transactions.push(tx); + } + }; retval = self.handle_common_stub( &buffer[parsed as usize..], 0, @@ -761,7 +931,7 @@ impl DCERPCState { if retval == -1 { return AppLayerResult::err(); } - self.tx_id += 1; // Complete response, maybe to be used in future? + self.handle_bind_cache(current_call_id, true); } _ => { SCLogDebug!("Unrecognized packet type"); @@ -786,13 +956,8 @@ impl DCERPCState { } fn evaluate_stub_params( - input: &[u8], - input_len: u16, - hdrflags: u8, - lenleft: u16, - stub_data_buffer: &mut Vec, - stub_data_buffer_len: &mut u16, - stub_data_buffer_reset: &mut bool, + input: &[u8], input_len: u16, hdrflags: u8, lenleft: u16, stub_data_buffer: &mut Vec, + stub_data_buffer_len: &mut u16, stub_data_buffer_reset: &mut bool, ) -> u16 { let stub_len: u16; let fragtype = hdrflags & (PFC_FIRST_FRAG | PFC_LAST_FRAG); @@ -813,8 +978,7 @@ fn evaluate_stub_params( #[no_mangle] pub extern "C" fn rs_parse_dcerpc_request_gap( - state: &mut DCERPCState, - _input_len: u32, + state: &mut DCERPCState, _input_len: u32, ) -> AppLayerResult { if state.handle_gap_ts() == 0 { return AppLayerResult::ok(); @@ -824,8 +988,7 @@ pub extern "C" fn rs_parse_dcerpc_request_gap( #[no_mangle] pub extern "C" fn rs_parse_dcerpc_response_gap( - state: &mut DCERPCState, - _input_len: u32, + state: &mut DCERPCState, _input_len: u32, ) -> AppLayerResult { if state.handle_gap_tc() == 0 { return AppLayerResult::ok(); @@ -835,13 +998,8 @@ 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, - input: *const u8, - input_len: u32, - _data: *mut std::os::raw::c_void, - flags: u8, + _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 input_len > 0 && input != std::ptr::null_mut() { let buf = build_slice!(input, input_len as usize); @@ -852,13 +1010,8 @@ 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, - input: *const u8, - input_len: u32, - _data: *mut std::os::raw::c_void, - flags: u8, + _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 input_len > 0 { if input != std::ptr::null_mut() { @@ -890,8 +1043,8 @@ pub extern "C" fn rs_dcerpc_state_transaction_free(_state: *mut std::os::raw::c_ pub extern "C" fn rs_dcerpc_get_tx_detect_state( vtx: *mut std::os::raw::c_void, ) -> *mut core::DetectEngineState { - let dce_state = cast_pointer!(vtx, DCERPCState); - match dce_state.de_state { + let dce_tx = cast_pointer!(vtx, DCERPCTransaction); + match dce_tx.de_state { Some(ds) => ds, None => std::ptr::null_mut(), } @@ -899,34 +1052,41 @@ pub extern "C" fn rs_dcerpc_get_tx_detect_state( #[no_mangle] pub extern "C" fn rs_dcerpc_set_tx_detect_state( - vtx: *mut std::os::raw::c_void, - de_state: *mut core::DetectEngineState, + vtx: *mut std::os::raw::c_void, de_state: *mut core::DetectEngineState, ) -> u8 { - let dce_state = cast_pointer!(vtx, DCERPCState); - dce_state.de_state = Some(de_state); + let dce_tx = cast_pointer!(vtx, DCERPCTransaction); + dce_tx.de_state = Some(de_state); 0 } #[no_mangle] pub extern "C" fn rs_dcerpc_get_tx( - state: *mut std::os::raw::c_void, - _tx_id: u64, -) -> *mut DCERPCState { - let dce_state = cast_pointer!(state, DCERPCState); - dce_state + vtx: *mut std::os::raw::c_void, tx_id: u32, +) -> *mut DCERPCTransaction { + let dce_state = cast_pointer!(vtx, DCERPCState); + match dce_state.get_tx(tx_id) { + Some(tx) => tx, + None => std::ptr::null_mut(), + } } #[no_mangle] -pub extern "C" fn rs_dcerpc_get_tx_cnt(_state: *mut std::os::raw::c_void) -> u8 { - 1 +pub extern "C" fn rs_dcerpc_get_tx_cnt(vtx: *mut std::os::raw::c_void) -> u32 { + let dce_state = cast_pointer!(vtx, DCERPCState); + dce_state.tx_id } #[no_mangle] -pub extern "C" fn rs_dcerpc_get_alstate_progress( - _tx: *mut std::os::raw::c_void, - _direction: u8, -) -> u8 { - 0 +pub extern "C" fn rs_dcerpc_get_alstate_progress(tx: &mut DCERPCTransaction, direction: u8) -> u8 { + if direction == core::STREAM_TOSERVER && tx.req_done { + SCLogDebug!("tx {} TOSERVER progress 1 => {:?}", tx.call_id, tx); + return 1; + } else if direction == core::STREAM_TOCLIENT && tx.resp_done { + SCLogDebug!("tx {} TOCLIENT progress 1 => {:?}", tx.call_id, tx); + return 1; + } + SCLogDebug!("tx {} direction {} progress 0", tx.call_id, direction); + return 0; } #[no_mangle] @@ -939,35 +1099,27 @@ pub extern "C" fn rs_dcerpc_get_tx_data( tx: *mut std::os::raw::c_void) -> *mut AppLayerTxData { - let tx = cast_pointer!(tx, DCERPCState); + let tx = cast_pointer!(tx, DCERPCTransaction); return &mut tx.tx_data; } #[no_mangle] pub unsafe extern "C" fn rs_dcerpc_get_stub_data( - state: &mut DCERPCState, - buf: *mut *const u8, - len: *mut u32, - endianness: *mut u8, - dir: u8, + tx: &mut DCERPCTransaction, buf: *mut *const u8, len: *mut u32, endianness: *mut u8, dir: u8, ) { match dir { core::STREAM_TOSERVER => { - if let Some(ref req) = state.request { - *len = req.stub_data_buffer_len as u32; - *buf = req.stub_data_buffer.as_ptr(); - SCLogDebug!("DCERPC Request stub buffer: Setting buffer to: {:?}", *buf); - } + *len = tx.stub_data_buffer_len_ts as u32; + *buf = tx.stub_data_buffer_ts.as_ptr(); + SCLogDebug!("DCERPC Request stub buffer: Setting buffer to: {:?}", *buf); } _ => { - if let Some(ref resp) = state.response { - *len = resp.stub_data_buffer_len as u32; - *buf = resp.stub_data_buffer.as_ptr(); - SCLogDebug!("DCERPC Response stub buffer: Setting buffer to: {:?}", *buf); - } + *len = tx.stub_data_buffer_len_tc as u32; + *buf = tx.stub_data_buffer_tc.as_ptr(); + SCLogDebug!("DCERPC Response stub buffer: Setting buffer to: {:?}", *buf); } } - *endianness = state.get_hdr_drep_0() & 0x10; + *endianness = tx.get_endianness(); } #[cfg(test)] @@ -1397,13 +1549,12 @@ mod tests { assert_eq!(5, hdr.rpc_vers); assert_eq!(1024, hdr.frag_length); } - if let Some(req) = dcerpc_state.request { - assert_eq!(11, req.ctxid); - assert_eq!(9, req.opnum); - assert_eq!(1, req.first_request_seen); - assert_eq!(1000, req.stub_data_buffer_len); - assert_eq!(true, req.stub_data_buffer_reset); - } + let tx = &dcerpc_state.transactions[0]; + assert_eq!(11, tx.ctxid); + assert_eq!(9, tx.opnum); + assert_eq!(1, tx.first_request_seen); + assert_eq!(1000, tx.stub_data_buffer_len_ts); + assert_eq!(true, tx.stub_data_buffer_reset_ts); } #[test] @@ -1531,9 +1682,8 @@ mod tests { AppLayerResult::ok(), dcerpc_state.handle_input_data(&request3, core::STREAM_TOSERVER) ); - if let Some(ref req) = dcerpc_state.request { - assert_eq!(20, req.stub_data_buffer_len); - } + let tx = &dcerpc_state.transactions[0]; + assert_eq!(20, tx.stub_data_buffer_len_ts); } #[test] @@ -1590,9 +1740,8 @@ mod tests { AppLayerResult::ok(), dcerpc_state.handle_input_data(&request2, core::STREAM_TOSERVER) ); - if let Some(ref req) = dcerpc_state.request { - assert_eq!(12, req.stub_data_buffer_len); - } + let tx = &dcerpc_state.transactions[0]; + assert_eq!(12, tx.stub_data_buffer_len_ts); } #[test] @@ -2099,10 +2248,9 @@ mod tests { AppLayerResult::ok(), dcerpc_state.handle_input_data(request2, core::STREAM_TOSERVER) ); - if let Some(ref req) = dcerpc_state.request { - assert_eq!(2, req.opnum); - assert_eq!(0, req.ctxid); - assert_eq!(14, req.stub_data_buffer_len); - } + let tx = &dcerpc_state.transactions[0]; + assert_eq!(2, tx.opnum); + assert_eq!(0, tx.ctxid); + assert_eq!(14, tx.stub_data_buffer_len_ts); } } diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index e4e5440d53b..3d1adf663df 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -20,8 +20,7 @@ use std::mem::transmute; use crate::applayer::{AppLayerResult, AppLayerTxData}; use crate::core; use crate::dcerpc::dcerpc::{ - DCERPCRequest, DCERPCResponse, DCERPCUuidEntry, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, - PFC_FIRST_FRAG, + DCERPCTransaction, DCERPCUuidEntry, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFC_FIRST_FRAG, }; use crate::dcerpc::parser; use crate::log::*; @@ -55,9 +54,9 @@ pub struct DCERPCHdrUdp { #[derive(Debug)] pub struct DCERPCUDPState { + pub tx_id: u32, pub header: Option, - pub request: Option, - pub response: Option, + pub transactions: Vec, pub fraglenleft: u16, pub uuid_entry: Option, pub uuid_list: Vec, @@ -68,9 +67,9 @@ pub struct DCERPCUDPState { impl DCERPCUDPState { pub fn new() -> DCERPCUDPState { return DCERPCUDPState { + tx_id: 0, header: None, - request: None, - response: None, + transactions: Vec::new(), fraglenleft: 0, uuid_entry: None, uuid_list: Vec::new(), @@ -79,27 +78,44 @@ impl DCERPCUDPState { }; } - fn new_request(&mut self) { - let request = DCERPCRequest::new(); - self.request = Some(request); + fn create_tx(&mut self, serial_no: u16) -> DCERPCTransaction { + let mut tx = DCERPCTransaction::new(); + let endianness = self.get_hdr_drep_0() & 0x10; + tx.id = self.tx_id; + tx.call_id = serial_no as u32; + tx.endianness = endianness; + self.tx_id += 1; + tx } - fn new_response(&mut self) { - let response = DCERPCResponse::new(); - self.response = Some(response); + + fn evaluate_serial_no(&mut self) -> u16 { + let mut serial_no: u16; + let mut serial_hi: u8 = 0; + let mut serial_lo: u8 = 0; + let endianness = self.get_hdr_drep_0(); + if let Some(ref hdr) = &self.header { + serial_hi = hdr.serial_hi; + serial_lo = hdr.serial_lo; + } + if endianness & 0x10 == 0 { + serial_no = serial_lo as u16; + serial_no = serial_no.rotate_left(8) | serial_hi as u16; + } else { + serial_no = serial_hi as u16; + serial_no = serial_no.rotate_left(8) | serial_lo as u16; + } + serial_no } - fn create_new_query(&mut self, pkt_type: u8) { - match pkt_type { - DCERPC_TYPE_REQUEST => { - self.new_request(); - } - DCERPC_TYPE_RESPONSE => { - self.new_response(); - } - _ => { - SCLogDebug!("Unrecognized packet type"); + + fn find_tx(&mut self, serial_no: u16) -> Option<&mut DCERPCTransaction> { + for tx in &mut self.transactions { + let found = tx.call_id == (serial_no as u32); + if found { + return Some(tx); } } + None } fn get_hdr_pkt_type(&self) -> Option { @@ -120,6 +136,14 @@ impl DCERPCUDPState { None } + fn get_hdr_drep_0(&self) -> u8 { + debug_validate_bug_on!(self.header.is_none()); + if let Some(ref hdr) = &self.header { + return hdr.drep[0]; + } + 0 + } + pub fn get_hdr_fraglen(&self) -> Option { debug_validate_bug_on!(self.header.is_none()); if let Some(ref hdr) = &self.header { @@ -130,35 +154,47 @@ impl DCERPCUDPState { } pub fn handle_fragment_data(&mut self, input: &[u8], input_len: u16) -> u16 { - let mut retval: u16 = 0; + let retval: u16; let hdrflags1 = self.get_hdr_flags1().unwrap_or(0); let fraglenleft = self.fraglenleft; + let hdrtype = self.get_hdr_pkt_type().unwrap_or(0); + let serial_no = self.evaluate_serial_no(); + let tx; + if let Some(transaction) = self.find_tx(serial_no) { + tx = transaction; + } else { + SCLogDebug!( + "No transaction found matching the serial number: {:?}", + serial_no + ); + return 0; + } // Update the stub params based on the packet type - match self.get_hdr_pkt_type().unwrap_or(0) { + match hdrtype { DCERPC_TYPE_REQUEST => { - if let Some(ref mut req) = self.request { - retval = evaluate_stub_params( - input, - input_len, - hdrflags1, - fraglenleft, - &mut req.stub_data_buffer, - &mut req.stub_data_buffer_len, - ); - } + retval = evaluate_stub_params( + input, + input_len, + hdrflags1, + fraglenleft, + &mut tx.stub_data_buffer_ts, + &mut tx.stub_data_buffer_len_ts, + ); + tx.req_done = true; + tx.frag_cnt_ts += 1; } DCERPC_TYPE_RESPONSE => { - if let Some(ref mut resp) = self.response { - retval = evaluate_stub_params( - input, - input_len, - hdrflags1, - fraglenleft, - &mut resp.stub_data_buffer, - &mut resp.stub_data_buffer_len, - ); - } + retval = evaluate_stub_params( + input, + input_len, + hdrflags1, + fraglenleft, + &mut tx.stub_data_buffer_tc, + &mut tx.stub_data_buffer_len_tc, + ); + tx.resp_done = true; + tx.frag_cnt_tc += 1; } _ => { SCLogDebug!("Unrecognized packet type"); @@ -210,10 +246,11 @@ impl DCERPCUDPState { } let mut input_left = input.len() as i32 - parsed; - let pkt_type = self.get_hdr_pkt_type().unwrap_or(0); let fraglen = self.get_hdr_fraglen().unwrap_or(0); self.fraglenleft = fraglen; - self.create_new_query(pkt_type); + let serial_no = self.evaluate_serial_no(); + let tx = self.create_tx(serial_no); + self.transactions.push(tx); // Parse rest of the body while parsed >= DCERPC_UDP_HDR_LEN && parsed < fraglen as i32 && input_left > 0 { let retval = self.handle_fragment_data(&input[parsed as usize..], input_left as u16); @@ -231,11 +268,7 @@ impl DCERPCUDPState { } fn evaluate_stub_params( - input: &[u8], - input_len: u16, - hdrflags: u8, - lenleft: u16, - stub_data_buffer: &mut Vec, + input: &[u8], input_len: u16, hdrflags: u8, lenleft: u16, stub_data_buffer: &mut Vec, stub_data_buffer_len: &mut u16, ) -> u16 { let stub_len: u16; @@ -260,13 +293,8 @@ fn evaluate_stub_params( #[no_mangle] pub extern "C" fn rs_dcerpc_udp_parse( - _flow: *mut core::Flow, - state: &mut DCERPCUDPState, - _pstate: *mut std::os::raw::c_void, - input: *const u8, - input_len: u32, - _data: *mut std::os::raw::c_void, - _flags: u8, + _flow: *mut core::Flow, state: &mut DCERPCUDPState, _pstate: *mut std::os::raw::c_void, + input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, _flags: u8, ) -> AppLayerResult { if input_len > 0 && input != std::ptr::null_mut() { let buf = build_slice!(input, input_len as usize); @@ -289,8 +317,7 @@ pub unsafe extern "C" fn rs_dcerpc_udp_state_new() -> *mut std::os::raw::c_void #[no_mangle] pub extern "C" fn rs_dcerpc_udp_state_transaction_free( - _state: *mut std::os::raw::c_void, - _tx_id: u64, + _state: *mut std::os::raw::c_void, _tx_id: u64, ) { // do nothing } @@ -308,8 +335,7 @@ pub extern "C" fn rs_dcerpc_udp_get_tx_detect_state( #[no_mangle] pub extern "C" fn rs_dcerpc_udp_set_tx_detect_state( - vtx: *mut std::os::raw::c_void, - de_state: *mut core::DetectEngineState, + vtx: *mut std::os::raw::c_void, de_state: *mut core::DetectEngineState, ) -> u8 { let dce_state = cast_pointer!(vtx, DCERPCUDPState); dce_state.de_state = Some(de_state); @@ -327,8 +353,7 @@ pub extern "C" fn rs_dcerpc_udp_get_tx_data( #[no_mangle] pub extern "C" fn rs_dcerpc_udp_get_tx( - state: *mut std::os::raw::c_void, - _tx_id: u64, + state: *mut std::os::raw::c_void, _tx_id: u64, ) -> *mut DCERPCUDPState { let dce_state = cast_pointer!(state, DCERPCUDPState); dce_state @@ -341,8 +366,7 @@ pub extern "C" fn rs_dcerpc_udp_get_tx_cnt(_state: *mut std::os::raw::c_void) -> #[no_mangle] pub extern "C" fn rs_dcerpc_udp_get_alstate_progress( - _tx: *mut std::os::raw::c_void, - _direction: u8, + _tx: *mut std::os::raw::c_void, _direction: u8, ) -> u8 { 0 } @@ -516,8 +540,9 @@ mod tests { dcerpcudp_state.handle_input_data(request) ); assert_eq!(0, dcerpcudp_state.fraglenleft); - if let Some(req) = dcerpcudp_state.request { - assert_eq!(1392, req.stub_data_buffer_len); - } + assert_eq!( + 1392, + dcerpcudp_state.transactions[0].stub_data_buffer_len_ts + ); } } diff --git a/rust/src/dcerpc/detect.rs b/rust/src/dcerpc/detect.rs index 2e9d78f8d60..a7edeaccbb0 100644 --- a/rust/src/dcerpc/detect.rs +++ b/rust/src/dcerpc/detect.rs @@ -16,7 +16,8 @@ */ use super::dcerpc::{ - DCERPCState, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, DCERPC_UUID_ENTRY_FLAG_FF, + DCERPCState, DCERPCTransaction, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, + DCERPC_UUID_ENTRY_FLAG_FF, }; use crate::log::*; use std::ffi::CStr; @@ -100,8 +101,10 @@ fn match_iface_version(version: u16, if_data: &DCEIfaceData) -> bool { } } -fn match_backuuid(state: &mut DCERPCState, if_data: &mut DCEIfaceData) -> u8 { - let mut ret = 1; +fn match_backuuid( + tx: &mut DCERPCTransaction, state: &mut DCERPCState, if_data: &mut DCEIfaceData, +) -> u8 { + let mut ret = 0; if let Some(ref bindack) = state.bindack { for uuidentry in bindack.accepted_uuid_list.iter() { ret = 1; @@ -123,7 +126,7 @@ fn match_backuuid(state: &mut DCERPCState, if_data: &mut DCEIfaceData) -> u8 { break; } } - let ctxid = state.get_req_ctxid().unwrap_or(0); + let ctxid = tx.get_req_ctxid(); ret = ret & ((uuidentry.ctxid == ctxid) as u8); if ret == 0 { SCLogDebug!("CTX IDs/UUIDs do not match"); @@ -243,16 +246,17 @@ fn parse_opnum_data(arg: &str) -> Result { } #[no_mangle] -pub extern "C" fn rs_dcerpc_iface_match(state: &mut DCERPCState, if_data: &mut DCEIfaceData) -> u8 { - let first_req_seen = state.get_first_req_seen().unwrap_or(0); +pub extern "C" fn rs_dcerpc_iface_match( + tx: &mut DCERPCTransaction, state: &mut DCERPCState, if_data: &mut DCEIfaceData, +) -> u8 { + let first_req_seen = tx.get_first_req_seen(); if first_req_seen == 0 { return 0; } match state.get_hdr_type() { Some(x) => match x { - DCERPC_TYPE_REQUEST | DCERPC_TYPE_RESPONSE => { - } + DCERPC_TYPE_REQUEST | DCERPC_TYPE_RESPONSE => {} _ => { return 0; } @@ -262,7 +266,7 @@ pub extern "C" fn rs_dcerpc_iface_match(state: &mut DCERPCState, if_data: &mut D } }; - return match_backuuid(state, if_data); + return match_backuuid(tx, state, if_data); } #[no_mangle] @@ -292,10 +296,13 @@ pub unsafe extern "C" fn rs_dcerpc_iface_free(ptr: *mut c_void) { #[no_mangle] pub unsafe extern "C" fn rs_dcerpc_opnum_match( - state: &mut DCERPCState, - opnum_data: &mut DCEOpnumData, + tx: &mut DCERPCTransaction, opnum_data: &mut DCEOpnumData, ) -> u8 { - let opnum = state.get_req_opnum().unwrap_or(0); // TODO is the default to 0 OK? + let first_req_seen = tx.get_first_req_seen(); + if first_req_seen == 0 { + return 0; + } + let opnum = tx.get_req_opnum(); for range in opnum_data.data.iter() { if range.range2 == DETECT_DCE_OPNUM_RANGE_UNINITIALIZED { if range.range1 == opnum as u32 { diff --git a/rust/src/dcerpc/parser.rs b/rust/src/dcerpc/parser.rs index 0356479e89a..a7552a40cfd 100644 --- a/rust/src/dcerpc/parser.rs +++ b/rust/src/dcerpc/parser.rs @@ -256,10 +256,6 @@ named_args!(pub parse_dcerpc_request(endianness: Endianness) , ctxid: ctxid, opnum: opnum, first_request_seen: 1, - stub_data_buffer: Vec::new(), - stub_data_buffer_len: 0, - stub_data_buffer_reset: false, - cmd: 0, } ) ) diff --git a/src/detect-dce-iface.c b/src/detect-dce-iface.c index 98e379b84c2..589ca37d54a 100644 --- a/src/detect-dce-iface.c +++ b/src/detect-dce-iface.c @@ -126,7 +126,7 @@ static int DetectDceIfaceMatchRust(DetectEngineThreadCtx *det_ctx, if (f->alproto == ALPROTO_DCERPC) { // TODO check if state is NULL - return rs_dcerpc_iface_match(state, (void *)m); + return rs_dcerpc_iface_match(txv, state, (void *)m); } int ret = 0; diff --git a/src/detect-dce-opnum.c b/src/detect-dce-opnum.c index ab46d96a504..fc64e316709 100644 --- a/src/detect-dce-opnum.c +++ b/src/detect-dce-opnum.c @@ -100,8 +100,7 @@ static int DetectDceOpnumMatchRust(DetectEngineThreadCtx *det_ctx, SCEnter(); if (f->alproto == ALPROTO_DCERPC) { - // TODO check for NULL state - return rs_dcerpc_opnum_match(state, (void *)m); + return rs_dcerpc_opnum_match(txv, (void *)m); } if (rs_smb_tx_match_dce_opnum(txv, (void *)m) != 1)