/src/suricata/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::direction::Direction; |
21 | | use crate::filetracker::*; |
22 | | |
23 | | use crate::smb::smb::*; |
24 | | use suricata_sys::sys::SCFileFlowFlagsToFlags; |
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 | 51.4k | pub fn new() -> Self { |
42 | 51.4k | return Self { |
43 | 51.4k | file_tracker: FileTransferTracker::new(), |
44 | 51.4k | ..Default::default() |
45 | 51.4k | } |
46 | 51.4k | } |
47 | | |
48 | 208k | pub fn update_file_flags(&mut self, flow_file_flags: u16) { |
49 | 208k | let dir_flag = if self.direction == Direction::ToServer { STREAM_TOSERVER } else { STREAM_TOCLIENT }; |
50 | 208k | self.file_tracker.file_flags = unsafe { SCFileFlowFlagsToFlags(flow_file_flags, dir_flag) }; |
51 | 208k | } |
52 | | } |
53 | | |
54 | | /// little wrapper around the FileTransferTracker::new_chunk method |
55 | 94.0k | pub fn filetracker_newchunk(ft: &mut FileTransferTracker, name: &[u8], data: &[u8], |
56 | 94.0k | chunk_offset: u64, chunk_size: u32, is_last: bool, xid: &u32) |
57 | | { |
58 | 94.0k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
59 | 94.0k | ft.new_chunk(sfcm, name, data, chunk_offset, |
60 | 94.0k | chunk_size, 0, is_last, xid); |
61 | 94.0k | } |
62 | 94.0k | } |
63 | | |
64 | 50 | pub fn filetracker_trunc(ft: &mut FileTransferTracker) |
65 | | { |
66 | 50 | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
67 | 50 | ft.trunc(sfcm); |
68 | 50 | } |
69 | 50 | } |
70 | | |
71 | 25.6k | pub fn filetracker_close(ft: &mut FileTransferTracker) |
72 | | { |
73 | 25.6k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
74 | 25.6k | ft.close(sfcm); |
75 | 25.6k | } |
76 | 25.6k | } |
77 | | |
78 | 69.0k | fn filetracker_update(ft: &mut FileTransferTracker, data: &[u8], gap_size: u32) -> u32 |
79 | | { |
80 | 69.0k | if let Some(sfcm) = unsafe { SURICATA_SMB_FILE_CONFIG } { |
81 | 69.0k | ft.update(sfcm, data, gap_size) |
82 | | } else { |
83 | 0 | 0 |
84 | | } |
85 | 69.0k | } |
86 | | |
87 | | impl SMBState { |
88 | 51.4k | pub fn new_file_tx(&mut self, fuid: &[u8], file_name: &[u8], direction: Direction) |
89 | 51.4k | -> &mut SMBTransaction |
90 | | { |
91 | 51.4k | let mut tx = self.new_tx(); |
92 | 51.4k | tx.type_data = Some(SMBTransactionTypeData::FILE(SMBTransactionFile::new())); |
93 | 51.4k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
94 | 51.4k | d.direction = direction; |
95 | 51.4k | d.fuid = fuid.to_vec(); |
96 | 51.4k | d.file_name = file_name.to_vec(); |
97 | 51.4k | d.file_tracker.tx_id = tx.id - 1; |
98 | 51.4k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
99 | 51.4k | d.update_file_flags(tx.tx_data.file_flags); |
100 | 51.4k | } |
101 | 51.4k | tx.tx_data.init_files_opened(); |
102 | 51.4k | 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 | 51.4k | self.transactions.push_back(tx); |
106 | 51.4k | let tx_ref = self.transactions.back_mut(); |
107 | 51.4k | return tx_ref.unwrap(); |
108 | 51.4k | } |
109 | | |
110 | | /// get file tx for a open file. Returns None if a file for the fuid exists, |
111 | | /// but has already been closed. |
112 | 149k | pub fn get_file_tx_by_fuid_with_open_file(&mut self, fuid: &[u8], direction: Direction) |
113 | 149k | -> Option<&mut SMBTransaction> |
114 | | { |
115 | 149k | let f = fuid.to_vec(); |
116 | 5.64M | for tx in &mut self.transactions { |
117 | 5.53M | let found = match tx.type_data { |
118 | 165k | Some(SMBTransactionTypeData::FILE(ref mut d)) => { |
119 | 165k | direction == d.direction && f == d.fuid && !d.file_tracker.is_done() |
120 | | }, |
121 | 5.36M | _ => { false }, |
122 | | }; |
123 | | |
124 | 5.53M | if found { |
125 | | SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); |
126 | 42.5k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
127 | 42.5k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
128 | 42.5k | d.update_file_flags(tx.tx_data.file_flags); |
129 | 42.5k | } |
130 | 42.5k | tx.tx_data.updated_tc = true; |
131 | 42.5k | tx.tx_data.updated_ts = true; |
132 | 42.5k | return Some(tx); |
133 | 5.49M | } |
134 | | } |
135 | | SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); |
136 | 107k | return None; |
137 | 149k | } |
138 | | |
139 | | /// get file tx for a fuid. File may already have been closed. |
140 | 299k | pub fn get_file_tx_by_fuid(&mut self, fuid: &[u8], direction: Direction) |
141 | 299k | -> Option<&mut SMBTransaction> |
142 | | { |
143 | 299k | let f = fuid.to_vec(); |
144 | 9.36M | for tx in &mut self.transactions { |
145 | 9.16M | let found = match tx.type_data { |
146 | 234k | Some(SMBTransactionTypeData::FILE(ref mut d)) => { |
147 | 234k | direction == d.direction && f == d.fuid |
148 | | }, |
149 | 8.93M | _ => { false }, |
150 | | }; |
151 | | |
152 | 9.16M | if found { |
153 | | SCLogDebug!("SMB: Found SMB file TX with ID {}", tx.id); |
154 | 103k | if let Some(SMBTransactionTypeData::FILE(ref mut d)) = tx.type_data { |
155 | 103k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
156 | 103k | d.update_file_flags(tx.tx_data.file_flags); |
157 | 103k | } |
158 | 103k | tx.tx_data.updated_tc = true; |
159 | 103k | tx.tx_data.updated_ts = true; |
160 | 103k | return Some(tx); |
161 | 9.06M | } |
162 | | } |
163 | | SCLogDebug!("SMB: Failed to find SMB TX with FUID {:?}", fuid); |
164 | 195k | return None; |
165 | 299k | } |
166 | | |
167 | | // update in progress chunks for file transfers |
168 | | // return how much data we consumed |
169 | 4.39M | pub fn filetracker_update(&mut self, direction: Direction, data: &[u8], gap_size: u32) -> u32 { |
170 | 4.39M | let mut chunk_left = if direction == Direction::ToServer { |
171 | 2.24M | self.file_ts_left |
172 | | } else { |
173 | 2.14M | self.file_tc_left |
174 | | }; |
175 | 4.39M | if chunk_left == 0 { |
176 | 4.23M | return 0 |
177 | 157k | } |
178 | | SCLogDebug!("chunk_left {} data {}", chunk_left, data.len()); |
179 | 157k | let file_handle = if direction == Direction::ToServer { |
180 | 66.1k | self.file_ts_guid.to_vec() |
181 | | } else { |
182 | 91.1k | self.file_tc_guid.to_vec() |
183 | | }; |
184 | | |
185 | 157k | let data_to_handle_len = if chunk_left as usize >= data.len() { |
186 | 156k | data.len() |
187 | | } else { |
188 | 546 | chunk_left as usize |
189 | | }; |
190 | | |
191 | 157k | if chunk_left <= data.len() as u32 { |
192 | 2.30k | chunk_left = 0; |
193 | 155k | } else { |
194 | 155k | chunk_left -= data.len() as u32; |
195 | 155k | } |
196 | | |
197 | 157k | if direction == Direction::ToServer { |
198 | 66.1k | self.file_ts_left = chunk_left; |
199 | 91.1k | } else { |
200 | 91.1k | self.file_tc_left = chunk_left; |
201 | 91.1k | } |
202 | | |
203 | 157k | let ssn_gap = self.ts_ssn_gap | self.tc_ssn_gap; |
204 | | // get the tx and update it |
205 | 157k | let consumed = match self.get_file_tx_by_fuid(&file_handle, direction) { |
206 | 69.0k | Some(tx) => { |
207 | 69.0k | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
208 | 69.0k | if ssn_gap { |
209 | 2.80k | let queued_data = tdf.file_tracker.get_queued_size(); |
210 | 2.80k | if queued_data > 2000000 { // TODO should probably be configurable |
211 | 0 | SCLogDebug!("QUEUED size {} while we've seen GAPs. Truncating file.", queued_data); |
212 | 0 | filetracker_trunc(&mut tdf.file_tracker); |
213 | 2.80k | } |
214 | 66.2k | } |
215 | | |
216 | | // reset timestamp if we get called after a gap |
217 | 69.0k | if tdf.post_gap_ts > 0 { |
218 | 24 | tdf.post_gap_ts = 0; |
219 | 69.0k | } |
220 | | |
221 | 69.0k | let file_data = &data[0..data_to_handle_len]; |
222 | 69.0k | filetracker_update(&mut tdf.file_tracker, file_data, gap_size) |
223 | | } else { |
224 | 0 | 0 |
225 | | } |
226 | | }, |
227 | | None => { |
228 | | SCLogDebug!("not found for handle {:?}", file_handle); |
229 | 88.2k | 0 }, |
230 | | }; |
231 | | |
232 | 157k | return consumed; |
233 | 4.39M | } |
234 | | } |
235 | | |
236 | | use crate::applayer::AppLayerGetFileState; |
237 | | |
238 | 1.46M | pub(super) unsafe extern "C" fn smb_gettxfiles(tx: *mut std::ffi::c_void, direction: u8) -> AppLayerGetFileState { |
239 | 1.46M | let tx = cast_pointer!(tx, SMBTransaction); |
240 | 1.46M | if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { |
241 | 1.45M | let tx_dir : u8 = tdf.direction.into(); |
242 | 1.45M | if direction & tx_dir != 0 { |
243 | 1.30M | if let Some(sfcm) = { SURICATA_SMB_FILE_CONFIG } { |
244 | 1.30M | return AppLayerGetFileState { fc: &mut tdf.file_tracker.file, cfg: sfcm.files_sbcfg } |
245 | 0 | } |
246 | 153k | } |
247 | 10.8k | } |
248 | 164k | AppLayerGetFileState::err() |
249 | 1.46M | } |