From 88adab8bd3d62912941647ab65032d5ec75c893a Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Wed, 1 Jul 2026 10:43:29 -0400 Subject: [PATCH] smb: fail transaction creation once the limit is reached new_tx() now refuses to create a transaction when the list is already at SMB_MAX_TX, returning None instead of a transaction. Every creation path -- the new_*_tx helpers and their callers across smb1/smb2/dcerpc/session/ files/ioctl -- propagates that, so no single input can create more than the limit, including a compound SMB2 request that chains many PDUs in one record. When the list is full the parser puts the flow into an error state and stops processing it. This replaces the previous force-completion of old transactions, which did not reliably bound the list and could leave transactions unreclaimable on asymmetric flows. The now-unused tx_index_completed bookkeeping is removed. Issue: 8629 (cherry picked from commit c8f68b0e4f9e721dca1c9ed6e08597c6964c7ac3) --- rust/src/smb/dcerpc.rs | 27 ++++++---- rust/src/smb/files.rs | 9 ++-- rust/src/smb/session.rs | 9 ++-- rust/src/smb/smb.rs | 101 +++++++++++++++++------------------ rust/src/smb/smb1.rs | 76 ++++++++++++++++++++------ rust/src/smb/smb1_session.rs | 7 ++- rust/src/smb/smb2.rs | 49 +++++++++++++---- rust/src/smb/smb2_ioctl.rs | 19 ++++--- rust/src/smb/smb2_session.rs | 7 ++- 9 files changed, 192 insertions(+), 112 deletions(-) diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs index 5cb1adeba2..0f0b4c2e51 100644 --- a/rust/src/smb/dcerpc.rs +++ b/rust/src/smb/dcerpc.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Open Information Security Foundation +/* Copyright (C) 2017-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -124,9 +124,9 @@ impl SMBTransactionDCERPC { impl SMBState { fn new_dcerpc_tx(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.hdr = hdr; tx.vercmd = vercmd; tx.type_data = Some(SMBTransactionTypeData::DCERPC( @@ -134,14 +134,13 @@ impl SMBState { SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } fn new_dcerpc_tx_for_response(&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, call_id: u32) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.hdr = hdr; tx.vercmd = vercmd; tx.type_data = Some(SMBTransactionTypeData::DCERPC( @@ -149,8 +148,7 @@ impl SMBState { SCLogDebug!("SMB: TX DCERPC created: ID {} hdr {:?}", tx.id, tx.hdr); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } fn get_dcerpc_tx(&mut self, hdr: &SMBCommonHdr, vercmd: &SMBVerCmdStat, call_id: u32) @@ -237,7 +235,10 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, } } - let tx = state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id); + let tx = match state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id) { + Some(tx) => tx, + None => return false, + }; match dcer.packet_type { DCERPC_TYPE_REQUEST => { match parse_dcerpc_request_record(dcer.data, dcer.frag_len, dcer.little_endian) { @@ -514,7 +515,11 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState, }; if !found { // pick up DCERPC tx even if we missed the request - let tx = state.new_dcerpc_tx_for_response(hdr, vercmd.clone(), dcer.call_id); + let tx = match state.new_dcerpc_tx_for_response( + hdr, vercmd.clone(), dcer.call_id) { + Some(tx) => tx, + None => return false, + }; dcerpc_response_handle(tx, vercmd, &dcer); } }, diff --git a/rust/src/smb/files.rs b/rust/src/smb/files.rs index ef9e620bb8..1f11825866 100644 --- a/rust/src/smb/files.rs +++ b/rust/src/smb/files.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2022 Open Information Security Foundation +/* Copyright (C) 2018-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -85,9 +85,9 @@ fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) impl SMBState { pub fn new_file_tx(&mut self, fuid: &[u8], file_name: &[u8], direction: Direction) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.type_data = Some(SMBTransactionTypeData::FILE(SMBTransactionFile::new())); if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { d.direction = direction; @@ -102,8 +102,7 @@ impl SMBState { SCLogDebug!("SMB: new_file_tx: TX FILE created: ID {} NAME {}", tx.id, String::from_utf8_lossy(file_name)); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } /// get file tx for a open file. Returns None if a file for the fuid exists, diff --git a/rust/src/smb/session.rs b/rust/src/smb/session.rs index 241973fc2b..113599075a 100644 --- a/rust/src/smb/session.rs +++ b/rust/src/smb/session.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Open Information Security Foundation +/* Copyright (C) 2018-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -36,9 +36,9 @@ impl SMBTransactionSessionSetup { impl SMBState { pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.hdr = hdr; tx.type_data = Some(SMBTransactionTypeData::SESSIONSETUP( @@ -48,8 +48,7 @@ impl SMBState { SCLogDebug!("SMB: TX SESSIONSETUP created: ID {}", tx.id); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } pub fn get_sessionsetup_tx(&mut self, hdr: SMBCommonHdr) diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 0a0fc10824..1494b8147c 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2022 Open Information Security Foundation +/* Copyright (C) 2017-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -330,9 +330,9 @@ impl SMBTransactionSetFilePathInfo { impl SMBState { pub fn new_setfileinfo_tx(&mut self, filename: Vec, fid: Vec, subcmd: u16, loi: u16, delete_on_close: bool) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO( SMBTransactionSetFilePathInfo::new( @@ -342,15 +342,14 @@ impl SMBState { SCLogDebug!("SMB: TX SETFILEPATHINFO created: ID {}", tx.id); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } pub fn new_setpathinfo_tx(&mut self, filename: Vec, subcmd: u16, loi: u16, delete_on_close: bool) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; let fid : Vec = Vec::new(); tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO( @@ -361,8 +360,7 @@ impl SMBState { SCLogDebug!("SMB: TX SETFILEPATHINFO created: ID {}", tx.id); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } } @@ -383,9 +381,9 @@ impl SMBTransactionRename { impl SMBState { pub fn new_rename_tx(&mut self, fuid: Vec, oldname: Vec, newname: Vec) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.type_data = Some(SMBTransactionTypeData::RENAME( SMBTransactionRename::new(fuid, oldname, newname))); @@ -394,8 +392,7 @@ impl SMBState { SCLogDebug!("SMB: TX RENAME created: ID {}", tx.id); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } } @@ -726,7 +723,6 @@ pub struct SMBState<> { /// transactions list pub transactions: VecDeque, - tx_index_completed: usize, /// tx counter for assigning incrementing id's to tx's tx_id: u64, @@ -783,9 +779,8 @@ impl SMBState { check_post_gap_file_txs: false, post_gap_files_checked: false, transactions: VecDeque::new(), - tx_index_completed: 0, - tx_id:0, - dialect:0, + tx_id: 0, + dialect: 0, dialect_vec: None, dcerpc_ifaces: None, ts: 0, @@ -798,28 +793,21 @@ impl SMBState { self._debug_tx_stats(); } - pub fn new_tx(&mut self) -> SMBTransaction { + pub fn new_tx(&mut self) -> Option { + if self.transactions.len() >= unsafe { SMB_MAX_TX } { + // Refuse to create a transaction once the list is at the limit. This + // bounds the number of transactions a single input can create, no + // matter the path (compound records, dcerpc, ...). Callers stop + // using the transaction and the parser puts the flow into an error + // state (see issue 8629). + self.set_event(SMBEvent::TooManyTransactions); + return None; + } let mut tx = SMBTransaction::new(); self.tx_id += 1; tx.id = self.tx_id; SCLogDebug!("TX {} created", tx.id); - if self.transactions.len() > unsafe { SMB_MAX_TX } { - let mut index = self.tx_index_completed; - for tx_old in &mut self.transactions.range_mut(self.tx_index_completed..) { - index += 1; - if !tx_old.request_done || !tx_old.response_done { - tx_old.tx_data.updated_tc = true; - tx_old.tx_data.updated_ts = true; - tx_old.request_done = true; - tx_old.response_done = true; - tx_old.set_event(SMBEvent::TooManyTransactions); - break; - } - } - self.tx_index_completed = index; - - } - return tx; + Some(tx) } pub fn free_tx(&mut self, tx_id: u64) { @@ -839,7 +827,6 @@ impl SMBState { if found { SCLogDebug!("freeing TX with ID {} TX.ID {} at index {} left: {} max id: {}", tx_id, tx_id+1, index, self.transactions.len(), self.tx_id); - self.tx_index_completed = 0; self.transactions.remove(index); } } @@ -888,9 +875,9 @@ impl SMBState { * track a single cmd request/reply pair. */ pub fn new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; if smb_ver == 1 && smb_cmd <= 255 { tx.vercmd.set_smb1_cmd(smb_cmd as u8); } else if smb_ver == 2 { @@ -905,8 +892,7 @@ impl SMBState { SCLogDebug!("SMB: TX GENERIC created: ID {} tx list {} {:?}", tx.id, self.transactions.len(), &tx); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } pub fn get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16) @@ -963,9 +949,9 @@ impl SMBState { } pub fn new_negotiate_tx(&mut self, smb_ver: u8) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; if smb_ver == 1 { tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL); } else if smb_ver == 2 { @@ -979,8 +965,7 @@ impl SMBState { SCLogDebug!("SMB: TX NEGOTIATE created: ID {} SMB ver {}", tx.id, smb_ver); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } pub fn get_negotiate_tx(&mut self, smb_ver: u8) @@ -1003,9 +988,9 @@ impl SMBState { } pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.hdr = hdr; tx.type_data = Some(SMBTransactionTypeData::TREECONNECT( @@ -1016,8 +1001,7 @@ impl SMBState { SCLogDebug!("SMB: TX TREECONNECT created: ID {} NAME {}", tx.id, String::from_utf8_lossy(&name)); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } pub fn get_treeconnect_tx(&mut self, hdr: SMBCommonHdr) @@ -1040,9 +1024,9 @@ impl SMBState { pub fn new_create_tx(&mut self, file_name: &[u8], disposition: u32, del: bool, dir: bool, hdr: SMBCommonHdr) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.hdr = hdr; tx.type_data = Some(SMBTransactionTypeData::CREATE( SMBTransactionCreate::new( @@ -1052,8 +1036,7 @@ impl SMBState { tx.response_done = self.tc_trunc; // no response expected if tc is truncated self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool) @@ -1347,6 +1330,13 @@ impl SMBState { /// Parsing function, handling TCP chunks fragmentation pub fn parse_tcp_data_ts(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult { + // The transaction list is full: new_tx() is refusing to create more, so + // put the flow into an error state and stop processing it (see issue + // 8629). + if self.transactions.len() >= unsafe { SMB_MAX_TX } { + self.set_event(SMBEvent::TooManyTransactions); + return AppLayerResult::err(); + } let mut cur_i = stream_slice.as_slice(); let consumed = self.handle_skip(Direction::ToServer, cur_i.len() as u32); if consumed > 0 { @@ -1684,6 +1674,13 @@ impl SMBState { /// Parsing function, handling TCP chunks fragmentation pub fn parse_tcp_data_tc(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult { + // The transaction list is full: new_tx() is refusing to create more, so + // put the flow into an error state and stop processing it (see issue + // 8629). + if self.transactions.len() >= unsafe { SMB_MAX_TX } { + self.set_event(SMBEvent::TooManyTransactions); + return AppLayerResult::err(); + } let mut cur_i = stream_slice.as_slice(); let consumed = self.handle_skip(Direction::ToClient, cur_i.len() as u32); if consumed > 0 { diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index a3a45ac64f..a1c5422360 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018-2022 Open Information Security Foundation +/* Copyright (C) 2018-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -198,7 +198,10 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let mut oldname = rd.oldname; oldname.retain(|&i|i != 0x00); - let tx = state.new_rename_tx(Vec::new(), oldname, newname); + let tx = match state.new_rename_tx(Vec::new(), oldname, newname) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb1_cmd(SMB1_COMMAND_RENAME); @@ -227,8 +230,11 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp); let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_setpathinfo_tx(pd.oldname, - rd.subcmd, pd.loi, disp.delete); + let tx = match state.new_setpathinfo_tx(pd.oldname, + rd.subcmd, pd.loi, disp.delete) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); @@ -257,7 +263,11 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let fid : Vec = Vec::new(); - let tx = state.new_rename_tx(fid, pd.oldname, newname); + let tx = match state.new_rename_tx( + fid, pd.oldname, newname) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); @@ -310,8 +320,11 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and Some(n) => n.to_vec(), None => b"".to_vec(), }; - let tx = state.new_setfileinfo_tx(filename, pd.fid.to_vec(), - rd.subcmd, pd.loi, disp.delete); + let tx = match state.new_setfileinfo_tx(filename, pd.fid.to_vec(), + rd.subcmd, pd.loi, disp.delete) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); @@ -345,7 +358,10 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and Some(n) => n.to_vec(), None => b"".to_vec(), }; - let tx = state.new_rename_tx(pd.fid.to_vec(), oldname, newname); + let tx = match state.new_rename_tx(pd.fid.to_vec(), oldname, newname) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2); @@ -452,7 +468,10 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and None => { false }, }; if !found { - let tx = state.new_negotiate_tx(1); + let tx = match state.new_negotiate_tx(1) { + Some(tx) => tx, + None => return, + }; if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { tdn.dialects = dialects; } @@ -482,8 +501,11 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and state.ssn2vec_map.insert(name_key, name_val); let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_create_tx(&cr.file_name, - cr.disposition, del, dir, tx_hdr); + let tx = match state.new_create_tx(&cr.file_name, + cr.disposition, del, dir, tx_hdr) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb1_cmd(command); SCLogDebug!("TS CREATE TX {} created", tx.id); true @@ -511,7 +533,10 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and // store hdr as SMBHDR_TYPE_TREE, so with tree id 0 // when the response finds this we update it - let tx = state.new_treeconnect_tx(name_key, name_val); + let tx = match state.new_treeconnect_tx(name_key, name_val) { + Some(tx) => tx, + None => return, + }; if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { tdn.req_service = Some(tr.service.to_vec()); } @@ -571,7 +596,10 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and }; if !have_tx && smb1_create_new_tx(command) { let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_generic_tx(1, command as u16, tx_key); + let tx = match state.new_generic_tx(1, command as u16, tx_key) { + Some(tx) => tx, + None => return, + }; SCLogDebug!("tx {} created for {}/{}", tx.id, command, &smb1_command_string(command)); tx.set_events(events); if no_response_expected { @@ -984,7 +1012,11 @@ pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offse let vercmd = SMBVerCmdStat::new1_with_ntstatus(command, r.nt_status); smb_write_dcerpc_record(state, vercmd, hdr, rd.data); } else { - let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToServer); + let tx = match state.new_file_tx( + &file_fid, &file_name, Direction::ToServer) { + Some(tx) => tx, + None => return, + }; if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { let file_id : u32 = tx.id as u32; if rd.offset < tdf.file_tracker.tracked { @@ -1073,7 +1105,11 @@ pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offse None => { false }, }; if !found { - let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToClient); + let tx = match state.new_file_tx( + &file_fid, &file_name, Direction::ToClient) { + Some(tx) => tx, + None => return, + }; if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { let file_id : u32 = tx.id as u32; SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id); @@ -1119,7 +1155,10 @@ pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offse fn smb1_request_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec) { if smb1_create_new_tx(r.command) || !events.is_empty() { let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_generic_tx(1, r.command as u16, tx_key); + let tx = match state.new_generic_tx(1, r.command as u16, tx_key) { + Some(tx) => tx, + None => return, + }; tx.set_events(events); } } @@ -1138,7 +1177,10 @@ fn smb1_response_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec return; } if !events.is_empty() { - let tx = state.new_generic_tx(1, r.command as u16, tx_key); + let tx = match state.new_generic_tx(1, r.command as u16, tx_key) { + Some(tx) => tx, + None => return, + }; tx.request_done = true; tx.response_done = true; SCLogDebug!("tx {} cmd {} is done", tx.id, r.command); diff --git a/rust/src/smb/smb1_session.rs b/rust/src/smb/smb1_session.rs index 80f23c8a64..f87799b975 100644 --- a/rust/src/smb/smb1_session.rs +++ b/rust/src/smb/smb1_session.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Open Information Security Foundation +/* Copyright (C) 2018-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -131,7 +131,10 @@ pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord, andx_offs Ok((rem, setup)) => { let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, r.ssn_id as u64, 0, r.multiplex_id as u64); - let tx = state.new_sessionsetup_tx(hdr); + let tx = match state.new_sessionsetup_tx(hdr) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb1_cmd(r.command); if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data { diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 98ed30da1c..76db5933cd 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2017-2022 Open Information Security Foundation +/* Copyright (C) 2017-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -234,7 +234,10 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema None => { b"".to_vec() } }; - let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToClient); + let tx = match state.new_file_tx(&file_guid, &file_name, Direction::ToClient) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ); tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, r.session_id, r.tree_id, 0); // TODO move into new_file_tx @@ -279,7 +282,10 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema SCLogDebug!("SMBv2/WRITE: request record"); if smb2_create_new_tx(r.command) { let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_generic_tx(2, r.command, tx_key); + let tx = match state.new_generic_tx(2, r.command, tx_key) { + Some(tx) => tx, + None => return, + }; tx.request_done = true; } match parse_smb2_request_write(r.data) { @@ -374,7 +380,10 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema SCLogDebug!("non-DCERPC pipe: skip rest of the record"); state.set_skip(Direction::ToServer, nbss_remaining); } else { - let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToServer); + let tx = match state.new_file_tx(&file_guid, &file_name, Direction::ToServer) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE); tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, r.session_id, r.tree_id, 0); // TODO move into new_file_tx @@ -433,7 +442,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) Some(n) => { n.to_vec() }, None => { b"".to_vec() }, }; - let tx = state.new_rename_tx(rd.guid.to_vec(), oldname, newname); + let tx = match state.new_rename_tx(rd.guid.to_vec(), oldname, newname) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO); @@ -457,7 +469,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) } }, }; - let tx = state.new_setfileinfo_tx(fname, rd.guid.to_vec(), rd.class as u16, rd.infolvl as u16, dis.delete); + let tx = match state.new_setfileinfo_tx(fname, rd.guid.to_vec(), rd.class as u16, rd.infolvl as u16, dis.delete) { + Some(tx) => tx, + None => return, + }; tx.hdr = tx_hdr; tx.request_done = true; tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO); @@ -507,7 +522,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) None => { false }, }; if !found { - let tx = state.new_negotiate_tx(2); + let tx = match state.new_negotiate_tx(2) { + Some(tx) => tx, + None => return, + }; if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { tdn.dialects2 = dialects; tdn.client_guid = Some(rd.client_guid.to_vec()); @@ -536,7 +554,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) name_val = name_val[1..].to_vec(); } - let tx = state.new_treeconnect_tx(name_key, name_val); + let tx = match state.new_treeconnect_tx(name_key, name_val) { + Some(tx) => tx, + None => return, + }; tx.request_done = true; tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT); true @@ -581,8 +602,11 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) state.ssn2vec_map.insert(name_key, cr.data.to_vec()); let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_create_tx(cr.data, - cr.disposition, del, dir, tx_hdr); + let tx = match state.new_create_tx(cr.data, + cr.disposition, del, dir, tx_hdr) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb2_cmd(r.command); SCLogDebug!("TS CREATE TX {} created", tx.id); true @@ -645,7 +669,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) /* if we don't have a tx, create it here (maybe) */ if !have_tx && smb2_create_new_tx(r.command) { let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_generic_tx(2, r.command, tx_key); + let tx = match state.new_generic_tx(2, r.command, tx_key) { + Some(tx) => tx, + None => return, + }; SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}", tx.id, r.command, r.session_id, r.tree_id, r.message_id); tx.set_events(events); diff --git a/rust/src/smb/smb2_ioctl.rs b/rust/src/smb/smb2_ioctl.rs index ff85a5fefb..06f4f55c3d 100644 --- a/rust/src/smb/smb2_ioctl.rs +++ b/rust/src/smb/smb2_ioctl.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Open Information Security Foundation +/* Copyright (C) 2018-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -39,9 +39,9 @@ impl SMBTransactionIoctl { impl SMBState { pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32) - -> &mut SMBTransaction + -> Option<&mut SMBTransaction> { - let mut tx = self.new_tx(); + let mut tx = self.new_tx()?; tx.hdr = hdr; tx.type_data = Some(SMBTransactionTypeData::IOCTL( SMBTransactionIoctl::new(func))); @@ -51,8 +51,7 @@ impl SMBState { SCLogDebug!("SMB: TX IOCTL created: ID {} FUNC {:08x}: {}", tx.id, func, &fsctl_func_to_string(func)); self.transactions.push_back(tx); - let tx_ref = self.transactions.back_mut(); - return tx_ref.unwrap(); + self.transactions.back_mut() } } @@ -74,12 +73,18 @@ pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record) smb_write_dcerpc_record(state, vercmd, hdr, rd.data); } else { SCLogDebug!("IOCTL {:08x} {}", rd.function, &fsctl_func_to_string(rd.function)); - let tx = state.new_ioctl_tx(hdr, rd.function); + let tx = match state.new_ioctl_tx(hdr, rd.function) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL); } }, _ => { - let tx = state.new_generic_tx(2, r.command, hdr); + let tx = match state.new_generic_tx(2, r.command, hdr) { + Some(tx) => tx, + None => return, + }; tx.set_event(SMBEvent::MalformedData); }, }; diff --git a/rust/src/smb/smb2_session.rs b/rust/src/smb/smb2_session.rs index 31a34165e4..2760fb99e8 100644 --- a/rust/src/smb/smb2_session.rs +++ b/rust/src/smb/smb2_session.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Open Information Security Foundation +/* Copyright (C) 2018-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -27,7 +27,10 @@ pub fn smb2_session_setup_request(state: &mut SMBState, r: &Smb2Record) match parse_smb2_request_session_setup(r.data) { Ok((_, setup)) => { let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); - let tx = state.new_sessionsetup_tx(hdr); + let tx = match state.new_sessionsetup_tx(hdr) { + Some(tx) => tx, + None => return, + }; tx.vercmd.set_smb2_cmd(r.command); if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {