diff options
Diffstat (limited to 'rust/src/smb/files.rs')
-rw-r--r-- | rust/src/smb/files.rs | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/rust/src/smb/files.rs b/rust/src/smb/files.rs new file mode 100644 index 0000000..b290357 --- /dev/null +++ b/rust/src/smb/files.rs @@ -0,0 +1,244 @@ +/* Copyright (C) 2018-2022 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use std; +use crate::core::*; +use crate::filetracker::*; +use crate::filecontainer::*; + +use crate::smb::smb::*; + +/// File tracking transaction. Single direction only. +#[derive(Default, Debug)] +pub struct SMBTransactionFile { + pub direction: Direction, + pub fuid: Vec<u8>, + pub file_name: Vec<u8>, + pub share_name: Vec<u8>, + pub file_tracker: FileTransferTracker, + /// after a gap, this will be set to a time in the future. If the file + /// receives no updates before that, it will be considered complete. + pub post_gap_ts: u64, + //pub files: Files, +} + +impl SMBTransactionFile { + pub fn new() -> Self { + return Self { + file_tracker: FileTransferTracker::new(), + ..Default::default() + } + } + + pub fn update_file_flags(&mut self, flow_file_flags: u16) { + let dir_flag = if self.direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; + self.file_tracker.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, dir_flag) }; + } +} + +/// little wrapper around the FileTransferTracker::new_chunk method +pub fn filetracker_newchunk(ft: &mut FileTransferTracker, name: &[u8], data: &[u8], + chunk_offset: u64, chunk_size: u32, is_last: bool, xid: &u32) +{ + if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { + ft.new_chunk(sfcm, name, data, chunk_offset, + chunk_size, 0, is_last, xid); + } +} + +pub fn filetracker_trunc(ft: &mut FileTransferTracker) +{ + if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { + ft.trunc(sfcm); + } +} + +pub fn filetracker_close(ft: &mut FileTransferTracker) +{ + if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { + ft.close(sfcm); + } +} + +fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32 +{ + if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { + ft.update(sfcm, data, gap_size) + } else { + 0 + } +} + +impl SMBState { + pub fn new_file_tx(&mut self, fuid: &[u8], file_name: &[u8], direction: Direction) + -> &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; + d.fuid = fuid.to_vec(); + d.file_name = file_name.to_vec(); + d.file_tracker.tx_id = tx.id - 1; + tx.tx_data.update_file_flags(self.state_data.file_flags); + d.update_file_flags(tx.tx_data.file_flags); + } + tx.tx_data.init_files_opened(); + tx.tx_data.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func? + 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(); + } + + /// get file tx for a open file. Returns None if a file for the fuid exists, + /// but has already been closed. + pub fn get_file_tx_by_fuid_with_open_file(&mut self, fuid: &[u8], direction: Direction) + -> Option<&mut SMBTransaction> + { + let f = fuid.to_vec(); + for tx in &mut self.transactions { + let found = match tx.type_data { + Some(SMBTransactionTypeData::FILE(ref mut d)) => { + direction == d.direction && f == d.fuid && !d.file_tracker.is_done() + }, + _ => { false }, + }; + + if found { + SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); + if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { + tx.tx_data.update_file_flags(self.state_data.file_flags); + d.update_file_flags(tx.tx_data.file_flags); + } + return Some(tx); + } + } + SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); + return None; + } + + /// get file tx for a fuid. File may already have been closed. + pub fn get_file_tx_by_fuid(&mut self, fuid: &[u8], direction: Direction) + -> Option<&mut SMBTransaction> + { + let f = fuid.to_vec(); + for tx in &mut self.transactions { + let found = match tx.type_data { + Some(SMBTransactionTypeData::FILE(ref mut d)) => { + direction == d.direction && f == d.fuid + }, + _ => { false }, + }; + + if found { + SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); + if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { + tx.tx_data.update_file_flags(self.state_data.file_flags); + d.update_file_flags(tx.tx_data.file_flags); + } + return Some(tx); + } + } + SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); + return None; + } + + // update in progress chunks for file transfers + // return how much data we consumed + pub fn filetracker_update(&mut self, direction: Direction, data: &[u8], gap_size: u32) -> u32 { + let mut chunk_left = if direction == Direction::ToServer { + self.file_ts_left + } else { + self.file_tc_left + }; + if chunk_left == 0 { + return 0 + } + SCLogDebug!("chunk_left {} data {}", chunk_left, data.len()); + let file_handle = if direction == Direction::ToServer { + self.file_ts_guid.to_vec() + } else { + self.file_tc_guid.to_vec() + }; + + let data_to_handle_len = if chunk_left as usize >= data.len() { + data.len() + } else { + chunk_left as usize + }; + + if chunk_left <= data.len() as u32 { + chunk_left = 0; + } else { + chunk_left -= data.len() as u32; + } + + if direction == Direction::ToServer { + self.file_ts_left = chunk_left; + } else { + self.file_tc_left = chunk_left; + } + + let ssn_gap = self.ts_ssn_gap | self.tc_ssn_gap; + // get the tx and update it + let consumed = match self.get_file_tx_by_fuid(&file_handle, direction) { + Some(tx) => { + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + if ssn_gap { + let queued_data = tdf.file_tracker.get_queued_size(); + if queued_data > 2000000 { // TODO should probably be configurable + SCLogDebug!("QUEUED size {} while we've seen GAPs. Truncating file.", queued_data); + filetracker_trunc(&mut tdf.file_tracker); + } + } + + // reset timestamp if we get called after a gap + if tdf.post_gap_ts > 0 { + tdf.post_gap_ts = 0; + } + + let file_data = &data[0..data_to_handle_len]; + filetracker_update(&mut tdf.file_tracker, file_data, gap_size) + } else { + 0 + } + }, + None => { + SCLogDebug!("not found for handle {:?}", file_handle); + 0 }, + }; + + return consumed; + } +} + +use crate::applayer::AppLayerGetFileState; +#[no_mangle] +pub unsafe extern "C" fn rs_smb_gettxfiles(_state: *mut std::ffi::c_void, tx: *mut std::ffi::c_void, direction: u8) -> AppLayerGetFileState { + let tx = cast_pointer!(tx, SMBTransaction); + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + let tx_dir : u8 = tdf.direction.into(); + if direction & tx_dir != 0 { + if let Some(sfcm) = { SURICATA_SMB_FILE_CONFIG } { + return AppLayerGetFileState { fc: &mut tdf.file_tracker.file, cfg: sfcm.files_sbcfg } + } + } + } + AppLayerGetFileState::err() +} |