/src/suricata8/rust/src/smb/files.rs
Line | Count | Source |
1 | | /* Copyright (C) 2018-2026 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | use std; |
19 | | use crate::core::*; |
20 | | use crate::direction::Direction; |
21 | | use crate::filetracker::*; |
22 | | use crate::filecontainer::*; |
23 | | |
24 | | use crate::smb::smb::*; |
25 | | |
26 | | /// File tracking transaction. Single direction only. |
27 | | #[derive(Default, Debug)] |
28 | | pub struct SMBTransactionFile { |
29 | | pub direction: Direction, |
30 | | pub fuid: Vec<u8>, |
31 | | pub file_name: Vec<u8>, |
32 | | pub share_name: Vec<u8>, |
33 | | pub file_tracker: FileTransferTracker, |
34 | | /// after a gap, this will be set to a time in the future. If the file |
35 | | /// receives no updates before that, it will be considered complete. |
36 | | pub post_gap_ts: u64, |
37 | | //pub files: Files, |
38 | | } |
39 | | |
40 | | impl SMBTransactionFile { |
41 | 43.6k | pub fn new() -> Self { |
42 | 43.6k | return Self { |
43 | 43.6k | file_tracker: FileTransferTracker::new(), |
44 | 43.6k | ..Default::default() |
45 | 43.6k | } |
46 | 43.6k | } |
47 | | |
48 | 182k | pub fn update_file_flags(&mut self, flow_file_flags: u16) { |
49 | 182k | let dir_flag = if self.direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; |
50 | 182k | self.file_tracker.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, dir_flag) }; |
51 | 182k | } |
52 | | } |
53 | | |
54 | | /// little wrapper around the FileTransferTracker::new_chunk method |
55 | 65.9k | pub fn filetracker_newchunk(ft: &mut FileTransferTracker, name: &[u8], data: &[u8], |
56 | 65.9k | chunk_offset: u64, chunk_size: u32, is_last: bool, xid: &u32) |
57 | | { |
58 | 65.9k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
59 | 65.9k | ft.new_chunk(sfcm, name, data, chunk_offset, |
60 | 65.9k | chunk_size, 0, is_last, xid); |
61 | 65.9k | } |
62 | 65.9k | } |
63 | | |
64 | 0 | pub fn filetracker_trunc(ft: &mut FileTransferTracker) |
65 | | { |
66 | 0 | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
67 | 0 | ft.trunc(sfcm); |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | 35.6k | pub fn filetracker_close(ft: &mut FileTransferTracker) |
72 | | { |
73 | 35.6k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
74 | 35.6k | ft.close(sfcm); |
75 | 35.6k | } |
76 | 35.6k | } |
77 | | |
78 | 70.3k | fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32 |
79 | | { |
80 | 70.3k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
81 | 70.3k | ft.update(sfcm, data, gap_size) |
82 | | } else { |
83 | 0 | 0 |
84 | | } |
85 | 70.3k | } |
86 | | |
87 | | impl SMBState { |
88 | 43.7k | pub fn new_file_tx(&mut self, fuid: &[u8], file_name: &[u8], direction: Direction) |
89 | 43.7k | -> Option<&mut SMBTransaction> |
90 | | { |
91 | 43.7k | let mut tx = self.new_tx()?; |
92 | 43.6k | tx.type_data = Some(SMBTransactionTypeData::FILE(SMBTransactionFile::new())); |
93 | 43.6k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
94 | 43.6k | d.direction = direction; |
95 | 43.6k | d.fuid = fuid.to_vec(); |
96 | 43.6k | d.file_name = file_name.to_vec(); |
97 | 43.6k | d.file_tracker.tx_id = tx.id - 1; |
98 | 43.6k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
99 | 43.6k | d.update_file_flags(tx.tx_data.file_flags); |
100 | 43.6k | } |
101 | 43.6k | tx.tx_data.init_files_opened(); |
102 | 43.6k | tx.tx_data.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func? |
103 | | SCLogDebug!("SMB: new_file_tx: TX FILE created: ID {} NAME {}", |
104 | | tx.id, String::from_utf8_lossy(file_name)); |
105 | 43.6k | self.transactions.push_back(tx); |
106 | 43.6k | self.transactions.back_mut() |
107 | 43.7k | } |
108 | | |
109 | | /// get file tx for a open file. Returns None if a file for the fuid exists, |
110 | | /// but has already been closed. |
111 | 76.1k | pub fn get_file_tx_by_fuid_with_open_file(&mut self, fuid: &[u8], direction: Direction) |
112 | 76.1k | -> Option<&mut SMBTransaction> |
113 | | { |
114 | 76.1k | let f = fuid.to_vec(); |
115 | 11.8M | for tx in &mut self.transactions { |
116 | 11.7M | let found = match tx.type_data { |
117 | 88.6k | Some(SMBTransactionTypeData::FILE(ref mut d)) => { |
118 | 88.6k | direction == d.direction && f == d.fuid && !d.file_tracker.is_done() |
119 | | }, |
120 | 11.7M | _ => { false }, |
121 | | }; |
122 | | |
123 | 11.7M | if found { |
124 | | SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); |
125 | 22.3k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
126 | 22.3k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
127 | 22.3k | d.update_file_flags(tx.tx_data.file_flags); |
128 | 22.3k | } |
129 | 22.3k | tx.tx_data.updated_tc = true; |
130 | 22.3k | tx.tx_data.updated_ts = true; |
131 | 22.3k | return Some(tx); |
132 | 11.7M | } |
133 | | } |
134 | | SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); |
135 | 53.8k | return None; |
136 | 76.1k | } |
137 | | |
138 | | /// get file tx for a fuid. File may already have been closed. |
139 | 156k | pub fn get_file_tx_by_fuid(&mut self, fuid: &[u8], direction: Direction) |
140 | 156k | -> Option<&mut SMBTransaction> |
141 | | { |
142 | 156k | let f = fuid.to_vec(); |
143 | 12.0M | for tx in &mut self.transactions { |
144 | 11.9M | let found = match tx.type_data { |
145 | 172k | Some(SMBTransactionTypeData::FILE(ref mut d)) => { |
146 | 172k | direction == d.direction && f == d.fuid |
147 | | }, |
148 | 11.8M | _ => { false }, |
149 | | }; |
150 | | |
151 | 11.9M | if found { |
152 | | SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); |
153 | 116k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
154 | 116k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
155 | 116k | d.update_file_flags(tx.tx_data.file_flags); |
156 | 116k | } |
157 | 116k | tx.tx_data.updated_tc = true; |
158 | 116k | tx.tx_data.updated_ts = true; |
159 | 116k | return Some(tx); |
160 | 11.8M | } |
161 | | } |
162 | | SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); |
163 | 39.1k | return None; |
164 | 156k | } |
165 | | |
166 | | // update in progress chunks for file transfers |
167 | | // return how much data we consumed |
168 | 1.47M | pub fn filetracker_update(&mut self, direction: Direction, data: &[u8], gap_size: u32) -> u32 { |
169 | 1.47M | let mut chunk_left = if direction == Direction::ToServer { |
170 | 741k | self.file_ts_left |
171 | | } else { |
172 | 733k | self.file_tc_left |
173 | | }; |
174 | 1.47M | if chunk_left == 0 { |
175 | 1.37M | return 0 |
176 | 100k | } |
177 | | SCLogDebug!("chunk_left {} data {}", chunk_left, data.len()); |
178 | 100k | let file_handle = if direction == Direction::ToServer { |
179 | 66.4k | self.file_ts_guid.to_vec() |
180 | | } else { |
181 | 34.3k | self.file_tc_guid.to_vec() |
182 | | }; |
183 | | |
184 | 100k | let data_to_handle_len = if chunk_left as usize >= data.len() { |
185 | 100k | data.len() |
186 | | } else { |
187 | 453 | chunk_left as usize |
188 | | }; |
189 | | |
190 | 100k | if chunk_left <= data.len() as u32 { |
191 | 456 | chunk_left = 0; |
192 | 100k | } else { |
193 | 100k | chunk_left -= data.len() as u32; |
194 | 100k | } |
195 | | |
196 | 100k | if direction == Direction::ToServer { |
197 | 66.4k | self.file_ts_left = chunk_left; |
198 | 66.4k | } else { |
199 | 34.3k | self.file_tc_left = chunk_left; |
200 | 34.3k | } |
201 | | |
202 | 100k | let ssn_gap = self.ts_ssn_gap | self.tc_ssn_gap; |
203 | | // get the tx and update it |
204 | 100k | let consumed = match self.get_file_tx_by_fuid(&file_handle, direction) { |
205 | 70.3k | Some(tx) => { |
206 | 70.3k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
207 | 70.3k | if ssn_gap { |
208 | 134 | let queued_data = tdf.file_tracker.get_queued_size(); |
209 | 134 | if queued_data > 2000000 { // TODO should probably be configurable |
210 | 0 | SCLogDebug!("QUEUED size {} while we've seen GAPs. Truncating file.", queued_data); |
211 | 0 | filetracker_trunc(&mut tdf.file_tracker); |
212 | 134 | } |
213 | 70.2k | } |
214 | | |
215 | | // reset timestamp if we get called after a gap |
216 | 70.3k | if tdf.post_gap_ts > 0 { |
217 | 0 | tdf.post_gap_ts = 0; |
218 | 70.3k | } |
219 | | |
220 | 70.3k | let file_data = &data[0..data_to_handle_len]; |
221 | 70.3k | filetracker_update(&mut tdf.file_tracker, file_data, gap_size) |
222 | | } else { |
223 | 0 | 0 |
224 | | } |
225 | | }, |
226 | | None => { |
227 | | SCLogDebug!("not found for handle {:?}", file_handle); |
228 | 30.4k | 0 }, |
229 | | }; |
230 | | |
231 | 100k | return consumed; |
232 | 1.47M | } |
233 | | } |
234 | | |
235 | | use crate::applayer::AppLayerGetFileState; |
236 | | |
237 | 345k | pub(super) unsafe extern "C" fn smb_gettxfiles(tx: *mut std::ffi::c_void, direction: u8) -> AppLayerGetFileState { |
238 | 345k | let tx = cast_pointer!(tx, SMBTransaction); |
239 | 345k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
240 | 345k | let tx_dir : u8 = tdf.direction.into(); |
241 | 345k | if direction & tx_dir != 0 { |
242 | 342k | if let Some(sfcm) = { SURICATA_SMB_FILE_CONFIG } { |
243 | 342k | return AppLayerGetFileState { fc: &mut tdf.file_tracker.file, cfg: sfcm.files_sbcfg } |
244 | 0 | } |
245 | 3.19k | } |
246 | 0 | } |
247 | 3.19k | AppLayerGetFileState::err() |
248 | 345k | } |