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
pull/15814/head
Jeff Lucovsky 1 week ago committed by Victor Julien
parent 392b6aee29
commit c8f68b0e4f

@ -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
@ -126,8 +126,8 @@ impl SMBTransactionDCERPC {
impl SMBState {
fn new_dcerpc_tx(
&mut self, hdr: SMBCommonHdr, vercmd: SMBVerCmdStat, cmd: u8, call_id: u32,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.vercmd = vercmd;
tx.type_data = Some(SMBTransactionTypeData::DCERPC(
@ -136,14 +136,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 {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.vercmd = vercmd;
tx.type_data = Some(SMBTransactionTypeData::DCERPC(
@ -152,8 +151,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(
@ -251,7 +249,9 @@ pub fn smb_write_dcerpc_record(
}
}
let tx = state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id);
let Some(tx) = state.new_dcerpc_tx(hdr, vercmd, dcer.packet_type, dcer.call_id) else {
return false;
};
match dcer.packet_type {
DCERPC_TYPE_REQUEST => {
match parse_dcerpc_request_record(dcer.data, dcer.frag_len, dcer.little_endian)
@ -556,7 +556,11 @@ pub fn smb_read_dcerpc_record(
};
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 Some(tx) =
state.new_dcerpc_tx_for_response(hdr, vercmd.clone(), dcer.call_id)
else {
return false;
};
dcerpc_response_handle(tx, vercmd, &dcer);
}
}

@ -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
@ -89,8 +89,8 @@ 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 {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
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;
@ -112,8 +112,7 @@ impl SMBState {
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,

@ -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,8 +36,8 @@ impl SMBTransactionSessionSetup {
}
impl SMBState {
pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr) -> &mut SMBTransaction {
let mut tx = self.new_tx();
pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr) -> Option<&mut SMBTransaction> {
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) -> Option<&mut SMBTransaction> {

@ -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
@ -358,8 +358,8 @@ impl SMBTransactionSetFilePathInfo {
impl SMBState {
pub fn new_setfileinfo_tx(
&mut self, filename: Vec<u8>, fid: Vec<u8>, subcmd: u16, loi: u16, delete_on_close: bool,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO(
SMBTransactionSetFilePathInfo::new(filename, fid, subcmd, loi, delete_on_close),
@ -369,14 +369,13 @@ 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<u8>, subcmd: u16, loi: u16, delete_on_close: bool,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
let fid: Vec<u8> = Vec::new();
tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO(
@ -387,8 +386,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()
}
}
@ -412,8 +410,8 @@ impl SMBTransactionRename {
impl SMBState {
pub fn new_rename_tx(
&mut self, fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.type_data = Some(SMBTransactionTypeData::RENAME(SMBTransactionRename::new(
fuid, oldname, newname,
@ -423,8 +421,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()
}
}
@ -745,7 +742,6 @@ pub struct SMBState {
/// transactions list
pub transactions: VecDeque<SMBTransaction>,
tx_index_completed: usize,
/// tx counter for assigning incrementing id's to tx's
tx_id: u64,
@ -818,7 +814,6 @@ 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,
dialect_vec: None,
@ -834,27 +829,21 @@ impl SMBState {
self._debug_tx_stats();
}
pub fn new_tx(&mut self) -> SMBTransaction {
pub fn new_tx(&mut self) -> Option<SMBTransaction> {
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.0.updated_tc = true;
tx_old.tx_data.0.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) {
@ -885,7 +874,6 @@ impl SMBState {
self.transactions.len(),
self.tx_id
);
self.tx_index_completed = 0;
self.transactions.remove(index);
}
}
@ -942,8 +930,8 @@ impl SMBState {
pub fn new_generic_tx(
&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
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 {
@ -962,8 +950,7 @@ impl SMBState {
&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) -> Option<&mut SMBTransaction> {
@ -1017,8 +1004,8 @@ impl SMBState {
return None;
}
pub fn new_negotiate_tx(&mut self, smb_ver: u8) -> &mut SMBTransaction {
let mut tx = self.new_tx();
pub fn new_negotiate_tx(&mut self, smb_ver: u8) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
if smb_ver == 1 {
tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL);
} else if smb_ver == 2 {
@ -1037,8 +1024,7 @@ impl SMBState {
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) -> Option<&mut SMBTransaction> {
@ -1056,8 +1042,10 @@ impl SMBState {
return None;
}
pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>) -> &mut SMBTransaction {
let mut tx = self.new_tx();
pub fn new_treeconnect_tx(
&mut self, hdr: SMBCommonHdr, name: Vec<u8>,
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.type_data = Some(SMBTransactionTypeData::TREECONNECT(
@ -1072,8 +1060,7 @@ impl SMBState {
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) -> Option<&mut SMBTransaction> {
@ -1094,8 +1081,8 @@ impl SMBState {
pub fn new_create_tx(
&mut self, file_name: &[u8], disposition: u32, del: bool, dir: bool, hdr: SMBCommonHdr,
) -> &mut SMBTransaction {
let mut tx = self.new_tx();
) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.type_data = Some(SMBTransactionTypeData::CREATE(SMBTransactionCreate::new(
file_name.to_vec(),
@ -1107,8 +1094,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(&mut self, guid: &[u8]) -> (&'static str, bool) {
@ -1551,6 +1537,13 @@ impl SMBState {
pub fn parse_tcp_data_ts(
&mut self, flow: *mut 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 {
@ -2105,6 +2098,13 @@ impl SMBState {
pub fn parse_tcp_data_tc(
&mut self, flow: *mut 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 {

@ -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,9 @@ fn smb1_request_record_one(
let mut oldname = rd.oldname;
oldname.retain(|&i| i != 0x00);
let tx = state.new_rename_tx(Vec::new(), oldname, newname);
let Some(tx) = state.new_rename_tx(Vec::new(), oldname, newname) else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb1_cmd(SMB1_COMMAND_RENAME);
@ -233,12 +235,14 @@ fn smb1_request_record_one(
let tx_hdr =
SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
let tx = state.new_setpathinfo_tx(
let Some(tx) = state.new_setpathinfo_tx(
pd.oldname,
rd.subcmd,
pd.loi,
disp.delete,
);
) else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
@ -274,7 +278,11 @@ fn smb1_request_record_one(
let fid: Vec<u8> = Vec::new();
let tx = state.new_rename_tx(fid, pd.oldname, newname);
let Some(tx) =
state.new_rename_tx(fid, pd.oldname, newname)
else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
@ -339,13 +347,15 @@ fn smb1_request_record_one(
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let tx = state.new_setfileinfo_tx(
let Some(tx) = state.new_setfileinfo_tx(
filename,
pd.fid.to_vec(),
rd.subcmd,
pd.loi,
disp.delete,
);
) else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
@ -387,11 +397,13 @@ fn smb1_request_record_one(
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let tx = state.new_rename_tx(
let Some(tx) = state.new_rename_tx(
pd.fid.to_vec(),
oldname,
newname,
);
) else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
@ -498,7 +510,9 @@ fn smb1_request_record_one(
None => false,
};
if !found {
let tx = state.new_negotiate_tx(1);
let Some(tx) = state.new_negotiate_tx(1) else {
return;
};
if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
tdn.dialects = dialects;
}
@ -530,7 +544,11 @@ fn smb1_request_record_one(
state.ssn2vec_cache.put(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 Some(tx) =
state.new_create_tx(&cr.file_name, cr.disposition, del, dir, tx_hdr)
else {
return;
};
tx.vercmd.set_smb1_cmd(command);
SCLogDebug!("TS CREATE TX {} created", tx.id);
true
@ -559,7 +577,9 @@ fn smb1_request_record_one(
// 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 Some(tx) = state.new_treeconnect_tx(name_key, name_val) else {
return;
};
if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
tdn.req_service = Some(tr.service.to_vec());
}
@ -625,7 +645,9 @@ fn smb1_request_record_one(
};
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 Some(tx) = state.new_generic_tx(1, command as u16, tx_key) else {
return;
};
SCLogDebug!(
"tx {} created for {}/{}",
tx.id,
@ -1083,7 +1105,10 @@ pub fn smb1_write_request_record(
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 Some(tx) = state.new_file_tx(&file_fid, &file_name, Direction::ToServer)
else {
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 {
@ -1196,7 +1221,11 @@ pub fn smb1_read_response_record(
None => false,
};
if !found {
let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToClient);
let Some(tx) =
state.new_file_tx(&file_fid, &file_name, Direction::ToClient)
else {
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);
@ -1257,7 +1286,9 @@ pub fn smb1_read_response_record(
fn smb1_request_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec<SMBEvent>) {
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 Some(tx) = state.new_generic_tx(1, r.command as u16, tx_key) else {
return;
};
tx.set_events(events);
}
}
@ -1276,7 +1307,9 @@ 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 Some(tx) = state.new_generic_tx(1, r.command as u16, tx_key) else {
return;
};
tx.request_done = true;
tx.response_done = true;
SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);

@ -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
@ -71,7 +71,9 @@ pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord, andx_offs
0,
r.multiplex_id as u64,
);
let tx = state.new_sessionsetup_tx(hdr);
let Some(tx) = state.new_sessionsetup_tx(hdr) else {
return;
};
tx.vercmd.set_smb1_cmd(r.command);
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {

@ -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
@ -252,7 +252,10 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema
None => b"<unknown>".to_vec(),
};
let tx = state.new_file_tx(&file_guid, &file_name, Direction::ToClient);
let Some(tx) = state.new_file_tx(&file_guid, &file_name, Direction::ToClient)
else {
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
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
@ -311,7 +314,9 @@ 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 Some(tx) = state.new_generic_tx(2, r.command, tx_key) else {
return;
};
tx.request_done = true;
}
match parse_smb2_request_write(r.data) {
@ -426,7 +431,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 Some(tx) = state.new_file_tx(&file_guid, &file_name, Direction::ToServer)
else {
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
if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
@ -503,7 +511,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) {
Some(n) => n.to_vec(),
None => b"<unknown>".to_vec(),
};
let tx = state.new_rename_tx(rd.guid.to_vec(), oldname, newname);
let Some(tx) = state.new_rename_tx(rd.guid.to_vec(), oldname, newname)
else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO);
@ -528,13 +539,15 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) {
}
}
};
let tx = state.new_setfileinfo_tx(
let Some(tx) = state.new_setfileinfo_tx(
fname,
rd.guid.to_vec(),
rd.class as u16,
rd.infolvl as u16,
dis.delete,
);
) else {
return;
};
tx.hdr = tx_hdr;
tx.request_done = true;
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO);
@ -575,7 +588,9 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) {
}
if state.get_negotiate_tx(2).is_none() {
let tx = state.new_negotiate_tx(2);
let Some(tx) = state.new_negotiate_tx(2) else {
return;
};
if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
tdn.dialects2 = dialects;
tdn.client_guid = Some(rd.client_guid.to_vec());
@ -601,7 +616,9 @@ 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 Some(tx) = state.new_treeconnect_tx(name_key, name_val) else {
return;
};
tx.request_done = true;
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT);
true
@ -644,7 +661,10 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) {
state.ssn2vec_cache.put(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 Some(tx) = state.new_create_tx(cr.data, cr.disposition, del, dir, tx_hdr)
else {
return;
};
tx.vercmd.set_smb2_cmd(r.command);
SCLogDebug!("TS CREATE TX {} created", tx.id);
true
@ -702,7 +722,9 @@ 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 Some(tx) = state.new_generic_tx(2, r.command, tx_key) else {
return;
};
SCLogDebug!(
"TS TX {} command {} created with session_id {} tree_id {} message_id {}",
tx.id,

@ -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,8 +36,8 @@ impl SMBTransactionIoctl {
}
impl SMBState {
pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32) -> &mut SMBTransaction {
let mut tx = self.new_tx();
pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32) -> Option<&mut SMBTransaction> {
let mut tx = self.new_tx()?;
tx.hdr = hdr;
tx.type_data = Some(SMBTransactionTypeData::IOCTL(SMBTransactionIoctl::new(
func,
@ -52,8 +52,7 @@ impl SMBState {
&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()
}
}
@ -78,12 +77,16 @@ pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record) {
rd.function,
&fsctl_func_to_string(rd.function)
);
let tx = state.new_ioctl_tx(hdr, rd.function);
let Some(tx) = state.new_ioctl_tx(hdr, rd.function) else {
return;
};
tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL);
}
}
_ => {
let tx = state.new_generic_tx(2, r.command, hdr);
let Some(tx) = state.new_generic_tx(2, r.command, hdr) else {
return;
};
tx.set_event(SMBEvent::MalformedData);
}
};

@ -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
@ -26,7 +26,9 @@ 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 Some(tx) = state.new_sessionsetup_tx(hdr) else {
return;
};
tx.vercmd.set_smb2_cmd(r.command);
if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {

Loading…
Cancel
Save