Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/smb/smb2_ioctl.rs
Line
Count
Source
1
/* Copyright (C) 2018 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 crate::smb::smb::*;
19
use crate::smb::smb2::*;
20
use crate::smb::smb2_records::*;
21
use crate::smb::dcerpc::*;
22
use crate::smb::events::*;
23
#[cfg(feature = "debug")]
24
use crate::smb::funcs::*;
25
use crate::smb::smb_status::*;
26
27
#[derive(Debug)]
28
pub struct SMBTransactionIoctl {
29
    pub func: u32,
30
}
31
32
impl SMBTransactionIoctl {
33
6.67k
    pub fn new(func: u32) -> Self {
34
6.67k
        return Self {
35
6.67k
            func,
36
6.67k
        };
37
6.67k
    }
38
}
39
40
impl SMBState {
41
6.67k
    pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32)
42
6.67k
        -> &mut SMBTransaction
43
    {
44
6.67k
        let mut tx = self.new_tx();
45
6.67k
        tx.hdr = hdr;
46
6.67k
        tx.type_data = Some(SMBTransactionTypeData::IOCTL(
47
6.67k
                    SMBTransactionIoctl::new(func)));
48
6.67k
        tx.request_done = true;
49
6.67k
        tx.response_done = self.tc_trunc; // no response expected if tc is truncated
50
51
        SCLogDebug!("SMB: TX IOCTL created: ID {} FUNC {:08x}: {}",
52
                tx.id, func, &fsctl_func_to_string(func));
53
6.67k
        self.transactions.push_back(tx);
54
6.67k
        let tx_ref = self.transactions.back_mut();
55
6.67k
        return tx_ref.unwrap();
56
6.67k
    }
57
}
58
59
// IOCTL responses ASYNC don't set the tree id
60
21.7k
pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record)
61
{
62
21.7k
    let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
63
21.7k
    match parse_smb2_request_ioctl(r.data) {
64
7.79k
        Ok((_, rd)) => {
65
            SCLogDebug!("IOCTL request data: {:?}", rd);
66
7.79k
            let is_dcerpc = if rd.is_pipe {
67
4.51k
                state.get_service_for_guid(rd.guid).1
68
            } else {
69
3.28k
                false
70
            };
71
7.79k
            if is_dcerpc {
72
1.12k
                SCLogDebug!("IOCTL request data is_pipe. Calling smb_write_dcerpc_record");
73
1.12k
                let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_IOCTL);
74
1.12k
                smb_write_dcerpc_record(state, vercmd, hdr, rd.data);
75
6.67k
            } else {
76
6.67k
                SCLogDebug!("IOCTL {:08x} {}", rd.function, &fsctl_func_to_string(rd.function));
77
6.67k
                let tx = state.new_ioctl_tx(hdr, rd.function);
78
6.67k
                tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL);
79
6.67k
            }
80
        },
81
13.9k
        _ => {
82
13.9k
            let tx = state.new_generic_tx(2, r.command, hdr);
83
13.9k
            tx.set_event(SMBEvent::MalformedData);
84
13.9k
        },
85
    };
86
21.7k
}
87
88
// IOCTL responses ASYNC don't set the tree id
89
30.4k
pub fn smb2_ioctl_response_record(state: &mut SMBState, r: &Smb2Record)
90
{
91
30.4k
    let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
92
30.4k
    match parse_smb2_response_ioctl(r.data) {
93
14.8k
        Ok((_, rd)) => {
94
            SCLogDebug!("IOCTL response data: {:?}", rd);
95
96
14.8k
            let is_dcerpc = if rd.is_pipe {
97
11.6k
                state.get_service_for_guid(rd.guid).1
98
            } else {
99
3.13k
                false
100
            };
101
            // see https://github.com/rust-lang/rust-clippy/issues/15158
102
            #[allow(clippy::collapsible_else_if)]
103
14.8k
            if is_dcerpc {
104
7.49k
                SCLogDebug!("IOCTL response data is_pipe. Calling smb_read_dcerpc_record");
105
7.49k
                let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_IOCTL, r.nt_status);
106
7.49k
                SCLogDebug!("TODO passing empty GUID");
107
7.49k
                smb_read_dcerpc_record(state, vercmd, hdr, &[],rd.data);
108
7.49k
            } else {
109
                SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", hdr);
110
7.32k
                if let Some(tx) = state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &hdr) {
111
2.85k
                    tx.set_status(r.nt_status, false);
112
2.85k
                    if r.nt_status != SMB_NTSTATUS_PENDING {
113
2.00k
                        tx.response_done = true;
114
2.00k
                    }
115
4.47k
                }
116
            }
117
        },
118
        _ => {
119
            SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", hdr);
120
15.6k
            if let Some(tx) = state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &hdr) {
121
                SCLogDebug!("updated status of tx {}", tx.id);
122
2.20k
                tx.set_status(r.nt_status, false);
123
2.20k
                if r.nt_status != SMB_NTSTATUS_PENDING {
124
1.78k
                    tx.response_done = true;
125
1.78k
                }
126
                
127
                // parsing failed for 'SUCCESS' record, set event
128
2.20k
                if r.nt_status == SMB_NTSTATUS_SUCCESS {
129
323
                    SCLogDebug!("parse fail {:?}", r);
130
323
                    tx.set_event(SMBEvent::MalformedData);
131
1.87k
                }
132
13.4k
            }
133
        },
134
    };
135
30.4k
}