Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}