]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
smb2: break out ioctl handling
authorVictor Julien <victor@inliniac.net>
Tue, 13 Mar 2018 07:05:03 +0000 (08:05 +0100)
committerVictor Julien <victor@inliniac.net>
Tue, 13 Mar 2018 12:05:13 +0000 (13:05 +0100)
rust/src/smb/mod.rs
rust/src/smb/smb.rs
rust/src/smb/smb2.rs
rust/src/smb/smb2_ioctl.rs [new file with mode: 0644]

index 6e428866741d094c5daba6f54cf9b0597217737f..65e1577ffc2ff375a424cb2a185c1aa6312eca66 100644 (file)
@@ -27,6 +27,7 @@ pub mod smb1;
 pub mod smb1_session;
 pub mod smb2;
 pub mod smb2_session;
+pub mod smb2_ioctl;
 pub mod smb3;
 pub mod dcerpc;
 pub mod session;
index 8540926c44bef621c66427e984ed095d40d1f437..66c15b623a3c176473e39cf9793bec4cc3df6189 100644 (file)
@@ -49,6 +49,7 @@ use smb::dcerpc::*;
 use smb::session::*;
 use smb::events::*;
 use smb::files::*;
+use smb::smb2_ioctl::*;
 
 pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
 
index 858278aa6b937d53ccb140b07edd4a8f81119103..7f4ee2642b05ba5ef2e5e08d6e66629f5fb01711 100644 (file)
@@ -22,12 +22,11 @@ use nom::IResult;
 use smb::smb::*;
 use smb::smb2_records::*;
 use smb::smb2_session::*;
+use smb::smb2_ioctl::*;
 use smb::dcerpc::*;
 use smb::events::*;
 use smb::files::*;
 
-use smb::funcs::*;
-
 pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL:      u16 = 0;
 pub const SMB2_COMMAND_SESSION_SETUP:           u16 = 1;
 pub const SMB2_COMMAND_SESSION_LOGOFF:          u16 = 2;
@@ -268,39 +267,12 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
     SCLogDebug!("SMBv2 request record, command {} tree {} session {}",
             &smb2_command_string(r.command), r.tree_id, r.session_id);
 
-    let key_session_id = r.session_id;
-    let mut key_tree_id = r.tree_id;
-    let key_message_id = r.message_id;
     let mut events : Vec<SMBEvent> = Vec::new();
 
     let have_tx = match r.command {
         SMB2_COMMAND_IOCTL => {
-            let have_ioctl_tx = match parse_smb2_request_ioctl(r.data) {
-                IResult::Done(_, rd) => {
-                    SCLogDebug!("IOCTL request data: {:?}", rd);
-                    let is_dcerpc = rd.is_pipe && match state.get_service_for_guid(&rd.guid) {
-                        (_, x) => x,
-                    };
-                    if is_dcerpc {
-                        // some IOCTL responses don't set the tree id
-                        key_tree_id = 0;
-
-                        SCLogDebug!("IOCTL request data is_pipe. Calling smb_write_dcerpc_record");
-                        let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
-                                key_session_id, key_tree_id, key_message_id);
-                        let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_IOCTL);
-                        smb_write_dcerpc_record(state, vercmd, hdr, rd.data)
-                    } else {
-                        SCLogDebug!("IOCTL {:08x} {}", rd.function, &fsctl_func_to_string(rd.function));
-                        let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
-                        let tx = state.new_ioctl_tx(hdr, rd.function);
-                        tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL);
-                        true
-                    }
-                },
-                _ => { false },
-            };
-            have_ioctl_tx
+            smb2_ioctl_request_record(state, r);
+            true
         },
         SMB2_COMMAND_TREE_DISCONNECT => {
             let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
@@ -458,11 +430,10 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
     /* if we don't have a tx, create it here (maybe) */
     if !have_tx {
         if smb2_create_new_tx(r.command) {
-            let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX,
-                    key_session_id, key_tree_id, key_message_id);
+            let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
             let tx = state.new_generic_tx(2, r.command, tx_key);
             SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}",
-                    tx.id, r.command, key_session_id, key_tree_id, key_message_id);
+                    tx.id, r.command, r.session_id, r.tree_id, r.message_id);
             tx.set_events(events);
         }
     }
@@ -474,60 +445,12 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             &smb2_command_string(r.command), &smb_ntstatus_string(r.nt_status),
             r.tree_id, r.session_id, r.message_id);
 
