Coverage Report

Created: 2026-01-16 07:00

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