Coverage Report

Created: 2026-06-07 07:05

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
7.09k
    pub fn new(func: u32) -> Self {
34
7.09k
        return Self {
35
7.09k
            func,
36
7.09k
        };
37
7.09k
    }
38
}
39
40
impl SMBState {
41
7.09k
    pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32)
42
7.09k
        -> &mut SMBTransaction
43
    {
44
7.09k
        let mut tx = self.new_tx();
45
7.09k
        tx.hdr = hdr;
46
7.09k
        tx.type_data = Some(SMBTransactionTypeData::IOCTL(
47
7.09k
                    SMBTransactionIoctl::new(func)));
48
7.09k
        tx.request_done = true;
49
7.09k
        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
7.09k
        self.transactions.push_back(tx);
54
7.09k
        let tx_ref = self.transactions.back_mut();
55
7.09k
        return tx_ref.unwrap();
56
7.09k
    }
57
}
58
59
// IOCTL responses ASYNC don't set the tree id
60
21.4k
pub fn smb2_ioctl_request_record(state: &mut SMBState, r: &Smb2Record)
61
{
62
21.4k
    let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
63
21.4k
    match parse_smb2_request_ioctl(r.data) {
64
8.37k
        Ok((_, rd)) => {
65
            SCLogDebug!("IOCTL request data: {:?}", rd);
66
8.37k
            let is_dcerpc = if rd.is_pipe {
67
5.34k
                state.get_service_for_guid(rd.guid).1
68
            } else {
69
3.03k
                false
70
            };
71
8.37k
            if is_dcerpc {
72
1.28k
                SCLogDebug!("IOCTL request data is_pipe. Calling smb_write_dcerpc_record");
73
1.28k
                let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_IOCTL);
74
1.28k
                smb_write_dcerpc_record(state, vercmd, hdr, rd.data);
75
7.09k
            } else {
76
7.09k
                SCLogDebug!("IOCTL {:08x} {}", rd.function, &fsctl_func_to_string(rd.function));
77
7.09k
                let tx = state.new_ioctl_tx(hdr, rd.function);
78
7.09k
                tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL);
79
7.09k
            }
80
        },
81
13.0k
        _ => {
82
13.0k
            let tx = state.new_generic_tx(2, r.command, hdr);
83
13.0k
            tx.set_event(SMBEvent::MalformedData);
84
13.0k
        },
85
    };
86
21.4k
}
87
88
// IOCTL responses ASYNC don't set the tree id
89
28.7k
pub fn smb2_ioctl_response_record(state: &mut SMBState, r: &Smb2Record)
90
{
91
28.7k
    let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
92
28.7k
    match parse_smb2_response_ioctl(r.data) {
93
13.8k
        Ok((_, rd)) => {
94
            SCLogDebug!("IOCTL response data: {:?}", rd);
95
96
13.8k
            let is_dcerpc = if rd.is_pipe {
97
11.4k
                state.get_service_for_guid(rd.guid).1
98
            } else {
99
2.41k
                false
100
            };
101
            // see https://github.com/rust-lang/rust-clippy/issues/15158
102
            #[allow(clippy::collapsible_else_if)]
103
13.8k
            if is_dcerpc {
104
7.02k
                SCLogDebug!("IOCTL response data is_pipe. Calling smb_read_dcerpc_record");
105
7.02k
                let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_IOCTL, r.nt_status);
106
7.02k
                SCLogDebug!("TODO passing empty GUID");
107
7.02k
                smb_read_dcerpc_record(state, vercmd, hdr, &[],rd.data);
108
7.02k
            } else {
109
                SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", hdr);
110
6.85k
                if let Some(tx) = state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &hdr) {
111
2.94k
                    tx.set_status(r.nt_status, false);
112
2.94k
                    if r.nt_status != SMB_NTSTATUS_PENDING {
113
2.31k
                        tx.response_done = true;
114
2.31k
                    }
115
3.90k
                }
116
            }
117
        },
118
        _ => {
119
            SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", hdr);
120
14.9k
            if let Some(tx) = state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &hdr) {
121
                SCLogDebug!("updated status of tx {}", tx.id);
122
2.56k
                tx.set_status(r.nt_status, false);
123
2.56k
                if r.nt_status != SMB_NTSTATUS_PENDING {
124
2.24k
                    tx.response_done = true;
125
2.24k
                }
126
                
127
                // parsing failed for 'SUCCESS' record, set event
128
2.56k
                if r.nt_status == SMB_NTSTATUS_SUCCESS {
129
289
                    SCLogDebug!("parse fail {:?}", r);
130
289
                    tx.set_event(SMBEvent::MalformedData);
131
2.27k
                }
132
12.3k
            }
133
        },
134
    };
135
28.7k
}