-    let key_session_id = r.session_id;
-    let mut key_tree_id = r.tree_id;
-    let key_message_id = r.message_id;
     let mut events : Vec<SMBEvent> = Vec::new();
 
     let have_tx = match r.command {
         SMB2_COMMAND_IOCTL => {
-            let have_ioctl_tx = match parse_smb2_response_ioctl(r.data) {
-                IResult::Done(_, rd) => {
-                    SCLogDebug!("IOCTL response data: {:?}", rd);
-
-                    let is_dcerpc = rd.is_pipe && match state.get_service_for_guid(&rd.guid) {
-                        (_, x) => x,
-                    };
-                    if is_dcerpc {
-                        // some IOCTL responses don't set the tree id
-                        key_tree_id = 0;
-
-                        SCLogDebug!("IOCTL response data is_pipe. Calling smb_read_dcerpc_record");
-                        let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
-                                key_session_id, key_tree_id, key_message_id);
-                        let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_IOCTL, r.nt_status);
-                        SCLogNotice!("TODO passing empty GUID");
-                        smb_read_dcerpc_record(state, vercmd, hdr, &[],rd.data)
-                    } else {
-                        false
-                    }
-                },
-                _ => {
-                    if r.nt_status == SMB_NTSTATUS_PENDING {
-                        // find tx by ssn/msgid/treeid (+cmd)
-
-                        let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
-                                key_session_id, key_tree_id, key_message_id);
-                        SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", tx_key);
-                        match state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &tx_key) {
-                            Some(tx) => {
-                                tx.set_status(r.nt_status, false);
-                                SCLogDebug!("updated status of tx {}", tx.id);
-                                true
-                            },
-                            _ => { false },
-                        }
-                    } else if r.nt_status != SMB_NTSTATUS_SUCCESS {
-                        false
-                    } else {
-                        SCLogDebug!("parse fail {:?}", r);
-                        events.push(SMBEvent::MalformedData);
-                        false
-                    }
-                },
-            };
-
-            have_ioctl_tx
+            smb2_ioctl_response_record(state, r);
+            true
         },
         SMB2_COMMAND_SESSION_SETUP => {
             smb2_session_setup_response(state, r);
@@ -739,11 +662,10 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
         },
     };
     if !have_tx {
-        let tx_hdr = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX,
-                key_session_id, key_tree_id, key_message_id);
+        let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
         SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}",
                 &smb2_command_string(r.command),
-                key_session_id, key_tree_id, key_message_id);
+                r.session_id, r.tree_id, r.message_id);
         let _found = match state.get_generic_tx(2, r.command, &tx_hdr) {
             Some(tx) => {
                 SCLogDebug!("tx {} with {}/{} marked as done",
@@ -762,36 +684,3 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
         };
     }
 }
