diff --git a/rust/src/smb/mod.rs b/rust/src/smb/mod.rs index 6e42886674..65e1577ffc 100644 --- a/rust/src/smb/mod.rs +++ b/rust/src/smb/mod.rs @@ -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; diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 8540926c44..66c15b623a 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -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; diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 858278aa6b..7f4ee2642b 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -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 = 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 = 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 index 0000000000..7685d22123 --- /dev/null +++ b/rust/src/smb/smb2_ioctl.rs @@ -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); + } + }, + _ => { }, + } + }, + }; +}