/src/suricata7/rust/src/smb/files.rs
Line | Count | Source |
1 | | /* Copyright (C) 2018-2022 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::filetracker::*; |
21 | | use crate::filecontainer::*; |
22 | | |
23 | | use crate::smb::smb::*; |
24 | | |
25 | | /// File tracking transaction. Single direction only. |
26 | | #[derive(Default, Debug)] |
27 | | pub struct SMBTransactionFile { |
28 | | pub direction: Direction, |
29 | | pub fuid: Vec<u8>, |
30 | | pub file_name: Vec<u8>, |
31 | | pub share_name: Vec<u8>, |
32 | | pub file_tracker: FileTransferTracker, |
33 | | /// after a gap, this will be set to a time in the future. If the file |
34 | | /// receives no updates before that, it will be considered complete. |
35 | | pub post_gap_ts: u64, |
36 | | //pub files: Files, |
37 | | } |
38 | | |
39 | | impl SMBTransactionFile { |
40 | 74.4k | pub fn new() -> Self { |
41 | 74.4k | return Self { |
42 | 74.4k | file_tracker: FileTransferTracker::new(), |
43 | 74.4k | ..Default::default() |
44 | 74.4k | } |
45 | 74.4k | } |
46 | | |
47 | 330k | pub fn update_file_flags(&mut self, flow_file_flags: u16) { |
48 | 330k | let dir_flag = if self.direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; |
49 | 330k | self.file_tracker.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, dir_flag) }; |
50 | 330k | } |
51 | | } |
52 | | |
53 | | /// little wrapper around the FileTransferTracker::new_chunk method |
54 | 153k | pub fn filetracker_newchunk(ft: &mut FileTransferTracker, name: &[u8], data: &[u8], |
55 | 153k | chunk_offset: u64, chunk_size: u32, is_last: bool, xid: &u32) |
56 | | { |
57 | 153k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
58 | 153k | ft.new_chunk(sfcm, name, data, chunk_offset, |
59 | 153k | chunk_size, 0, is_last, xid); |
60 | 153k | } |
61 | 153k | } |
62 | | |
63 | 5 | pub fn filetracker_trunc(ft: &mut FileTransferTracker) |
64 | | { |
65 | 5 | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
66 | 5 | ft.trunc(sfcm); |
67 | 5 | } |
68 | 5 | } |
69 | | |
70 | 41.9k | pub fn filetracker_close(ft: &mut FileTransferTracker) |
71 | | { |
72 | 41.9k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
73 | 41.9k | ft.close(sfcm); |
74 | 41.9k | } |
75 | 41.9k | } |
76 | | |
77 | 67.0k | fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32 |
78 | | { |
79 | 67.0k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
80 | 67.0k | ft.update(sfcm, data, gap_size) |
81 | | } else { |
82 | 0 | 0 |
83 | | } |
84 | 67.0k | } |
85 | | |
86 | | impl SMBState { |
87 | 74.4k | pub fn new_file_tx(&mut self, fuid: &[u8], file_name: &[u8], direction: Direction) |
88 | 74.4k | -> &mut SMBTransaction |
89 | | { |
90 | 74.4k | let mut tx = self.new_tx(); |
91 | 74.4k | tx.type_data = Some(SMBTransactionTypeData::FILE(SMBTransactionFile::new())); |
92 | 74.4k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
93 | 74.4k | d.direction = direction; |
94 | 74.4k | d.fuid = fuid.to_vec(); |
95 | 74.4k | d.file_name = file_name.to_vec(); |
96 | 74.4k | d.file_tracker.tx_id = tx.id - 1; |
97 | 74.4k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
98 | 74.4k | d.update_file_flags(tx.tx_data.file_flags); |
99 | 74.4k | } |
100 | 74.4k | tx.tx_data.init_files_opened(); |
101 | 74.4k | tx.tx_data.file_tx = if direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; // TODO direction to flag func? |
102 | | SCLogDebug!("SMB: new_file_tx: TX FILE created: ID {} NAME {}", |
103 | | tx.id, String::from_utf8_lossy(file_name)); |
104 | 74.4k | self.transactions.push_back(tx); |
105 | 74.4k | let tx_ref = self.transactions.back_mut(); |
106 | 74.4k | return tx_ref.unwrap(); |
107 | 74.4k | } |
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 | 260k | pub fn get_file_tx_by_fuid_with_open_file(&mut self, fuid: &[u8], direction: Direction) |
112 | 260k | -> Option<&mut SMBTransaction> |
113 | | { |
114 | 260k | let f = fuid.to_vec(); |
115 | 17.1M | for tx in &mut self.transactions { |
116 | 17.0M | let found = match tx.type_data { |
117 | 277k | Some(SMBTransactionTypeData::FILE(ref mut d)) => { |
118 | 277k | direction == d.direction && f == d.fuid && !d.file_tracker.is_done() |
119 | | }, |
120 | 16.7M | _ => { false }, |
121 | | }; |
122 | | |
123 | 17.0M | if found { |
124 | | SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); |
125 | 78.9k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
126 | 78.9k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
127 | 78.9k | d.update_file_flags(tx.tx_data.file_flags); |
128 | 78.9k | } |
129 | 78.9k | tx.tx_data.updated_tc = true; |
130 | 78.9k | tx.tx_data.updated_ts = true; |
131 | 78.9k | return Some(tx); |
132 | 16.9M | } |
133 | | } |
134 | | SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); |
135 | 181k | return None; |
136 | 260k | } |
137 | | |
138 | | /// get file tx for a fuid. File may already have been closed. |
139 | 321k | pub fn get_file_tx_by_fuid(&mut self, fuid: &[u8], direction: Direction) |
140 | 321k | -> Option<&mut SMBTransaction> |
141 | | { |
142 | 321k | let f = fuid.to_vec(); |
143 | 176M | for tx in &mut self.transactions { |
144 | 176M | let found = match tx.type_data { |
145 | 271k | Some(SMBTransactionTypeData::FILE(ref mut d)) => { |
146 | 271k | direction == d.direction && f == d.fuid |
147 | | }, |
148 | 176M | _ => { false }, |
149 | | }; |
150 | | |
151 | 176M | if found { |
152 | | SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); |
153 | 172k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
154 | 172k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
155 | 172k | d.update_file_flags(tx.tx_data.file_flags); |
156 | 172k | } |
157 | 172k | tx.tx_data.updated_tc = true; |
158 | 172k | tx.tx_data.updated_ts = true; |
159 | 172k | return Some(tx); |
160 | 176M | } |
161 | | } |
162 | | SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); |
163 | 148k | return None; |
164 | 321k | } |
165 | | |
166 | | // update in progress chunks for file transfers |
167 | | // return how much data we consumed |
168 | 4.85M | pub fn filetracker_update(&mut self, direction: Direction, data: &[u8], gap_size: u32) -> u32 { |
169 | 4.85M | let mut chunk_left = if direction == Direction::ToServer { |
170 | 2.46M | self.file_ts_left |
171 | | } else { |
172 | 2.39M | self.file_tc_left |
173 | | }; |
174 | 4.85M | if chunk_left == 0 { |
175 | 4.71M | return 0 |
176 | 133k | } |
177 | | SCLogDebug!("chunk_left {} data {}", chunk_left, data.len()); |
178 | 133k | let file_handle = if direction == Direction::ToServer { |
179 | 84.5k | self.file_ts_guid.to_vec() |
180 | | } else { |
181 | 49.0k | self.file_tc_guid.to_vec() |
182 | | }; |
183 | | |
184 | 133k | let data_to_handle_len = if chunk_left as usize >= data.len() { |
185 | 133k | data.len() |
186 | | } else { |
187 | 366 | chunk_left as usize |
188 | | }; |
189 | | |
190 | 133k | if chunk_left <= data.len() as u32 { |
191 | 1.19k | chunk_left = 0; |
192 | 132k | } else { |
193 | 132k | chunk_left -= data.len() as u32; |
194 | 132k | } |
195 | | |
196 | 133k | if direction == Direction::ToServer { |
197 | 84.5k | self.file_ts_left = chunk_left; |
198 | 84.5k | } else { |
199 | 49.0k | self.file_tc_left = chunk_left; |
200 | 49.0k | } |
201 | | |
202 | 133k | let ssn_gap = self.ts_ssn_gap | self.tc_ssn_gap; |
203 | | // get the tx and update it |
204 | 133k | let consumed = match self.get_file_tx_by_fuid(&file_handle, direction) { |
205 | 67.0k | Some(tx) => { |
206 | 67.0k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
207 | 67.0k | if ssn_gap { |
208 | 2.41k | let queued_data = tdf.file_tracker.get_queued_size(); |
209 | 2.41k | 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 | 2.41k | } |
213 | 64.6k | } |
214 | | |
215 | | // reset timestamp if we get called after a gap |
216 | 67.0k | if tdf.post_gap_ts > 0 { |
217 | 11 | tdf.post_gap_ts = 0; |
218 | 67.0k | } |
219 | | |
220 | 67.0k | let file_data = &data[0..data_to_handle_len]; |
221 | 67.0k | 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 | 66.6k | 0 }, |
229 | | }; |
230 | | |
231 | 133k | return consumed; |
232 | 4.85M | } |
233 | | } |
234 | | |
235 | | use crate::applayer::AppLayerGetFileState; |
236 | | #[no_mangle] |
237 | 1.65M | pub unsafe extern "C" fn rs_smb_gettxfiles(_state: *mut std::ffi::c_void, tx: *mut std::ffi::c_void, direction: u8) -> AppLayerGetFileState { |
238 | 1.65M | let tx = cast_pointer!(tx, SMBTransaction); |
239 | 1.65M | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
240 | 1.64M | let tx_dir : u8 = tdf.direction.into(); |
241 | 1.64M | if direction & tx_dir != 0 { |
242 | 1.57M | if let Some(sfcm) = { SURICATA_SMB_FILE_CONFIG } { |
243 | 1.57M | return AppLayerGetFileState { fc: &mut tdf.file_tracker.file, cfg: sfcm.files_sbcfg } |
244 | 0 | } |
245 | 75.8k | } |
246 | 9.74k | } |
247 | 85.5k | AppLayerGetFileState::err() |
248 | 1.65M | } |