-
-#[derive(Debug)]
-pub struct SMBTransactionIoctl {
-    pub func: u32,
-}
-
-impl SMBTransactionIoctl {
-    pub fn new(func: u32) -> SMBTransactionIoctl {
-        return SMBTransactionIoctl {
-            func: func,
-        }
-    }
-}
-
-impl SMBState {
-    pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32)
-        -> (&mut SMBTransaction)
-    {
-        let mut tx = self.new_tx();
-        tx.hdr = hdr;
-        tx.type_data = Some(SMBTransactionTypeData::IOCTL(
-                    SMBTransactionIoctl::new(func)));
-        tx.request_done = true;
-        tx.response_done = self.tc_trunc; // no response expected if tc is truncated
-
-        SCLogDebug!("SMB: TX IOCTL created: ID {} FUNC {:08x}: {}",
-                tx.id, func, &fsctl_func_to_string(func));
-        self.transactions.push(tx);
-        let tx_ref = self.transactions.last_mut();
-        return tx_ref.unwrap();
-    }
-}
-
diff --git a/rust/src/smb/smb2_ioctl.rs b/rust/src/smb/smb2_ioctl.rs
new file mode 100644 (file)
index 0000000..7685d22
--- /dev/null
@@ -0,0 +1,143 @@
+/* Copyright (C) 2018 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+use nom::IResult;
+use log::*;
+use smb::smb::*;
+use smb::smb2::*;
+use smb::smb2_records::*;
+use smb::dcerpc::*;
+use smb::events::*;
+use smb::funcs::*;
+
+#[derive(Debug)]
+pub struct SMBTransactionIoctl {
+    pub func: u32,
+}
+
+impl SMBTransactionIoctl {
+    pub fn new(func: u32) -> SMBTransactionIoctl {
+        return SMBTransactionIoctl {
+            func: func,
+        }
+    }
+}
+
+impl SMBState {
+    pub fn new_ioctl_tx(&mut self, hdr: SMBCommonHdr, func: u32)
+        -> (&mut SMBTransaction)
+    {
+        let mut tx = self.new_tx();
+        tx.hdr = hdr;
+        tx.type_data = Some(SMBTransactionTypeData::IOCTL(
+                    SMBTransactionIoctl::new(func)));
+        tx.request_done = true;
+        tx.response_done = self.tc_trunc; // no response expected if tc is truncated
+
+        SCLogDebug!("SMB: TX IOCTL created: ID {} FUNC {:08x}: {}",
+                tx.id, func, &fsctl_func_to_string(func));
+        self.transactions.push(tx);
+        let tx_ref = self.transactions.last_mut();
+        return tx_ref.unwrap();
+    }
+}
+
+// IOCTL responses ASYNC don't set the tree id
+pub fn smb2_ioctl_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
+{
+    match parse_smb2_request_ioctl(r.data) {
+        IResult::Done(_, rd) => {
+            SCLogDebug!("IOCTL request data: {:?}", rd);
+            let is_dcerpc = rd.is_pipe && match state.get_service_for_guid(&rd.guid) {
+                (_, x) => x,
+            };
+            let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
+                    r.session_id, 0, r.message_id);
+            if is_dcerpc {
+                SCLogDebug!("IOCTL request data is_pipe. Calling smb_write_dcerpc_record");
+                let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_IOCTL);
+                smb_write_dcerpc_record(state, vercmd, hdr, rd.data);
+            } else {
+                SCLogDebug!("IOCTL {:08x} {}", rd.function, &fsctl_func_to_string(rd.function));
+                let tx = state.new_ioctl_tx(hdr, rd.function);
+                tx.vercmd.set_smb2_cmd(SMB2_COMMAND_IOCTL);
+            }
+        },
+        _ => {
+            let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
+                    r.session_id, 0, r.message_id);
+            let tx = state.new_generic_tx(2, r.command, hdr);
+            tx.set_event(SMBEvent::MalformedData);
+        },
+    };
+}
+
+// IOCTL responses ASYNC don't set the tree id
+pub fn smb2_ioctl_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
+{
+    match parse_smb2_response_ioctl(r.data) {
+        IResult::Done(_, rd) => {
+            SCLogDebug!("IOCTL response data: {:?}", rd);
+
+            let is_dcerpc = rd.is_pipe && match state.get_service_for_guid(&rd.guid) {
+                (_, x) => x,
+            };
+            if is_dcerpc {
+                SCLogDebug!("IOCTL response data is_pipe. Calling smb_read_dcerpc_record");
+                let hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
+                        r.session_id, 0, r.message_id);
+                let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_IOCTL, r.nt_status);
+                SCLogNotice!("TODO passing empty GUID");
+                smb_read_dcerpc_record(state, vercmd, hdr, &[],rd.data);
+            } else {
+                let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
+                        r.session_id, 0, r.message_id);
+                SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", tx_key);
+                match state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &tx_key) {
+                    Some(tx) => {
+                        tx.set_status(r.nt_status, false);
+                        if r.nt_status != SMB_NTSTATUS_PENDING {
+                            tx.response_done = true;
+                        }
+                    },
+                    None => { },
+                }
+            }
+        },
+        _ => {
+            let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
+                    r.session_id, 0, r.message_id);
+            SCLogDebug!("SMB2_COMMAND_IOCTL/SMB_NTSTATUS_PENDING looking for {:?}", tx_key);
+            match state.get_generic_tx(2, SMB2_COMMAND_IOCTL, &tx_key) {
+                Some(tx) => {
+                    SCLogDebug!("updated status of tx {}", tx.id);
+                    tx.set_status(r.nt_status, false);
+                    if r.nt_status != SMB_NTSTATUS_PENDING {
+                        tx.response_done = true;
+                    }
+
+                    // parsing failed for 'SUCCESS' record, set event
+                    if r.nt_status == SMB_NTSTATUS_SUCCESS {
+                        SCLogDebug!("parse fail {:?}", r);
+                        tx.set_event(SMBEvent::MalformedData);
+                    }
+                },
+                _ => { },
+            }
+        },
+    };
+}