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 c8f68b0e4f)
pull/15826/head
Jeff Lucovsky 2 weeks ago committed by Philippe Antoine
parent 82af0aeb7c
commit 88adab8bd3

@ -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);
}
},

@ -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,

@ -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)

@ -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<u8>, fid: Vec<u8>,
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<u8>,
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<u8> = 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<u8>, oldname: Vec<u8>, newname: Vec<u8>)
-> &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<SMBTransaction>,
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<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.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<u8>)
-> &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 {

@ -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<u8> = 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"<unknown>".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"<unknown>".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<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 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);

@ -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 {

@ -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"<unknown>".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"<unknown>".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);

@ -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);
},
};

@ -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 {

Loading…
Cancel
Save