Coverage Report

Created: 2026-03-31 07:45

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