1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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()
}
|